From f36b440bffd9ae10b9c783acd12199e879555e9f Mon Sep 17 00:00:00 2001 From: Dmitry Date: Wed, 7 Jan 2026 00:20:43 +0300 Subject: [PATCH] fixes --- .../lib/api/purchase/payment_manager.dart | 17 ++++++++++ .../lib/api/v2/purchases_api_v2.dart | 19 ++++++++++++ .../daos/mixins/soft_delete_mixin.dart | 3 ++ .../lib/database/daos/pack_dao.dart | 3 ++ .../lib/database/daos/payment_dao.dart | 31 ++++++++++++++++--- .../lib/database/daos/user_dao.dart | 6 ++++ 6 files changed, 75 insertions(+), 4 deletions(-) diff --git a/mnemo_cards_backend/lib/api/purchase/payment_manager.dart b/mnemo_cards_backend/lib/api/purchase/payment_manager.dart index 7507c6c..3ac686b 100644 --- a/mnemo_cards_backend/lib/api/purchase/payment_manager.dart +++ b/mnemo_cards_backend/lib/api/purchase/payment_manager.dart @@ -23,12 +23,19 @@ class PaymentManager { /// Создать платеж в базе данных Future createPayment(PaymentDto paymentDto, String userId) async { + print('🔍 PaymentManager.createPayment: Starting'); + print('🔍 PaymentManager.createPayment: userId=$userId, externalToken=${paymentDto.externalToken}'); final companion = paymentDto.toCompanion(userId); + print('🔍 PaymentManager.createPayment: Calling paymentDao.createPayment'); final paymentId = await _db.paymentDao.createPayment(companion); + print('✅ PaymentManager.createPayment: Payment created, id=$paymentId'); + print('🔍 PaymentManager.createPayment: Getting payment by id'); final payment = await _db.paymentDao.getPaymentById(paymentId); if (payment == null) { + print('❌ PaymentManager.createPayment: Payment not found after creation, id=$paymentId'); throw Exception('Failed to create payment'); } + print('✅ PaymentManager.createPayment: Payment retrieved successfully'); return payment.toDto(); } @@ -317,17 +324,24 @@ class PaymentManager { required String userId, List products = const [], }) async { + print('🔍 PaymentManager.createYookassaUrl: Starting'); + print('🔍 PaymentManager.createYookassaUrl: amount=$amount, userId=$userId, products=${products.length}'); + + print('🔍 PaymentManager.createYookassaUrl: Calling YooMoneyHandler.createPayment'); final yookassaPayment = await _yooMoneyHandler.createPayment( amount: amount, description: description, userId: userId, ); + print('✅ PaymentManager.createYookassaUrl: YooKassa payment created, id=${yookassaPayment.id}'); if (yookassaPayment.confirmationUrl == null) { + print('❌ PaymentManager.createYookassaUrl: confirmationUrl is null'); throw Exception('Failed to create YooKassa payment URL'); } // Создать запись платежа в БД + print('🔍 PaymentManager.createYookassaUrl: Creating payment in database'); final paymentDto = PaymentDto( amount: amount, currency: 'RUB', @@ -339,8 +353,11 @@ class PaymentManager { meta: null, ); + print('🔍 PaymentManager.createYookassaUrl: Calling createPayment'); await createPayment(paymentDto, userId); + print('✅ PaymentManager.createYookassaUrl: Payment created in database'); + print('✅ PaymentManager.createYookassaUrl: Returning confirmationUrl'); return yookassaPayment.confirmationUrl!; } diff --git a/mnemo_cards_backend/lib/api/v2/purchases_api_v2.dart b/mnemo_cards_backend/lib/api/v2/purchases_api_v2.dart index 0bb95d5..66b14b9 100644 --- a/mnemo_cards_backend/lib/api/v2/purchases_api_v2.dart +++ b/mnemo_cards_backend/lib/api/v2/purchases_api_v2.dart @@ -75,42 +75,57 @@ class PurchasesApiV2 { String packId, ) async { try { + print('🔍 createPackPurchase: Starting for packId=$packId'); final user = request.user; if (user == null) { + print('❌ createPackPurchase: User is null'); return _unauthorized(); } if (user.id == null) { + print('❌ createPackPurchase: User.id is null'); return _unauthorized(); } + print('🔍 createPackPurchase: Getting pack information for packId=$packId'); // Get pack information final pack = await _packManager.getPack(packId); if (pack == null) { + print('❌ createPackPurchase: Pack not found packId=$packId'); return _notFound('Pack not found'); } + print('✅ createPackPurchase: Pack found: ${pack.title}'); + print('🔍 createPackPurchase: Checking pack access for userId=${user.id}, packId=$packId'); // Check if already purchased final hasAccess = await _db.userDao.hasPackAccess( user.id!, packId, ); if (hasAccess) { + print('❌ createPackPurchase: Pack already purchased'); return _badRequest('Pack is already purchased'); } + print('✅ createPackPurchase: Pack not purchased yet'); // Get price (with discounts if applicable) String? price = pack.price; if (price != null && user.id != null) { + print('🔍 createPackPurchase: Getting user data for userId=${user.id}'); final userData = await _db.userDao.getUserData(user.id!); if (userData != null) { + print('✅ createPackPurchase: User data found'); // Apply discounts if any // For now, use price as-is. Discounts can be added later if needed. + } else { + print('⚠️ createPackPurchase: User data not found'); } } if (price == null) { + print('❌ createPackPurchase: Pack price is null'); return _badRequest('Pack is not available for purchase'); } + print('✅ createPackPurchase: Price=$price'); // Create products list final products = [ @@ -120,6 +135,7 @@ class PurchasesApiV2 { ), ]; + print('🔍 createPackPurchase: Creating YooKassa payment URL'); // Create payment URL final confirmationUrl = await _paymentManager.createYookassaUrl( amount: price, @@ -127,6 +143,7 @@ class PurchasesApiV2 { userId: user.id!, products: products, ); + print('✅ createPackPurchase: Payment URL created: $confirmationUrl'); // Get payment by external token (we need to find it) // Since createYookassaUrl creates payment internally, we need to get it @@ -142,6 +159,8 @@ class PurchasesApiV2 { 'checkUrl': checkUrl, }); } catch (e, s) { + print('❌ createPackPurchase ERROR: $e'); + print('❌ createPackPurchase STACK: $s'); developer.log('Error in createPackPurchase: $e', error: e, stackTrace: s); return _internalServerError(e.toString()); } diff --git a/mnemo_cards_backend/lib/database/daos/mixins/soft_delete_mixin.dart b/mnemo_cards_backend/lib/database/daos/mixins/soft_delete_mixin.dart index 67c4bb6..3bc8890 100644 --- a/mnemo_cards_backend/lib/database/daos/mixins/soft_delete_mixin.dart +++ b/mnemo_cards_backend/lib/database/daos/mixins/soft_delete_mixin.dart @@ -44,6 +44,8 @@ mixin SoftDeleteMixin on DatabaseAccessor { /// /// Возвращает null если запись не найдена или удалена. Future getActiveById(String id) async { + print('🔍 SoftDeleteMixin.getActiveById: id=$id, table=${table.entityName}'); + print('🔍 SoftDeleteMixin.getActiveById: SQL: SELECT * FROM ${table.entityName} WHERE id = ? AND is_deleted = false LIMIT 1'); final query = selectActive() ..where((t) { final idColumn = (t as dynamic).id; @@ -52,6 +54,7 @@ mixin SoftDeleteMixin on DatabaseAccessor { ..limit(1); final results = await query.get(); + print('✅ SoftDeleteMixin.getActiveById: Result count=${results.length}'); return results.isNotEmpty ? results.first : null; } } diff --git a/mnemo_cards_backend/lib/database/daos/pack_dao.dart b/mnemo_cards_backend/lib/database/daos/pack_dao.dart index dd13890..172eef2 100644 --- a/mnemo_cards_backend/lib/database/daos/pack_dao.dart +++ b/mnemo_cards_backend/lib/database/daos/pack_dao.dart @@ -23,11 +23,14 @@ class PackDao extends DatabaseAccessor with _$PackDaoMixin { /// Получить пак по ID Future getPackById(String id) async { + print('🔍 PackDao.getPackById: packId=$id'); + print('🔍 PackDao.getPackById: SQL: SELECT * FROM card_packs WHERE id = ? LIMIT 1'); final query = select(cardPacks) ..where((p) => p.id.equals(id)) ..limit(1); final results = await query.get(); + print('✅ PackDao.getPackById: Result count=${results.length}'); return results.isNotEmpty ? results.first : null; } diff --git a/mnemo_cards_backend/lib/database/daos/payment_dao.dart b/mnemo_cards_backend/lib/database/daos/payment_dao.dart index 28d8097..5072019 100644 --- a/mnemo_cards_backend/lib/database/daos/payment_dao.dart +++ b/mnemo_cards_backend/lib/database/daos/payment_dao.dart @@ -15,8 +15,12 @@ class PaymentDao extends DatabaseAccessor TableInfo get table => payments; /// Получить платеж по ID (только активные) - Future getPaymentById(String id) { - return getActiveById(id); + Future getPaymentById(String id) async { + print('🔍 PaymentDao.getPaymentById: paymentId=$id'); + print('🔍 PaymentDao.getPaymentById: Calling getActiveById'); + final result = await getActiveById(id); + print('✅ PaymentDao.getPaymentById: Result=${result != null ? "found" : "not found"}'); + return result; } /// Получить платежи пользователя (только активные) @@ -46,19 +50,34 @@ class PaymentDao extends DatabaseAccessor /// Создать платеж Future createPayment(PaymentsCompanion payment) async { + print('🔍 PaymentDao.createPayment: Starting payment creation'); + print('🔍 PaymentDao.createPayment: externalToken=${payment.externalToken.value}'); + print('🔍 PaymentDao.createPayment: userId=${payment.userId.value}'); + print('🔍 PaymentDao.createPayment: amount=${payment.amount.value}'); + final inserted = await into(payments).insertReturning(payment); + print('✅ PaymentDao.createPayment: Payment inserted, date=${inserted.date.dateTime}'); + // Query back to get the id since Payment class doesn't have id field // (database needs to be regenerated to include id in Payment class) // Use externalToken if available, otherwise query by unique fields if (inserted.externalToken != null && inserted.externalToken!.isNotEmpty) { + print('🔍 PaymentDao.createPayment: Querying by externalToken=${inserted.externalToken}'); + print('🔍 PaymentDao.createPayment: SQL: SELECT id FROM payments WHERE external_token = ? LIMIT 1'); final results = await (customSelect( 'SELECT id FROM payments WHERE external_token = ? LIMIT 1', variables: [Variable.withString(inserted.externalToken!)], readsFrom: {payments}, ).map((row) => row.read('id')).get()); - if (results.isNotEmpty) return results.first; + if (results.isNotEmpty) { + print('✅ PaymentDao.createPayment: Found payment by externalToken, id=${results.first}'); + return results.first; + } + print('⚠️ PaymentDao.createPayment: No payment found by externalToken'); } // Fallback: query by userId, date, and amount + print('🔍 PaymentDao.createPayment: Querying by userId, date, amount (fallback)'); + print('🔍 PaymentDao.createPayment: SQL: SELECT id FROM payments WHERE user_id = ? AND date = ? AND amount = ? ORDER BY created_at DESC LIMIT 1'); final results = await (customSelect( 'SELECT id FROM payments WHERE user_id = ? AND date = ? AND amount = ? ORDER BY created_at DESC LIMIT 1', variables: [ @@ -68,7 +87,11 @@ class PaymentDao extends DatabaseAccessor ], readsFrom: {payments}, ).map((row) => row.read('id')).get()); - if (results.isNotEmpty) return results.first; + if (results.isNotEmpty) { + print('✅ PaymentDao.createPayment: Found payment by fallback query, id=${results.first}'); + return results.first; + } + print('❌ PaymentDao.createPayment: Failed to retrieve payment ID after creation'); throw Exception('Failed to retrieve payment ID after creation'); } diff --git a/mnemo_cards_backend/lib/database/daos/user_dao.dart b/mnemo_cards_backend/lib/database/daos/user_dao.dart index 0af4a70..83ab708 100644 --- a/mnemo_cards_backend/lib/database/daos/user_dao.dart +++ b/mnemo_cards_backend/lib/database/daos/user_dao.dart @@ -145,11 +145,14 @@ class UserDao extends DatabaseAccessor with _$UserDaoMixin { /// Получить UserData пользователя Future getUserData(String userId) async { + print('🔍 UserDao.getUserData: userId=$userId'); + print('🔍 UserDao.getUserData: SQL: SELECT * FROM user_datas WHERE user_id = ? LIMIT 1'); final query = select(userDatas) ..where((ud) => ud.userId.equals(userId)) ..limit(1); final results = await query.get(); + print('✅ UserDao.getUserData: Result count=${results.length}'); return results.isNotEmpty ? results.first : null; } @@ -393,11 +396,14 @@ class UserDao extends DatabaseAccessor with _$UserDaoMixin { /// Проверить, есть ли у пользователя доступ к паку Future hasPackAccess(String userId, String packId) async { + print('🔍 UserDao.hasPackAccess: userId=$userId, packId=$packId'); + print('🔍 UserDao.hasPackAccess: SQL: SELECT * FROM user_packs WHERE user_id = ? AND pack_id = ? LIMIT 1'); final query = select(userPacks) ..where((up) => up.userId.equals(userId) & up.packId.equals(packId)) ..limit(1); final results = await query.get(); + print('✅ UserDao.hasPackAccess: Result count=${results.length}, hasAccess=${results.isNotEmpty}'); return results.isNotEmpty; }