fixes
Some checks failed
Web App CI / test (push) Waiting to run
Web App 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
Backend CI / test (push) Has been cancelled
Mobile App CI / test (push) Has been cancelled
Deploy Telegram Bot / Deploy Telegram Bot (push) Has been cancelled
Backend CI / build (push) Has been cancelled
Mobile App CI / build-android (push) Has been cancelled
Mobile App CI / build-ios (push) Has been cancelled
Some checks failed
Web App CI / test (push) Waiting to run
Web App 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
Backend CI / test (push) Has been cancelled
Mobile App CI / test (push) Has been cancelled
Deploy Telegram Bot / Deploy Telegram Bot (push) Has been cancelled
Backend CI / build (push) Has been cancelled
Mobile App CI / build-android (push) Has been cancelled
Mobile App CI / build-ios (push) Has been cancelled
This commit is contained in:
parent
496aac9135
commit
ca2913ad55
8 changed files with 63 additions and 21 deletions
|
|
@ -12,6 +12,17 @@ import 'rustore/rustore_purchase_handler.dart';
|
||||||
import 'yoo_money.dart';
|
import 'yoo_money.dart';
|
||||||
import '../../user/user_drift_extension.dart';
|
import '../../user/user_drift_extension.dart';
|
||||||
|
|
||||||
|
/// Result of creating YooKassa payment URL
|
||||||
|
class YookassaPaymentResult {
|
||||||
|
final String confirmationUrl;
|
||||||
|
final String paymentId;
|
||||||
|
|
||||||
|
YookassaPaymentResult({
|
||||||
|
required this.confirmationUrl,
|
||||||
|
required this.paymentId,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@lazySingleton
|
@lazySingleton
|
||||||
class PaymentManager {
|
class PaymentManager {
|
||||||
final AppDatabase _db;
|
final AppDatabase _db;
|
||||||
|
|
@ -318,7 +329,7 @@ class PaymentManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Создать URL для оплаты через YooKassa
|
/// Создать URL для оплаты через YooKassa
|
||||||
Future<String> createYookassaUrl({
|
Future<YookassaPaymentResult> createYookassaUrl({
|
||||||
required String amount,
|
required String amount,
|
||||||
required String description,
|
required String description,
|
||||||
required String userId,
|
required String userId,
|
||||||
|
|
@ -357,8 +368,11 @@ class PaymentManager {
|
||||||
await createPayment(paymentDto, userId);
|
await createPayment(paymentDto, userId);
|
||||||
print('✅ PaymentManager.createYookassaUrl: Payment created in database');
|
print('✅ PaymentManager.createYookassaUrl: Payment created in database');
|
||||||
|
|
||||||
print('✅ PaymentManager.createYookassaUrl: Returning confirmationUrl');
|
print('✅ PaymentManager.createYookassaUrl: Returning confirmationUrl and paymentId');
|
||||||
return yookassaPayment.confirmationUrl!;
|
return YookassaPaymentResult(
|
||||||
|
confirmationUrl: yookassaPayment.confirmationUrl!,
|
||||||
|
paymentId: yookassaPayment.id,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Получить платежи пользователя
|
/// Получить платежи пользователя
|
||||||
|
|
|
||||||
|
|
@ -137,26 +137,22 @@ class PurchasesApiV2 {
|
||||||
|
|
||||||
print('🔍 createPackPurchase: Creating YooKassa payment URL');
|
print('🔍 createPackPurchase: Creating YooKassa payment URL');
|
||||||
// Create payment URL
|
// Create payment URL
|
||||||
final confirmationUrl = await _paymentManager.createYookassaUrl(
|
final paymentResult = await _paymentManager.createYookassaUrl(
|
||||||
amount: price,
|
amount: price,
|
||||||
description: 'Покупка пакета: ${pack.title}',
|
description: 'Покупка пакета: ${pack.title}',
|
||||||
userId: user.id!,
|
userId: user.id!,
|
||||||
products: products,
|
products: products,
|
||||||
);
|
);
|
||||||
print('✅ createPackPurchase: Payment URL created: $confirmationUrl');
|
print('✅ createPackPurchase: Payment URL created: ${paymentResult.confirmationUrl}, paymentId=${paymentResult.paymentId}');
|
||||||
|
|
||||||
// Get payment by external token (we need to find it)
|
|
||||||
// Since createYookassaUrl creates payment internally, we need to get it
|
|
||||||
// For now, we'll create a simple response
|
|
||||||
// TODO: Improve this to return proper YookassaPaymentDto
|
|
||||||
|
|
||||||
// Build return URL for payment verification
|
// Build return URL for payment verification
|
||||||
final baseUri = request.requestedUri;
|
final baseUri = request.requestedUri;
|
||||||
final checkUrl = '${baseUri.scheme}://${baseUri.host}${baseUri.hasPort ? ':${baseUri.port}' : ''}/api/v2/purchases/payments/verify?packId=$packId';
|
final checkUrl = '${baseUri.scheme}://${baseUri.host}${baseUri.hasPort ? ':${baseUri.port}' : ''}/api/v2/purchases/payments/${paymentResult.paymentId}/verify?productId=$packId&productType=pack';
|
||||||
|
|
||||||
return _ok({
|
return _ok({
|
||||||
'purchaseUrl': confirmationUrl,
|
'purchaseUrl': paymentResult.confirmationUrl,
|
||||||
'checkUrl': checkUrl,
|
'checkUrl': checkUrl,
|
||||||
|
'paymentId': paymentResult.paymentId,
|
||||||
});
|
});
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
print('❌ createPackPurchase ERROR: $e');
|
print('❌ createPackPurchase ERROR: $e');
|
||||||
|
|
@ -287,7 +283,7 @@ class PurchasesApiV2 {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create payment URL
|
// Create payment URL
|
||||||
final confirmationUrl = await _paymentManager.createYookassaUrl(
|
final paymentResult = await _paymentManager.createYookassaUrl(
|
||||||
amount: price,
|
amount: price,
|
||||||
description: description,
|
description: description,
|
||||||
userId: user.id!,
|
userId: user.id!,
|
||||||
|
|
@ -296,11 +292,12 @@ class PurchasesApiV2 {
|
||||||
|
|
||||||
// Build return URL for payment verification
|
// Build return URL for payment verification
|
||||||
final baseUri = request.requestedUri;
|
final baseUri = request.requestedUri;
|
||||||
final checkUrl = '${baseUri.scheme}://${baseUri.host}${baseUri.hasPort ? ':${baseUri.port}' : ''}/api/v2/purchases/payments/verify?productId=$productId&productType=$productTypeStr';
|
final checkUrl = '${baseUri.scheme}://${baseUri.host}${baseUri.hasPort ? ':${baseUri.port}' : ''}/api/v2/purchases/payments/${paymentResult.paymentId}/verify?productId=$productId&productType=$productTypeStr';
|
||||||
|
|
||||||
return _ok({
|
return _ok({
|
||||||
'purchaseUrl': confirmationUrl,
|
'purchaseUrl': paymentResult.confirmationUrl,
|
||||||
'checkUrl': checkUrl,
|
'checkUrl': checkUrl,
|
||||||
|
'paymentId': paymentResult.paymentId,
|
||||||
});
|
});
|
||||||
} catch (e, s) {
|
} catch (e, s) {
|
||||||
developer.log('Error in createPayment: $e', error: e, stackTrace: s);
|
developer.log('Error in createPayment: $e', error: e, stackTrace: s);
|
||||||
|
|
|
||||||
|
|
@ -336,8 +336,15 @@ class AppDatabase extends _$AppDatabase {
|
||||||
|
|
||||||
// 2. Удаляем все старые вопросы (soft delete)
|
// 2. Удаляем все старые вопросы (soft delete)
|
||||||
print('Soft-deleting all existing test questions...');
|
print('Soft-deleting all existing test questions...');
|
||||||
await customStatement(
|
final now = PgDateTime(DateTime.now());
|
||||||
'UPDATE test_questions SET is_deleted = TRUE, deleted_at = NOW() WHERE is_deleted = FALSE',
|
await (update(testQuestions)
|
||||||
|
..where((tq) => tq.isDeleted.equals(false)))
|
||||||
|
.write(
|
||||||
|
TestQuestionsCompanion(
|
||||||
|
isDeleted: const Value(true),
|
||||||
|
deletedAt: Value(now),
|
||||||
|
updatedAt: Value(now),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
print(
|
print(
|
||||||
'All old questions marked as deleted. Create new questions via admin panel.',
|
'All old questions marked as deleted. Create new questions via admin panel.',
|
||||||
|
|
@ -365,7 +372,7 @@ class AppDatabase extends _$AppDatabase {
|
||||||
|
|
||||||
// 5. Удаляем всю старую статистику (она будет создаваться заново)
|
// 5. Удаляем всю старую статистику (она будет создаваться заново)
|
||||||
print('Deleting old test statistics...');
|
print('Deleting old test statistics...');
|
||||||
await customStatement('DELETE FROM test_statistics');
|
await (delete(testStatistics)).go();
|
||||||
print(
|
print(
|
||||||
'All old statistics deleted. New statistics will be collected automatically.',
|
'All old statistics deleted. New statistics will be collected automatically.',
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,13 @@ part 'yookassa_payment_dto.g.dart';
|
||||||
class YookassaPaymentDto {
|
class YookassaPaymentDto {
|
||||||
final String purchaseUrl;
|
final String purchaseUrl;
|
||||||
final String checkUrl;
|
final String checkUrl;
|
||||||
|
final String paymentId;
|
||||||
|
|
||||||
YookassaPaymentDto({required this.purchaseUrl, required this.checkUrl});
|
YookassaPaymentDto({
|
||||||
|
required this.purchaseUrl,
|
||||||
|
required this.checkUrl,
|
||||||
|
required this.paymentId,
|
||||||
|
});
|
||||||
|
|
||||||
factory YookassaPaymentDto.fromJson(Map<String, dynamic> json) =>
|
factory YookassaPaymentDto.fromJson(Map<String, dynamic> json) =>
|
||||||
_$YookassaPaymentDtoFromJson(json);
|
_$YookassaPaymentDtoFromJson(json);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,8 @@ abstract class _$YookassaPaymentDtoCWProxy {
|
||||||
|
|
||||||
YookassaPaymentDto checkUrl(String checkUrl);
|
YookassaPaymentDto checkUrl(String checkUrl);
|
||||||
|
|
||||||
|
YookassaPaymentDto paymentId(String paymentId);
|
||||||
|
|
||||||
/// Creates a new instance with the provided field values.
|
/// Creates a new instance with the provided field values.
|
||||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `YookassaPaymentDto(...).copyWith.fieldName(value)`.
|
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `YookassaPaymentDto(...).copyWith.fieldName(value)`.
|
||||||
///
|
///
|
||||||
|
|
@ -18,7 +20,11 @@ abstract class _$YookassaPaymentDtoCWProxy {
|
||||||
/// ```dart
|
/// ```dart
|
||||||
/// YookassaPaymentDto(...).copyWith(id: 12, name: "My name")
|
/// YookassaPaymentDto(...).copyWith(id: 12, name: "My name")
|
||||||
/// ```
|
/// ```
|
||||||
YookassaPaymentDto call({String purchaseUrl, String checkUrl});
|
YookassaPaymentDto call({
|
||||||
|
String purchaseUrl,
|
||||||
|
String checkUrl,
|
||||||
|
String paymentId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Callable proxy for `copyWith` functionality.
|
/// Callable proxy for `copyWith` functionality.
|
||||||
|
|
@ -35,6 +41,9 @@ class _$YookassaPaymentDtoCWProxyImpl implements _$YookassaPaymentDtoCWProxy {
|
||||||
@override
|
@override
|
||||||
YookassaPaymentDto checkUrl(String checkUrl) => call(checkUrl: checkUrl);
|
YookassaPaymentDto checkUrl(String checkUrl) => call(checkUrl: checkUrl);
|
||||||
|
|
||||||
|
@override
|
||||||
|
YookassaPaymentDto paymentId(String paymentId) => call(paymentId: paymentId);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
/// Creates a new instance with the provided field values.
|
/// Creates a new instance with the provided field values.
|
||||||
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `YookassaPaymentDto(...).copyWith.fieldName(value)`.
|
/// Passing `null` to a nullable field nullifies it, while `null` for a non-nullable field is ignored. To update a single field use `YookassaPaymentDto(...).copyWith.fieldName(value)`.
|
||||||
|
|
@ -46,6 +55,7 @@ class _$YookassaPaymentDtoCWProxyImpl implements _$YookassaPaymentDtoCWProxy {
|
||||||
YookassaPaymentDto call({
|
YookassaPaymentDto call({
|
||||||
Object? purchaseUrl = const $CopyWithPlaceholder(),
|
Object? purchaseUrl = const $CopyWithPlaceholder(),
|
||||||
Object? checkUrl = const $CopyWithPlaceholder(),
|
Object? checkUrl = const $CopyWithPlaceholder(),
|
||||||
|
Object? paymentId = const $CopyWithPlaceholder(),
|
||||||
}) {
|
}) {
|
||||||
return YookassaPaymentDto(
|
return YookassaPaymentDto(
|
||||||
purchaseUrl:
|
purchaseUrl:
|
||||||
|
|
@ -57,6 +67,10 @@ class _$YookassaPaymentDtoCWProxyImpl implements _$YookassaPaymentDtoCWProxy {
|
||||||
? _value.checkUrl
|
? _value.checkUrl
|
||||||
// ignore: cast_nullable_to_non_nullable
|
// ignore: cast_nullable_to_non_nullable
|
||||||
: checkUrl as String,
|
: checkUrl as String,
|
||||||
|
paymentId: paymentId == const $CopyWithPlaceholder() || paymentId == null
|
||||||
|
? _value.paymentId
|
||||||
|
// ignore: cast_nullable_to_non_nullable
|
||||||
|
: paymentId as String,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -77,10 +91,12 @@ YookassaPaymentDto _$YookassaPaymentDtoFromJson(Map<String, dynamic> json) =>
|
||||||
YookassaPaymentDto(
|
YookassaPaymentDto(
|
||||||
purchaseUrl: json['purchaseUrl'] as String,
|
purchaseUrl: json['purchaseUrl'] as String,
|
||||||
checkUrl: json['checkUrl'] as String,
|
checkUrl: json['checkUrl'] as String,
|
||||||
|
paymentId: json['paymentId'] as String,
|
||||||
);
|
);
|
||||||
|
|
||||||
Map<String, dynamic> _$YookassaPaymentDtoToJson(YookassaPaymentDto instance) =>
|
Map<String, dynamic> _$YookassaPaymentDtoToJson(YookassaPaymentDto instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
'purchaseUrl': instance.purchaseUrl,
|
'purchaseUrl': instance.purchaseUrl,
|
||||||
'checkUrl': instance.checkUrl,
|
'checkUrl': instance.checkUrl,
|
||||||
|
'paymentId': instance.paymentId,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -103,7 +103,7 @@ class _PurchasePageState extends State<PurchasePage> {
|
||||||
|
|
||||||
// Show dialog with verification option
|
// Show dialog with verification option
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_showPaymentVerificationDialog(payment.checkUrl);
|
_showPaymentVerificationDialog(payment.paymentId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ void main() {
|
||||||
final paymentDto = YookassaPaymentDto(
|
final paymentDto = YookassaPaymentDto(
|
||||||
purchaseUrl: 'https://example.com/pay',
|
purchaseUrl: 'https://example.com/pay',
|
||||||
checkUrl: 'https://example.com/check',
|
checkUrl: 'https://example.com/check',
|
||||||
|
paymentId: paymentId,
|
||||||
);
|
);
|
||||||
when(
|
when(
|
||||||
() => httpRepository.createPackPurchase(packId),
|
() => httpRepository.createPackPurchase(packId),
|
||||||
|
|
@ -59,6 +60,7 @@ void main() {
|
||||||
final paymentDto = YookassaPaymentDto(
|
final paymentDto = YookassaPaymentDto(
|
||||||
purchaseUrl: 'https://example.com/pay',
|
purchaseUrl: 'https://example.com/pay',
|
||||||
checkUrl: 'https://example.com/check',
|
checkUrl: 'https://example.com/check',
|
||||||
|
paymentId: paymentId,
|
||||||
);
|
);
|
||||||
when(
|
when(
|
||||||
() => httpRepository.createPayment(
|
() => httpRepository.createPayment(
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ void main() {
|
||||||
final testPayment = YookassaPaymentDto(
|
final testPayment = YookassaPaymentDto(
|
||||||
purchaseUrl: 'https://yookassa.ru/pay/payment-123',
|
purchaseUrl: 'https://yookassa.ru/pay/payment-123',
|
||||||
checkUrl: 'https://example.com/check/payment-123',
|
checkUrl: 'https://example.com/check/payment-123',
|
||||||
|
paymentId: 'payment-123',
|
||||||
);
|
);
|
||||||
|
|
||||||
test('returns null if pack info not loaded', () async {
|
test('returns null if pack info not loaded', () async {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue