Stripe ghost subscription fix - #8941
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (4)
🚧 Files skipped from review as they are similar to previous changes (3)
📝 WalkthroughWalkthroughThe change centralizes staging detection and setup. It supplies Stripe configuration from plans, registers Stripe without upfront key initialization, and defers subscription creation until the payment sheet confirms payment. ChangesStaging runtime configuration
Deferred Stripe payment flow
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🟡 Not ready to approve
The updated Stripe flow is missing an applySettings() propagation step and has inconsistent Stripe error filtering that can surface developer-facing messages to users.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Pull request overview
This pull request refactors the Stripe subscription flow to a deferred-intent approach (to avoid creating abandoned “ghost” subscriptions), sources the Stripe publishable key dynamically from the backend plans response, and centralizes staging-environment detection in lantern-core.
Changes:
- Moves Android Stripe subscriptions to a deferred-intent PaymentSheet flow where the backend subscription is created only after the user taps Pay.
- Refactors plans/provider modeling to expose a typed Stripe publishable key (
PlansData.stripePubKey) and keeps the Stripe SDK key in sync when plans change. - Centralizes environment selection via
Opts.IsStaging()and ensures staging is set early enough on Android IPC startup.
File summaries
| File | Description |
|---|---|
| lib/features/plans/provider/plans_notifier.dart | Adds a state listener to sync Stripe publishable key from fetched/cached plans. |
| lib/features/auth/choose_payment_method.dart | Switches Android Stripe flow to deferred-intent callbacks; improves Stripe error handling in UI. |
| lib/core/services/stripe_service.dart | Reworks Stripe service to deferred-intent PaymentSheet initialization/confirmation and adds user-facing error filtering. |
| lib/core/services/injection_container.dart | Registers StripeService on Android without upfront initialization; simplifies notification init path. |
| lib/core/models/plan_data.dart | Adds platform provider accessors and typed provider data (incl. Stripe pubKey). |
| lib/core/extensions/plan.dart | Adds monthlyUsdCents helper for Stripe amount quoting. |
| lib/core/extensions/error.dart | Adds StripeException-specific localized description filtering to avoid developer-facing messages. |
| lib/core/common/app_secrets.dart | Removes Stripe publishable key accessors from env-based secrets. |
| lantern-core/utils/common.go | Adds Opts.IsStaging() helper to centralize staging environment checks. |
| lantern-core/mobile/mobile.go | Ensures staging env is set before backend construction on Android IPC startup. |
| lantern-core/core.go | Replaces ad-hoc staging checks with opts.IsStaging(). |
Review details
- Files reviewed: 11/11 changed files
- Comments generated: 4
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (1)
lantern-core/utils/common.go (1)
18-23: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd regression tests for
Opts.IsStaging.
Opts.IsStaging()controls stage-environment selection, but the test files do not cover"stage","staging", or a non-staging value. Add focused cases for those inputs.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lantern-core/utils/common.go` around lines 18 - 23, Add focused regression tests for Opts.IsStaging covering both accepted values, "stage" and "staging", which must return true, plus a non-staging environment value, which must return false. Keep the tests table-driven or otherwise scoped specifically to this method.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/core/extensions/plan.dart`:
- Around line 15-20: Update the monthlyUsdCents getter to convert the Stripe
expectedMonthlyPrice amount from major USD units to cents by multiplying the
parsed value by 100 before rounding. Keep _amountOf and _formatPriceMap
unchanged so display currency formatting continues to use the existing
major-unit conversion.
In `@lib/core/models/plan_data.dart`:
- Around line 9-12: Update the platformProviders getter in the plan data model
to use PlatformUtils.isAndroid instead of PlatformUtils.isMobile, returning
providers.android on Android and providers.desktop on iOS or other platforms to
match choose_payment_method.dart and sort the displayed provider list.
In `@lib/core/services/stripe_service.dart`:
- Around line 163-174: Unify Stripe user-facing error filtering by making
StripeErrorMessage.userFacingMessage delegate to
StripeErrorExtension.localizedDescription, or by sharing its allowlist that
permits only card_error, validation_error, and decline-coded errors. Update
lib/core/services/stripe_service.dart:163-174 to remove the inconsistent
_hiddenErrorTypes denylist; retain lib/core/extensions/error.dart:159-174 as the
single implementation if delegation is used, otherwise remove it only if it
becomes redundant.
In `@lib/features/auth/choose_payment_method.dart`:
- Around line 209-246: Guard both the onSuccess and onError callbacks passed to
startStripeSDK with an early context.mounted check before invoking
finishPaymentRedirect, onPurchaseResult, or any context.showSnackBar call.
Preserve the existing success, cancellation, logging, and error-message behavior
when the widget remains mounted.
---
Nitpick comments:
In `@lantern-core/utils/common.go`:
- Around line 18-23: Add focused regression tests for Opts.IsStaging covering
both accepted values, "stage" and "staging", which must return true, plus a
non-staging environment value, which must return false. Keep the tests
table-driven or otherwise scoped specifically to this method.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 44fe5435-fe81-4a4b-9ee0-65f1aeafdae7
📒 Files selected for processing (11)
lantern-core/core.golantern-core/mobile/mobile.golantern-core/utils/common.golib/core/common/app_secrets.dartlib/core/extensions/error.dartlib/core/extensions/plan.dartlib/core/models/plan_data.dartlib/core/services/injection_container.dartlib/core/services/stripe_service.dartlib/features/auth/choose_payment_method.dartlib/features/plans/provider/plans_notifier.dart
💤 Files with no reviewable changes (1)
- lib/core/common/app_secrets.dart
There was a problem hiding this comment.
🟡 Not ready to approve
The Stripe publishable key is updated but Stripe.instance.applySettings() is no longer called anywhere, which can prevent the native SDK from receiving the key before initPaymentSheet runs.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
Suppressed comments (1)
lib/core/services/stripe_service.dart:62
Stripe.publishableKeyis set viaupdatePublishableKey, but I can't find any call toStripe.instance.applySettings()in the repo. flutter_stripe's docs/examples requireapplySettings()after setting the key so the native SDK picks it up; without it,initPaymentSheetmay fail due to a missing/stale publishable key.
// initPaymentSheet applies any pending settings (including the
// publishable key) to the native SDK itself. If plans never provided
// a key, this throws StripeConfigException into the catch below.
await Stripe.instance.initPaymentSheet(
- Files reviewed: 7/7 changed files
- Comments generated: 0 new
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
There was a problem hiding this comment.
🟡 Not ready to approve
The deferred PaymentSheet is initialized with IntentMode.paymentMode but the flow can return a SetupIntent client secret, which can break the PaymentSheet confirm step due to intent-type mismatch.
Once you've addressed the issues Copilot identified, you can request another Copilot review.
This review doesn't count toward merge requirements. Sign up for the private preview to control whether Copilot approvals count.
Review details
- Files reviewed: 7/7 changed files
- Comments generated: 1
- Review effort level: Lite
We're testing this review assessment. Please use 👍 or 👎 to tell us if it's correct.
| intentConfiguration: IntentConfiguration( | ||
| mode: IntentMode.paymentMode( | ||
| currencyCode: 'USD', | ||
| amount: amount, | ||
| // The subscription charges this payment method on renewal, so |
This pull request refactors and improves the Stripe payment flow, particularly for Android, by switching to a deferred-intent approach. It ensures that the Stripe publishable key is dynamically sourced from the backend, centralizes environment selection logic, and improves error handling and user messaging for Stripe failures. Additionally, it cleans up and modernizes how provider data is modeled and accessed.
Stripe Payment Flow Improvements:
lib/core/services/stripe_service.dart,lib/features/auth/choose_payment_method.dart) [1] [2]lib/core/services/stripe_service.dart,lib/core/models/plan_data.dart,lib/core/services/injection_container.dart) [1] [2] [3]Provider and Plan Modeling:
ProviderDataclass, making it easier and safer to access provider-specific fields like Stripe'spubKey. (lib/core/models/plan_data.dart)PlansDatafor retrieving the publishable key for the current platform and for sorting providers by subscription support. (lib/core/models/plan_data.dart)Error Handling and User Messaging:
lib/core/extensions/error.dart,lib/core/services/stripe_service.dart) [1] [2]lib/core/extensions/plan.dart)Environment Handling:
IsStagingmethod onOpts, ensuring consistent environment checks across the codebase. (lantern-core/utils/common.go,lantern-core/core.go,lantern-core/mobile/mobile.go) [1] [2] [3]Other Cleanups:
AppSecretsand updates imports accordingly. (lib/core/common/app_secrets.dart)lib/core/services/injection_container.dart)Summary by CodeRabbit
New Features
Bug Fixes