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
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:
parent
08143b9e30
commit
f36b440bff
6 changed files with 75 additions and 4 deletions
|
|
@ -23,12 +23,19 @@ class PaymentManager {
|
||||||
|
|
||||||
/// Создать платеж в базе данных
|
/// Создать платеж в базе данных
|
||||||
Future<PaymentDto> createPayment(PaymentDto paymentDto, String userId) async {
|
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);
|
final companion = paymentDto.toCompanion(userId);
|
||||||
|
print('🔍 PaymentManager.createPayment: Calling paymentDao.createPayment');
|
||||||
final paymentId = await _db.paymentDao.createPayment(companion);
|
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);
|
final payment = await _db.paymentDao.getPaymentById(paymentId);
|
||||||
if (payment == null) {
|
if (payment == null) {
|
||||||
|
print('❌ PaymentManager.createPayment: Payment not found after creation, id=$paymentId');
|
||||||
throw Exception('Failed to create payment');
|
throw Exception('Failed to create payment');
|
||||||
}
|
}
|
||||||
|
print('✅ PaymentManager.createPayment: Payment retrieved successfully');
|
||||||
return payment.toDto();
|
return payment.toDto();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -317,17 +324,24 @@ class PaymentManager {
|
||||||
required String userId,
|
required String userId,
|
||||||
List<MnemoCardsProductDto> products = const [],
|
List<MnemoCardsProductDto> products = const [],
|
||||||
}) async {
|
}) 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(
|
final yookassaPayment = await _yooMoneyHandler.createPayment(
|
||||||
amount: amount,
|
amount: amount,
|
||||||
description: description,
|
description: description,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
);
|
);
|
||||||
|
print('✅ PaymentManager.createYookassaUrl: YooKassa payment created, id=${yookassaPayment.id}');
|
||||||
|
|
||||||
if (yookassaPayment.confirmationUrl == null) {
|
if (yookassaPayment.confirmationUrl == null) {
|
||||||
|
print('❌ PaymentManager.createYookassaUrl: confirmationUrl is null');
|
||||||
throw Exception('Failed to create YooKassa payment URL');
|
throw Exception('Failed to create YooKassa payment URL');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создать запись платежа в БД
|
// Создать запись платежа в БД
|
||||||
|
print('🔍 PaymentManager.createYookassaUrl: Creating payment in database');
|
||||||
final paymentDto = PaymentDto(
|
final paymentDto = PaymentDto(
|
||||||
amount: amount,
|
amount: amount,
|
||||||
currency: 'RUB',
|
currency: 'RUB',
|
||||||
|
|
@ -339,8 +353,11 @@ class PaymentManager {
|
||||||
meta: null,
|
meta: null,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
print('🔍 PaymentManager.createYookassaUrl: Calling createPayment');
|
||||||
await createPayment(paymentDto, userId);
|
await createPayment(paymentDto, userId);
|
||||||
|
print('✅ PaymentManager.createYookassaUrl: Payment created in database');
|
||||||
|
|
||||||
|
print('✅ PaymentManager.createYookassaUrl: Returning confirmationUrl');
|
||||||
return yookassaPayment.confirmationUrl!;
|
return yookassaPayment.confirmationUrl!;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -75,42 +75,57 @@ class PurchasesApiV2 {
|
||||||
String packId,
|
String packId,
|
||||||
) async {
|
) async {
|
||||||
try {
|
try {
|
||||||
|
print('🔍 createPackPurchase: Starting for packId=$packId');
|
||||||
final user = request.user;
|
final user = request.user;
|
||||||
if (user == null) {
|
if (user == null) {
|
||||||
|
print('❌ createPackPurchase: User is null');
|
||||||
return _unauthorized();
|
return _unauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (user.id == null) {
|
if (user.id == null) {
|
||||||
|
print('❌ createPackPurchase: User.id is null');
|
||||||
return _unauthorized();
|
return _unauthorized();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
print('🔍 createPackPurchase: Getting pack information for packId=$packId');
|
||||||
// Get pack information
|
// Get pack information
|
||||||
final pack = await _packManager.getPack(packId);
|
final pack = await _packManager.getPack(packId);
|
||||||
if (pack == null) {
|
if (pack == null) {
|
||||||
|
print('❌ createPackPurchase: Pack not found packId=$packId');
|
||||||
return _notFound('Pack not found');
|
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
|
// Check if already purchased
|
||||||
final hasAccess = await _db.userDao.hasPackAccess(
|
final hasAccess = await _db.userDao.hasPackAccess(
|
||||||
user.id!,
|
user.id!,
|
||||||
packId,
|
packId,
|
||||||
);
|
);
|
||||||
if (hasAccess) {
|
if (hasAccess) {
|
||||||
|
print('❌ createPackPurchase: Pack already purchased');
|
||||||
return _badRequest('Pack is already purchased');
|
return _badRequest('Pack is already purchased');
|
||||||
}
|
}
|
||||||
|
print('✅ createPackPurchase: Pack not purchased yet');
|
||||||
|
|
||||||
// Get price (with discounts if applicable)
|
// Get price (with discounts if applicable)
|
||||||
String? price = pack.price;
|
String? price = pack.price;
|
||||||
if (price != null && user.id != null) {
|
if (price != null && user.id != null) {
|
||||||
|
print('🔍 createPackPurchase: Getting user data for userId=${user.id}');
|
||||||
final userData = await _db.userDao.getUserData(user.id!);
|
final userData = await _db.userDao.getUserData(user.id!);
|
||||||
if (userData != null) {
|
if (userData != null) {
|
||||||
|
print('✅ createPackPurchase: User data found');
|
||||||
// Apply discounts if any
|
// Apply discounts if any
|
||||||
// For now, use price as-is. Discounts can be added later if needed.
|
// For now, use price as-is. Discounts can be added later if needed.
|
||||||
|
} else {
|
||||||
|
print('⚠️ createPackPurchase: User data not found');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (price == null) {
|
if (price == null) {
|
||||||
|
print('❌ createPackPurchase: Pack price is null');
|
||||||
return _badRequest('Pack is not available for purchase');
|
return _badRequest('Pack is not available for purchase');
|
||||||
}
|
}
|
||||||
|
print('✅ createPackPurchase: Price=$price');
|
||||||
|
|
||||||
// Create products list
|
// Create products list
|
||||||
final products = [
|
final products = [
|
||||||
|
|
@ -120,6 +135,7 @@ class PurchasesApiV2 {
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
print('🔍 createPackPurchase: Creating YooKassa payment URL');
|
||||||
// Create payment URL
|
// Create payment URL
|
||||||
final confirmationUrl = await _paymentManager.createYookassaUrl(
|
final confirmationUrl = await _paymentManager.createYookassaUrl(
|
||||||
amount: price,
|
amount: price,
|
||||||
|
|
@ -127,6 +143,7 @@ class PurchasesApiV2 {
|
||||||
userId: user.id!,
|
userId: user.id!,
|
||||||
products: products,
|
products: products,
|
||||||
);
|
);
|
||||||
|
print('✅ createPackPurchase: Payment URL created: $confirmationUrl');
|
||||||
|
|
||||||
// Get payment by external token (we need to find it)
|
// Get payment by external token (we need to find it)
|
||||||
// Since createYookassaUrl creates payment internally, we need to get it
|
// Since createYookassaUrl creates payment internally, we need to get it
|
||||||
|
|
@ -142,6 +159,8 @@ class PurchasesApiV2 {
|
||||||
'checkUrl': checkUrl,
|
'checkUrl': checkUrl,
|
||||||
});
|
});
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
|
print('❌ createPackPurchase ERROR: $e');
|
||||||
|
print('❌ createPackPurchase STACK: $s');
|
||||||
developer.log('Error in createPackPurchase: $e', error: e, stackTrace: s);
|
developer.log('Error in createPackPurchase: $e', error: e, stackTrace: s);
|
||||||
return _internalServerError(e.toString());
|
return _internalServerError(e.toString());
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ mixin SoftDeleteMixin<T extends Table, D> on DatabaseAccessor<AppDatabase> {
|
||||||
///
|
///
|
||||||
/// Возвращает null если запись не найдена или удалена.
|
/// Возвращает null если запись не найдена или удалена.
|
||||||
Future<D?> getActiveById(String id) async {
|
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()
|
final query = selectActive()
|
||||||
..where((t) {
|
..where((t) {
|
||||||
final idColumn = (t as dynamic).id;
|
final idColumn = (t as dynamic).id;
|
||||||
|
|
@ -52,6 +54,7 @@ mixin SoftDeleteMixin<T extends Table, D> on DatabaseAccessor<AppDatabase> {
|
||||||
..limit(1);
|
..limit(1);
|
||||||
|
|
||||||
final results = await query.get();
|
final results = await query.get();
|
||||||
|
print('✅ SoftDeleteMixin.getActiveById: Result count=${results.length}');
|
||||||
return results.isNotEmpty ? results.first : null;
|
return results.isNotEmpty ? results.first : null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,11 +23,14 @@ class PackDao extends DatabaseAccessor<AppDatabase> with _$PackDaoMixin {
|
||||||
|
|
||||||
/// Получить пак по ID
|
/// Получить пак по ID
|
||||||
Future<CardPack?> getPackById(String id) async {
|
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)
|
final query = select(cardPacks)
|
||||||
..where((p) => p.id.equals(id))
|
..where((p) => p.id.equals(id))
|
||||||
..limit(1);
|
..limit(1);
|
||||||
|
|
||||||
final results = await query.get();
|
final results = await query.get();
|
||||||
|
print('✅ PackDao.getPackById: Result count=${results.length}');
|
||||||
return results.isNotEmpty ? results.first : null;
|
return results.isNotEmpty ? results.first : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,12 @@ class PaymentDao extends DatabaseAccessor<AppDatabase>
|
||||||
TableInfo<Payments, Payment> get table => payments;
|
TableInfo<Payments, Payment> get table => payments;
|
||||||
|
|
||||||
/// Получить платеж по ID (только активные)
|
/// Получить платеж по ID (только активные)
|
||||||
Future<Payment?> getPaymentById(String id) {
|
Future<Payment?> getPaymentById(String id) async {
|
||||||
return getActiveById(id);
|
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 {
|
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);
|
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
|
// 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)
|
// (database needs to be regenerated to include id in Payment class)
|
||||||
// Use externalToken if available, otherwise query by unique fields
|
// Use externalToken if available, otherwise query by unique fields
|
||||||
if (inserted.externalToken != null && inserted.externalToken!.isNotEmpty) {
|
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(
|
final results = await (customSelect(
|
||||||
'SELECT id FROM payments WHERE external_token = ? LIMIT 1',
|
'SELECT id FROM payments WHERE external_token = ? LIMIT 1',
|
||||||
variables: [Variable.withString(inserted.externalToken!)],
|
variables: [Variable.withString(inserted.externalToken!)],
|
||||||
readsFrom: {payments},
|
readsFrom: {payments},
|
||||||
).map((row) => row.read<String>('id')).get());
|
).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
|
// 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(
|
final results = await (customSelect(
|
||||||
'SELECT id FROM payments WHERE user_id = ? AND date = ? AND amount = ? ORDER BY created_at DESC LIMIT 1',
|
'SELECT id FROM payments WHERE user_id = ? AND date = ? AND amount = ? ORDER BY created_at DESC LIMIT 1',
|
||||||
variables: [
|
variables: [
|
||||||
|
|
@ -68,7 +87,11 @@ class PaymentDao extends DatabaseAccessor<AppDatabase>
|
||||||
],
|
],
|
||||||
readsFrom: {payments},
|
readsFrom: {payments},
|
||||||
).map((row) => row.read<String>('id')).get());
|
).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');
|
throw Exception('Failed to retrieve payment ID after creation');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -145,11 +145,14 @@ class UserDao extends DatabaseAccessor<AppDatabase> with _$UserDaoMixin {
|
||||||
|
|
||||||
/// Получить UserData пользователя
|
/// Получить UserData пользователя
|
||||||
Future<UserData?> getUserData(String userId) async {
|
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)
|
final query = select(userDatas)
|
||||||
..where((ud) => ud.userId.equals(userId))
|
..where((ud) => ud.userId.equals(userId))
|
||||||
..limit(1);
|
..limit(1);
|
||||||
|
|
||||||
final results = await query.get();
|
final results = await query.get();
|
||||||
|
print('✅ UserDao.getUserData: Result count=${results.length}');
|
||||||
return results.isNotEmpty ? results.first : null;
|
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 {
|
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)
|
final query = select(userPacks)
|
||||||
..where((up) => up.userId.equals(userId) & up.packId.equals(packId))
|
..where((up) => up.userId.equals(userId) & up.packId.equals(packId))
|
||||||
..limit(1);
|
..limit(1);
|
||||||
|
|
||||||
final results = await query.get();
|
final results = await query.get();
|
||||||
|
print('✅ UserDao.hasPackAccess: Result count=${results.length}, hasAccess=${results.isNotEmpty}');
|
||||||
return results.isNotEmpty;
|
return results.isNotEmpty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue