fixes
Some checks are pending
Backend CI / test (push) Waiting to run
Backend CI / build (push) Blocked by required conditions
Deploy Mnemo Cards / Deploy Backend (push) Waiting to run
Deploy Mnemo Cards / Deploy Web App (push) Blocked by required conditions
Deploy Mnemo Cards / Final Verification (push) Blocked by required conditions

This commit is contained in:
Dmitry 2026-01-07 00:20:43 +03:00
parent 08143b9e30
commit f36b440bff
6 changed files with 75 additions and 4 deletions

View file

@ -23,12 +23,19 @@ class PaymentManager {
/// Создать платеж в базе данных
Future<PaymentDto> 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<MnemoCardsProductDto> 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!;
}

View file

@ -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());
}

View file

@ -44,6 +44,8 @@ mixin SoftDeleteMixin<T extends Table, D> on DatabaseAccessor<AppDatabase> {
///
/// Возвращает null если запись не найдена или удалена.
Future<D?> 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<T extends Table, D> on DatabaseAccessor<AppDatabase> {
..limit(1);
final results = await query.get();
print('✅ SoftDeleteMixin.getActiveById: Result count=${results.length}');
return results.isNotEmpty ? results.first : null;
}
}

View file

@ -23,11 +23,14 @@ class PackDao extends DatabaseAccessor<AppDatabase> with _$PackDaoMixin {
/// Получить пак по ID
Future<CardPack?> 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;
}

View file

@ -15,8 +15,12 @@ class PaymentDao extends DatabaseAccessor<AppDatabase>
TableInfo<Payments, Payment> get table => payments;
/// Получить платеж по ID (только активные)
Future<Payment?> getPaymentById(String id) {
return getActiveById(id);
Future<Payment?> 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<AppDatabase>
/// Создать платеж
Future<String> 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<String>('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<AppDatabase>
],
readsFrom: {payments},
).map((row) => row.read<String>('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');
}

View file

@ -145,11 +145,14 @@ class UserDao extends DatabaseAccessor<AppDatabase> with _$UserDaoMixin {
/// Получить UserData пользователя
Future<UserData?> 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<AppDatabase> with _$UserDaoMixin {
/// Проверить, есть ли у пользователя доступ к паку
Future<bool> 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;
}