Skip to content

Commit a873aaf

Browse files
authored
Check in-flight in app purchase (#8732)
* Check in-flight in app purchase * code review updates * Safeguard checks.
1 parent c595a5d commit a873aaf

4 files changed

Lines changed: 32 additions & 2 deletions

File tree

lib/features/auth/confirm_email.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,19 @@ class ConfirmEmail extends HookConsumerWidget {
112112
appRouter.pop();
113113
return;
114114
}
115+
116+
/// Defensive: never delete a Pro account on back-press, even if every other
117+
/// guard somehow missed this case. A successful Apple/Google IAP promotes
118+
/// the legacy user to `pro` inline server-side, so a paid user that
119+
/// reaches this screen is already Pro and must be preserved.
120+
if (ref.read(isUserProProvider)) {
121+
appLogger.info(
122+
'Back press in ConfirmEmail with pro user; preserving account',
123+
);
124+
appRouter.pop();
125+
return;
126+
}
127+
115128
if (authFlow != AuthFlow.signUp) {
116129
appRouter.pop();
117130
return;

lib/features/auth/create_password.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:lantern/core/widgets/password_criteria.dart';
88
import 'package:lantern/core/keys/app_keys.dart';
99
import 'package:lantern/features/auth/provider/auth_notifier.dart';
1010
import 'package:lantern/features/home/provider/app_setting_notifier.dart';
11+
import 'package:lantern/features/plans/provider/payment_notifier.dart';
1112

1213

1314
@RoutePage(name: 'CreatePassword')
@@ -112,6 +113,10 @@ class CreatePassword extends HookConsumerWidget {
112113
context.hideLoadingDialog();
113114
appLogger.info('Password created successfully');
114115
ref.read(appSettingProvider.notifier).setUserLoggedIn(true);
116+
// Signup completed: release any payment-in-flight flag set by an IAP
117+
// (or other payment) flow that routed the user here, so future
118+
// unrelated signup back-presses are not misprotected.
119+
ref.read(paymentSessionProvider.notifier).clearRedirect();
115120
resolveRoutes(context, ref);
116121
},
117122
);

lib/features/home/provider/home_notifier.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import 'package:lantern/core/common/common.dart';
33
import 'package:lantern/core/extensions/user_data.dart';
44
import 'package:lantern/core/models/user.dart';
55
import 'package:lantern/features/home/provider/app_setting_notifier.dart';
6+
import 'package:lantern/features/plans/provider/payment_notifier.dart';
67
import 'package:lantern/features/plans/provider/referral_notifier.dart';
78
import 'package:lantern/features/vpn/provider/server_location_notifier.dart';
89
import 'package:lantern/lantern/lantern_service_notifier.dart';
@@ -153,5 +154,8 @@ class HomeNotifier extends _$HomeNotifier {
153154
void clearLogoutData() {
154155
ref.read(referralProvider.notifier).resetReferral();
155156
ref.read(appSettingProvider.notifier).setUserLoggedIn(false);
157+
// Defensive: a stale payment-in-flight flag would protect future
158+
// unrelated signup back-presses from deleting orphaned anonymous accounts.
159+
ref.read(paymentSessionProvider.notifier).clearRedirect();
156160
}
157161
}

lib/features/plans/plans.dart

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -370,11 +370,17 @@ class _PlansState extends ConsumerState<Plans> {
370370

371371
Future<void> startInAppPurchaseFlow(Plan plan) async {
372372
context.showLoadingDialog();
373+
// Mark a payment as in flight so that ConfirmEmail's back-press guard
374+
// (confirm_email.dart) preserves the anonymous account if the user
375+
// backs out before signup completes — same protection Stripe and the
376+
// external paymentRedirect flow already get.
377+
ref.read(paymentSessionProvider.notifier).markRedirectInitiated();
373378
final payments = ref.read(paymentProvider.notifier);
374379
final result = await payments.startInAppPurchaseFlow(
375380
planId: plan.id,
376381
onSuccess: (purchase) => processPurchase(purchase, plan),
377382
onError: (error) {
383+
ref.read(paymentSessionProvider.notifier).clearRedirect();
378384
if (!mounted) return;
379385
context.showSnackBar(error);
380386
appLogger.error('Error subscribing to plan: $error');
@@ -383,6 +389,7 @@ class _PlansState extends ConsumerState<Plans> {
383389
);
384390
if (!mounted) return;
385391
result.fold((error) {
392+
ref.read(paymentSessionProvider.notifier).clearRedirect();
386393
context.hideLoadingDialog();
387394
context.showSnackBar(error.localizedErrorMessage);
388395
appLogger.error('Error subscribing to plan: $error');
@@ -401,8 +408,9 @@ class _PlansState extends ConsumerState<Plans> {
401408

402409
final appSetting = ref.read(appSettingProvider);
403410
if (appSetting.userLoggedIn) {
404-
/// If user logged in and purchase is successful then check user account status
405-
/// to reflect new purchase and send user to pro flow
411+
/// Renewal: user is already logged in, so the back-press protection
412+
/// the redirect flag was guarding is no longer relevant.
413+
ref.read(paymentSessionProvider.notifier).clearRedirect();
406414
userRenewalFlow();
407415
return;
408416
}

0 commit comments

Comments
 (0)