forked from eatvio/eatvio_app
198 lines
6.2 KiB
Dart
198 lines
6.2 KiB
Dart
import 'package:eatvio_app/core/locator_service.dart';
|
||
import 'package:eatvio_app/core/utils/toasts.dart';
|
||
import 'package:eatvio_app/features/payment/models/promo_apply_response.dart';
|
||
import 'package:eatvio_app/features/payment/models/promo_code_model.dart';
|
||
import 'package:eatvio_app/features/payment/models/promo_validate_response.dart';
|
||
import 'package:eatvio_app/features/payment/presentation/widgets/promo_code_gate_bottom_sheet.dart';
|
||
import 'package:eatvio_app/features/payment/presentation/widgets/promo_code_modal.dart';
|
||
import 'package:eatvio_app/features/payment/presentation/widgets/sbp_banks_bottom_sheet.dart';
|
||
import 'package:eatvio_app/features/payment/repositories/payment_repository.dart';
|
||
import 'package:flutter/material.dart';
|
||
|
||
/// Оркестрирует флоу оплаты консультации:
|
||
/// applied rehydrate → (или гейт → форма) → apply при необходимости → SbpBanks.
|
||
Future<void> showConsultationPaymentFlow(
|
||
BuildContext context, {
|
||
required int planId,
|
||
Future<bool?> Function(BuildContext)? showGate,
|
||
Future<PromoCodeResult?> Function(
|
||
BuildContext, {
|
||
Future<PromoValidation> Function(String)? onValidate,
|
||
Future<bool> Function()? onRemove,
|
||
PromoCodeModel? initialPromo,
|
||
})? showForm,
|
||
Future<void> Function(BuildContext, {required int planId})? showSbp,
|
||
Future<PromoValidateResponse> Function({
|
||
required int planId,
|
||
required String promoCode,
|
||
})? validatePromo,
|
||
Future<PromoApplyResponse> Function({
|
||
required int planId,
|
||
required String promoCode,
|
||
})? applyPromo,
|
||
Future<PromoCodeModel?> Function(int planId)? listApplied,
|
||
Future<bool> Function(int planId)? removePromo,
|
||
}) async {
|
||
final gate = showGate ?? _showGate;
|
||
final form = showForm ?? _showForm;
|
||
final sbp = showSbp ?? _showSbp;
|
||
final validate = validatePromo ?? _defaultValidatePromo;
|
||
final apply = applyPromo ?? _defaultApplyPromo;
|
||
final findApplied = listApplied ?? _defaultFindApplied;
|
||
final remove = removePromo ?? _defaultRemovePromo;
|
||
|
||
final existing = await findApplied(planId);
|
||
if (!context.mounted) return;
|
||
|
||
if (existing != null) {
|
||
final formResult = await form(
|
||
context,
|
||
initialPromo: existing,
|
||
onRemove: () => remove(planId),
|
||
onValidate: (code) => _mapValidateResponse(
|
||
validate(planId: planId, promoCode: code),
|
||
),
|
||
);
|
||
if (formResult == null || !context.mounted) return;
|
||
|
||
if (formResult.hasPromoCode && !formResult.alreadyApplied) {
|
||
final applied = await _tryApplyPromo(
|
||
apply: apply,
|
||
planId: planId,
|
||
promoCode: formResult.code!,
|
||
);
|
||
if (!applied || !context.mounted) return;
|
||
}
|
||
|
||
if (!context.mounted) return;
|
||
await sbp(context, planId: planId);
|
||
return;
|
||
}
|
||
|
||
final gateChoice = await gate(context);
|
||
if (gateChoice == null || !context.mounted) return;
|
||
|
||
if (gateChoice) {
|
||
final formResult = await form(
|
||
context,
|
||
onValidate: (code) => _mapValidateResponse(
|
||
validate(planId: planId, promoCode: code),
|
||
),
|
||
);
|
||
if (formResult == null || !context.mounted) return;
|
||
|
||
if (formResult.hasPromoCode && !formResult.alreadyApplied) {
|
||
final applied = await _tryApplyPromo(
|
||
apply: apply,
|
||
planId: planId,
|
||
promoCode: formResult.code!,
|
||
);
|
||
if (!applied || !context.mounted) return;
|
||
}
|
||
}
|
||
|
||
if (!context.mounted) return;
|
||
await sbp(context, planId: planId);
|
||
}
|
||
|
||
Future<bool?> _showGate(BuildContext context) {
|
||
return showPromoCodeGateBottomSheet(context);
|
||
}
|
||
|
||
Future<PromoCodeResult?> _showForm(
|
||
BuildContext context, {
|
||
Future<PromoValidation> Function(String)? onValidate,
|
||
Future<bool> Function()? onRemove,
|
||
PromoCodeModel? initialPromo,
|
||
}) {
|
||
return ItvioPromoCodeModal.show(
|
||
context,
|
||
onValidate: onValidate,
|
||
onRemove: onRemove,
|
||
initialPromo: initialPromo,
|
||
);
|
||
}
|
||
|
||
Future<void> _showSbp(BuildContext context, {required int planId}) {
|
||
return showSbpBanksBottomSheet(context, planId: planId);
|
||
}
|
||
|
||
Future<PromoValidateResponse> _defaultValidatePromo({
|
||
required int planId,
|
||
required String promoCode,
|
||
}) {
|
||
return sl<PaymentRepository>().validatePromoCode(
|
||
planId: planId,
|
||
promoCode: promoCode,
|
||
);
|
||
}
|
||
|
||
Future<PromoApplyResponse> _defaultApplyPromo({
|
||
required int planId,
|
||
required String promoCode,
|
||
}) {
|
||
return sl<PaymentRepository>().applyPromoCode(
|
||
planId: planId,
|
||
promoCode: promoCode,
|
||
);
|
||
}
|
||
|
||
Future<PromoCodeModel?> _defaultFindApplied(int planId) {
|
||
return sl<PaymentRepository>().findAppliedPromoForPlan(planId);
|
||
}
|
||
|
||
Future<bool> _defaultRemovePromo(int planId) async {
|
||
try {
|
||
final response = await sl<PaymentRepository>().removePromoCode(planId: planId);
|
||
if (response.success) return true;
|
||
showErrorToast(response.error ?? 'Не удалось убрать промокод.');
|
||
return false;
|
||
} catch (_) {
|
||
showErrorToast('Не удалось убрать промокод. Попробуйте позже');
|
||
return false;
|
||
}
|
||
}
|
||
|
||
Future<PromoValidation> _mapValidateResponse(
|
||
Future<PromoValidateResponse> request,
|
||
) async {
|
||
try {
|
||
final response = await request;
|
||
if (!response.valid) {
|
||
return PromoValidation.failure(
|
||
response.error ?? 'Промокод невалиден.',
|
||
);
|
||
}
|
||
return PromoValidation.success(
|
||
discountPercent: response.discountPercent,
|
||
discountAmountRub: response.discountAmountRub,
|
||
originalAmountRub: response.originalAmountRub,
|
||
finalAmountRub: response.finalAmountRub,
|
||
);
|
||
} catch (_) {
|
||
return const PromoValidation.failure(
|
||
'Не удалось проверить промокод. Попробуйте позже',
|
||
);
|
||
}
|
||
}
|
||
|
||
Future<bool> _tryApplyPromo({
|
||
required Future<PromoApplyResponse> Function({
|
||
required int planId,
|
||
required String promoCode,
|
||
}) apply,
|
||
required int planId,
|
||
required String promoCode,
|
||
}) async {
|
||
try {
|
||
final response = await apply(planId: planId, promoCode: promoCode);
|
||
if (response.success) return true;
|
||
|
||
showErrorToast(response.error ?? 'Не удалось применить промокод.');
|
||
return false;
|
||
} catch (_) {
|
||
showErrorToast('Не удалось применить промокод. Попробуйте позже');
|
||
return false;
|
||
}
|
||
}
|