Skip to content

Sync v0.66.0#2421

Merged
remonh87 merged 12 commits into
mainfrom
sync_v0.66.0
Jun 4, 2026
Merged

Sync v0.66.0#2421
remonh87 merged 12 commits into
mainfrom
sync_v0.66.0

Conversation

@remonh87

@remonh87 remonh87 commented May 29, 2026

Copy link
Copy Markdown
Member

Stripe sync v 0.66


  • Have you followed the guidelines in our Contributing document?
  • Have you checked to ensure there aren't other open Pull Requests for the same change?
  • Have you added an explanation of what your changes do and why you'd like us to include them?
  • Do all checks pass?
  • Have you developed the feature on the latest stable version of Flutter?

  • AI was used to generate or assist with generating this PR. Please specify below how you used AI to help you, and what steps you have taken to manually verify the changes.

Summary by CodeRabbit

  • New Features

    • Multibanco payment support across SDKs, models, examples, and a new example screen
    • Embedded Payment Element for Flutter with deferred-confirm / confirmation-token support
    • Checkout session APIs with realtime session state events and example wiring
  • Bug Fixes

    • Improved embedded element lifecycle, event routing, and confirmation flows
    • Enhanced validation and error reporting across payment and checkout flows
  • Documentation

    • Multibanco integration guide and sidebar navigation entry

@remonh87 remonh87 requested a review from jonasbark May 29, 2026 15:29
@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds Multibanco docs and examples, introduces confirm-token handler APIs and widget lifecycle wiring, implements embedded payment element platform plumbing, and adds Checkout TurboModule/session mapping and events across Android and iOS. Platform interface models and generated serializers now support Multibanco instead of UPI.

Changes

Cross-platform payments and embedded element updates

Layer / File(s) Summary
Flutter docs, examples, and widget updates
docs/multibanco.mdx, docs.json, example/lib/screens/*, packages/stripe/lib/src/widgets/embedded_payment_element.dart
Adds Multibanco docs and two example screens; adds EmbeddedPaymentElementScreen and MultibancoScreen; wires confirm-token lifecycle and updates widget method-channel event names and creation params.
Dart SDK
packages/stripe/lib/src/stripe.dart
Adds setConfirmTokenHandler(ConfirmTokenHandler? handler) to Stripe public API.
Platform interface & generated models
packages/stripe_platform_interface/..., packages/stripe_web/...
Replaces UPI with Multibanco in Freezed/JSON models and enums, adds Multibanco PaymentMethodParams/data, adds setConfirmTokenHandler hook on platform interface and method-channel, and adds web no-op stubs.
Android embedded & Checkout bridge
packages/stripe_android/..., StripeSdkModule.kt, EmbeddedPaymentElementViewManager.kt, StripeSdkEmbeddedPaymentElementPlatformView.kt
Implements per-prefix device-event routing, normalizes platform view creation params, refactors embedded view manager for checkout/config gating, and adds Checkout TurboModule multi-session APIs, observation, and checkoutSessionDidChangeState events.
Android support & mappers
.../utils/*, Mappers.kt, UI view files`
Adds Checkout mappers, Checkout address update helpers, lifecycle default callbacks, Fresco bitmap reference handling, central constants, and replaces UPI mappings with Multibanco.
iOS embedded & Checkout bridge
EmbeddedPaymentElementFactory.swift, EmbeddedPaymentElementView.swift, StripeSdkImpl+Checkout.swift, Mappers+Checkout.swift
Registers per-view method channels, reworks confirm/clear/update mapping for embedded element, adds Checkout init/update/mutation bridge APIs, and maps Checkout state for RN events; emits checkoutSessionDidChangeState.
iOS payment factories & mappers
PaymentMethodFactory.swift, Mappers.swift
Adds Multibanco support and validation in payment-method factory, removes UPI mapping, and adds Multibanco next-action/voucher mapping.
Build & deps
android/build.gradle, stripe_ios.podspec, Package.swift
Bumps Android/iOS Stripe SDK versions and adds BuildConfig/react-native-version flags and small import/order changes.
Examples & tests
example/lib/screens/screens.dart, integration tests`
Registers new example entries and updates integration-test card expiration years.

Estimated code review effort
🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

Suggested labels
Awaiting response

Suggested reviewers

  • jonasbark

Poem

A rabbit taps keys with a curious hop,
Multibanco pages and examples pop.
Tokens and events snug in their line,
Checkout sessions hum, platform bridges align.
Hooray — carrot-powered CI, hoppity hop! 🥕

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sync_v0.66.0

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt (2)

31-44: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Scope RN event prefix to per-view id to prevent cross-instance event routing.

Android StripeSdkEmbeddedPaymentElementPlatformView registers/unregisters React Native’s DeviceEventManagerModule.RCTDeviceEventEmitter using the fixed prefix "embeddedPaymentElement", which is shared across all platform view instances. Even though the embedded element view type/channel is per-viewId on the Flutter side (flutter.stripe/embedded_payment_element/$viewId), the native event emitter prefix isn’t scoped, so multiple instances can still overwrite routing and get torn down together—scope the prefix with viewId (and align the JS/Dart listener to that same per-id event name).

🤖 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
`@packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt`
around lines 31 - 44, The RN event prefix is global and must be scoped per view
instance: update StripeSdkEmbeddedPaymentElementPlatformView to
register/unregister the DeviceEventManagerModule.RCTDeviceEventEmitter with a
per-view prefix that includes the view's id (e.g.
"embeddedPaymentElement_$viewId") instead of the fixed "embeddedPaymentElement";
change the registration call (where registerChannelForPrefix is invoked) and the
corresponding unregister call in dispose() to use the same formatted prefix
built from the instance's viewId (use the existing viewId/embeddedView
identifiers in the class), and ensure the JS/Dart listener is updated to
subscribe to the matching per-id event name
(flutter.stripe/embedded_payment_element/$viewId) so events are routed and torn
down per-instance.

47-61: ⚠️ Potential issue | 🟠 Major

Fix/clarify how embeddedPaymentElement.confirm delivers the confirmation outcome to Dart

On Android, confirm now returns immediately with result.success(null), but the actual outcome is emitted to Dart via the event/method call embeddedPaymentElementFormSheetConfirmComplete with a payload like { status: "completed"|"canceled"|"failed", error?: ... }, which Dart forwards to onFormSheetConfirmComplete.

However, EmbeddedPaymentElementController.confirm() awaits invokeMethod('confirm'), so on Android it resolves to null (unlike iOS, where the MethodChannel confirm returns the payload map and also invokes onConfirmComplete). Callers using controller.confirm() will lose the confirmation result on Android—either return the same payload from the Android confirm MethodChannel (wait for the completion event) or document that Android results are only delivered via onFormSheetConfirmComplete.

File pointers:

  • packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt ("confirm"result.success(null))
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EmbeddedPaymentElementView.kt (embeddedPaymentElementFormSheetConfirmComplete emission)
  • packages/stripe/lib/src/widgets/embedded_payment_element.dart (handles embeddedPaymentElementFormSheetConfirmComplete)
  • packages/stripe/lib/src/widgets/embedded_payment_element_controller.dart (confirm() awaits the MethodChannel return)
🤖 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
`@packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt`
around lines 47 - 61, The Android MethodChannel handler in onMethodCall
currently calls viewManager.confirm(embeddedView) and immediately
result.success(null), causing EmbeddedPaymentElementController.confirm() to
resolve to null; instead, modify the "confirm" case so it triggers
viewManager.confirm(embeddedView) but does NOT immediately return null —
subscribe for the embeddedPaymentElementFormSheetConfirmComplete event emitted
by EmbeddedPaymentElementView and, when that one-time completion payload
({status:..., error?}) arrives, call result.success(payload) (or
result.error(...) on failure), ensuring onMethodCall returns the same payload
shape that iOS provides and matches EmbeddedPaymentElementController.confirm()
expectations; use a one-shot listener/unsubscribe after delivery to avoid leaks.
packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentElementConfig.kt (1)

27-33: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wire paymentMethodConfigurationId into PaymentSheet.IntentConfiguration

  • Stripe Android SDK v0.66.0 PaymentSheet.IntentConfiguration supports both optional paymentMethodConfigurationId and onBehalfOf.
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentElementConfig.kt (builds PaymentSheet.IntentConfiguration at lines 27-33) sets paymentMethodTypes and onBehalfOf, but never sets paymentMethodConfigurationId (no references found in the repo), so any provided configuration ID would be ignored.
🤖 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
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentElementConfig.kt`
around lines 27 - 33, The PaymentSheet.IntentConfiguration built in
PaymentElementConfig (the return block constructing
PaymentSheet.IntentConfiguration) is missing the optional
paymentMethodConfigurationId field, so any provided configuration ID in
intentConfigurationParams will be ignored; fix this by reading
intentConfigurationParams.getString("paymentMethodConfigurationId") (or null if
absent) and pass it as the paymentMethodConfigurationId parameter when
constructing PaymentSheet.IntentConfiguration alongside the existing
paymentMethodTypes and onBehalfOf values.
packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Mappers.kt (1)

559-597: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Remove DisplayPayNowDetails from the return null case list so PayNow mapping executes.

In the when in Mappers.kt, NextActionType.DisplayPayNowDetails is included in the early comma-separated group that ends with null, -> { return null } (lines ~559-567), making the later NextActionType.DisplayPayNowDetails -> { type: "paynow", qrCodeUrl: ... } branch unreachable (lines ~592-597).

🐛 Proposed fix
     NextActionType.CashAppRedirect,
     NextActionType.BlikAuthorize,
     NextActionType.UseStripeSdk,
-    NextActionType.DisplayPayNowDetails,
     NextActionType.DisplayPromptPayDetails,
     null,
     -> {
       return null
     }
🤖 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
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Mappers.kt`
around lines 559 - 597, The grouped when-case in Mappers.kt currently lists
NextActionType.DisplayPayNowDetails in the early branch that immediately returns
null, which makes the later NextActionType.DisplayPayNowDetails -> {
nextActionMap.putString("type","paynow"); nextActionMap.putString("qrCodeUrl",
...) } branch unreachable; edit the when expression in the mapping function to
remove NextActionType.DisplayPayNowDetails from the comma-separated group that
returns null (or move it only to the dedicated branch) so the PayNow mapping for
NextActionData.DisplayPayNowDetails executes and populates nextActionMap as
intended.
packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift (1)

146-168: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Promise may never resolve if embeddedInstance is nil.

If embeddedInstance is nil, the optional chaining self?.embeddedInstance?.confirm does nothing and the resolve callback is never invoked, leaving the JS promise hanging indefinitely.

🛡️ Proposed fix: guard and resolve with error if instance missing
   `@objc`(confirmEmbeddedPaymentElement:reject:)
   public func confirmEmbeddedPaymentElement(resolve: `@escaping` RCTPromiseResolveBlock,
                                             reject: `@escaping` RCTPromiseRejectBlock) {
       DispatchQueue.main.async { [weak self] in
+          guard let embeddedInstance = self?.embeddedInstance else {
+              resolve(Errors.createError(
+                  ErrorType.Failed,
+                  "No EmbeddedPaymentElement instance — did you call create first?"
+              ))
+              return
+          }
-          self?.embeddedInstance?.presentingViewController = RCTPresentedViewController()
-          self?.embeddedInstance?.confirm { result in
+          embeddedInstance.presentingViewController = RCTPresentedViewController()
+          embeddedInstance.confirm { result in
               switch result {
               case .completed:
🤖 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 `@packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe`
Sdk/StripeSdkImpl+Embedded.swift around lines 146 - 168, In
confirmEmbeddedPaymentElement, guard that self and self.embeddedInstance are
non-nil before calling confirm; if missing, immediately call the promise
callback with an error (e.g., call resolve with
["status":"failed","error":"embeddedInstance is nil"] or call reject with an
appropriate code/message) so the JS promise never hangs; ensure you still set
presentingViewController (if instance exists) and then invoke
embeddedInstance.confirm as before.
🧹 Nitpick comments (4)
example/lib/screens/regional_payment_methods/multibanco_screen.dart (1)

75-89: 💤 Low value

Add const keywords to improve performance.

The EdgeInsets.all(16) and Text widgets can be marked const for better performance.

♻️ Proposed fix
   return ExampleScaffold(
     title: 'Multibanco',
     tags: ['Payment method'],
-    padding: EdgeInsets.all(16),
+    padding: const EdgeInsets.all(16),
     children: [
       LoadingButton(
         onPressed: () async {
           await _pay(context);
         },
-        text: 'Pay with Multibanco',
+        text: const Text('Pay with Multibanco'),
       ),
     ],
   );

Note: This assumes LoadingButton.text accepts a Widget. If it accepts a String, ignore the Text widget change.

🤖 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 `@example/lib/screens/regional_payment_methods/multibanco_screen.dart` around
lines 75 - 89, Wrap the static UI constructors with const to improve rebuild
performance: change EdgeInsets.all(16) to const EdgeInsets.all(16) and, if
LoadingButton's text parameter accepts a Widget, pass const Text('Pay with
Multibanco') instead of a non-const Text; leave the text as a String if
LoadingButton.text is typed as String. Update occurrences in the build method of
ExampleScaffold/LoadingButton where EdgeInsets.all(16) and the button text are
created.
packages/stripe/lib/src/widgets/embedded_payment_element.dart (1)

167-175: 💤 Low value

Consider if handler registration in confirm() is necessary.

The confirmHandler and confirmTokenHandler are already registered in initState and kept up-to-date via didUpdateWidget. Setting them again immediately before calling confirm() may be redundant unless there's a specific timing issue on the native side.

If this is intentional (e.g., to handle race conditions where native code might clear handlers), consider adding a comment explaining why.

🤖 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 `@packages/stripe/lib/src/widgets/embedded_payment_element.dart` around lines
167 - 175, The confirm() method currently re-registers handlers that are already
set in initState and maintained in didUpdateWidget: remove the redundant
Stripe.instance.setConfirmHandler(...) and
Stripe.instance.setConfirmTokenHandler(...) calls from confirm(), or if there is
a specific timing/race-condition reason to re-register here, add a concise
comment above those lines explaining the native-side issue and why
re-registration is required; reference the confirm() method and the
initState/didUpdateWidget lifecycle handling of
intentConfiguration.confirmHandler and intentConfiguration.confirmTokenHandler
when making the change.
packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt (1)

122-129: ⚡ Quick win

Consider cleanup of serverUpdateContinuations in invalidate().

While checkoutStateObservers and checkoutInstances are properly cleaned up in invalidate() (lines 182-184), the serverUpdateContinuations map is not. If the JS side calls checkoutRunServerUpdateStart but never calls checkoutRunServerUpdateComplete, the continuation will remain in the map indefinitely. Consider cancelling and clearing these continuations in invalidate() to prevent potential memory leaks.

♻️ Proposed cleanup
 override fun invalidate() {
   super.invalidate()

   stripeUIManagers.forEach { it.destroy() }
   stripeUIManagers.clear()
   checkoutStateObservers.values.forEach { it.cancel() }
   checkoutStateObservers.clear()
+  serverUpdateContinuations.values.forEach { it.cancel() }
+  serverUpdateContinuations.clear()
   checkoutInstances.clear()
 }
🤖 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
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt`
around lines 122 - 129, The serverUpdateContinuations map is not cleaned up in
invalidate(), which can leak CancellableContinuation<Result<Unit>> entries if JS
never calls checkoutRunServerUpdateComplete; update the invalidate() method to
iterate serverUpdateContinuations.values and cancel or resume each continuation
with an error (for example continuation.cancel(CancellationException("module
invalidated")) or continuation.resumeWith(Result.failure(...))), then clear
serverUpdateContinuations; reference the serverUpdateContinuations property and
the invalidate() function and ensure this cleanup runs alongside the existing
cleanup of checkoutInstances and checkoutStateObservers.
packages/stripe_platform_interface/lib/src/models/payment_methods.dart (1)

500-520: 💤 Low value

Consider whether PaymentMethodDataMultibanco is needed.

PaymentMethodDataMultibanco is structurally identical to PaymentMethodData (lines 480–498). The multibanco variant in PaymentMethodParams (line 464) already uses PaymentMethodData, so this new class appears unused. If there's no plan to add Multibanco-specific fields, consider removing this class to reduce duplication.

If the class is reserved for future Multibanco-specific extensions, a brief comment explaining that intent would clarify the design.

🤖 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 `@packages/stripe_platform_interface/lib/src/models/payment_methods.dart`
around lines 500 - 520, PaymentMethodDataMultibanco is a duplicate of
PaymentMethodData and appears unused (see PaymentMethodParams referencing
PaymentMethodData); either remove the PaymentMethodDataMultibanco declaration
and any references to it (ensuring PaymentMethodParams and serializers continue
to use PaymentMethodData and run codegen), or if you intend to reserve it for
future Multibanco-specific fields, keep the class but add a concise comment
above PaymentMethodDataMultibanco stating it exists for future
Multibanco-specific extensions and why it intentionally mirrors
PaymentMethodData now (update JSON serializers if necessary).
🤖 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 `@example/lib/screens/regional_payment_methods/multibanco_screen.dart`:
- Around line 14-29: The _createPaymentIntent method currently decodes
response.body without validating HTTP status; update it to check
response.statusCode (e.g., ensure 200/201) after the http.post and before
json.decode, and if the status is not successful, throw or return a structured
error (including response.statusCode and response.body) so callers of
_createPaymentIntent can handle failures instead of working with unexpected
decoded error payloads.
- Around line 31-48: The code reads result['clientSecret'] in _pay without
verifying it's present; update _pay to validate the clientSecret returned from
_createPaymentIntent (e.g., check that result != null and result['clientSecret']
is non-null/non-empty) before calling Stripe.instance.confirmPayment, and if
missing show an error via the scaffoldMessenger or return early to avoid passing
null to confirmPayment; reference the clientSecret variable, the
_createPaymentIntent call, and the Stripe.instance.confirmPayment invocation
when making the check and early-exit handling.

In
`@packages/stripe_android/android/src/main/kotlin/com/facebook/react/modules/core/DeviceEventManagerModule.java`:
- Around line 42-52: In emit(String eventName, Object params) the code iterates
prefixChannelMap.keySet() and then calls prefixChannelMap.get(prefix), which can
return null if unregisterChannelForPrefix runs concurrently; change iteration to
prefixChannelMap.entrySet() so you obtain both key and value atomically (check
entry.getKey().startsWith(eventName) or vice versa as appropriate), assign the
MethodChannel from entry.getValue(), and before calling uiThreadHandler.post
ensure the selected MethodChannel is non-null (skip/return if null) so
finalTarget.invokeMethod(...) cannot NPE on the UI thread.

In
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt`:
- Around line 229-245: In update() inside EmbeddedPaymentElementViewManager,
ensure we emit the completion event when parsing or configuration fails: catch
exceptions from JSONObject(json) and, if buildIntentConfiguration(cfg) returns
null, call the view's emitEmbeddedPaymentElementUpdateComplete(null) (in
addition to the existing android.util.Log.e) so the code path is consistent with
updateWithCheckout() and the EmbeddedPaymentElementView error path; locate the
logic around JSONObject(json), jsonToWritableMap(...),
buildIntentConfiguration(...) and add the
emitEmbeddedPaymentElementUpdateComplete(null) call in both the catch block and
the branch where intentConfig == null.

In
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentOptionDisplayDataMapper.kt`:
- Line 167: String.format is using the system default locale when creating hex
color strings in PaymentOptionDisplayDataMapper (e.g., the hexColor assignment
and the similar call further down), which can produce inconsistent output on
some locales; change those String.format calls to use an explicit Locale (use
java.util.Locale.US) so the format specifier "#%06X" always yields a consistent
hex color string across devices. Ensure both occurrences in
PaymentOptionDisplayDataMapper are updated to String.format(Locale.US, ...)
while keeping the existing RGB_MASK and color.toArgb() arguments unchanged.

In
`@packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift`:
- Around line 228-243: The native iOS code currently emits the confirm outcome
twice by both invoking the Flutter channel ("onConfirmComplete") and returning
the same payload via result(payload) from EmbeddedPaymentElement.confirm's
completion block; pick a single delivery mechanism and remove the duplicate:
either stop calling self.channel.invokeMethod("onConfirmComplete", ...) inside
the completion handler (so the Dart side only receives the outcome via the
method result that EmbeddedPaymentElement.confirm() returns), or stop calling
result(payload) and only send the outcome via "onConfirmComplete" (and ensure
Dart's _handleMethodCall routes/deserializes it to onFormSheetConfirmComplete or
the awaiting call accordingly). Update the code in the confirmation completion
block (the embeddedElement.confirm { confirmResult in ... } callback) to
implement the chosen single path and keep the existing payload construction and
result/error handling logic intact.

In `@packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe`
Sdk/EmbeddedPaymentElementView.swift:
- Line 85: The code sets
StripeSdkImpl.shared.embeddedInstance?.presentingViewController to a blank
RCTPresentedViewController(), which is not the real topmost presenter; instead
locate the top presenter by calling findViewControllerPresenter(from:) seeded
with a real root (use RCTKeyWindow()?.rootViewController or the embedded view's
window?.rootViewController) and assign that returned UIViewController to
embeddedInstance?.presentingViewController (and element.presentingViewController
where used). Update the assignment site that currently references
RCTPresentedViewController to call findViewControllerPresenter(from: seedRoot)
and use that result for presentation.

In
`@packages/stripe_platform_interface/lib/src/models/payment_methods.freezed.dart`:
- Around line 5351-5417: The _PaymentMethodParamsMultibanco variant is typed to
accept the generic PaymentMethodData but your new concrete Multibanco type is
PaymentMethodDataMultibanco (which no longer implements PaymentMethodData),
causing API inconsistency and downcasting in when/maybeWhen; update the
Multibanco params to use PaymentMethodDataMultibanco everywhere
(constructor/factory, field type, copyWith impl, equality/hashCode,
toJson/fromJson) or alternatively remove the PaymentMethodDataMultibanco class
and regenerate so the params keep PaymentMethodData—make the change for
_PaymentMethodParamsMultibanco and its copyWith implementation
(__$PaymentMethodParamsMultibancoCopyWithImpl) and repeat the same fix for the
other affected Multibanco ranges referenced (lines ~5794-6152).

---

Outside diff comments:
In
`@packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt`:
- Around line 31-44: The RN event prefix is global and must be scoped per view
instance: update StripeSdkEmbeddedPaymentElementPlatformView to
register/unregister the DeviceEventManagerModule.RCTDeviceEventEmitter with a
per-view prefix that includes the view's id (e.g.
"embeddedPaymentElement_$viewId") instead of the fixed "embeddedPaymentElement";
change the registration call (where registerChannelForPrefix is invoked) and the
corresponding unregister call in dispose() to use the same formatted prefix
built from the instance's viewId (use the existing viewId/embeddedView
identifiers in the class), and ensure the JS/Dart listener is updated to
subscribe to the matching per-id event name
(flutter.stripe/embedded_payment_element/$viewId) so events are routed and torn
down per-instance.
- Around line 47-61: The Android MethodChannel handler in onMethodCall currently
calls viewManager.confirm(embeddedView) and immediately result.success(null),
causing EmbeddedPaymentElementController.confirm() to resolve to null; instead,
modify the "confirm" case so it triggers viewManager.confirm(embeddedView) but
does NOT immediately return null — subscribe for the
embeddedPaymentElementFormSheetConfirmComplete event emitted by
EmbeddedPaymentElementView and, when that one-time completion payload
({status:..., error?}) arrives, call result.success(payload) (or
result.error(...) on failure), ensuring onMethodCall returns the same payload
shape that iOS provides and matches EmbeddedPaymentElementController.confirm()
expectations; use a one-shot listener/unsubscribe after delivery to avoid leaks.

In
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentElementConfig.kt`:
- Around line 27-33: The PaymentSheet.IntentConfiguration built in
PaymentElementConfig (the return block constructing
PaymentSheet.IntentConfiguration) is missing the optional
paymentMethodConfigurationId field, so any provided configuration ID in
intentConfigurationParams will be ignored; fix this by reading
intentConfigurationParams.getString("paymentMethodConfigurationId") (or null if
absent) and pass it as the paymentMethodConfigurationId parameter when
constructing PaymentSheet.IntentConfiguration alongside the existing
paymentMethodTypes and onBehalfOf values.

In
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Mappers.kt`:
- Around line 559-597: The grouped when-case in Mappers.kt currently lists
NextActionType.DisplayPayNowDetails in the early branch that immediately returns
null, which makes the later NextActionType.DisplayPayNowDetails -> {
nextActionMap.putString("type","paynow"); nextActionMap.putString("qrCodeUrl",
...) } branch unreachable; edit the when expression in the mapping function to
remove NextActionType.DisplayPayNowDetails from the comma-separated group that
returns null (or move it only to the dedicated branch) so the PayNow mapping for
NextActionData.DisplayPayNowDetails executes and populates nextActionMap as
intended.

In `@packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe`
Sdk/StripeSdkImpl+Embedded.swift:
- Around line 146-168: In confirmEmbeddedPaymentElement, guard that self and
self.embeddedInstance are non-nil before calling confirm; if missing,
immediately call the promise callback with an error (e.g., call resolve with
["status":"failed","error":"embeddedInstance is nil"] or call reject with an
appropriate code/message) so the JS promise never hangs; ensure you still set
presentingViewController (if instance exists) and then invoke
embeddedInstance.confirm as before.

---

Nitpick comments:
In `@example/lib/screens/regional_payment_methods/multibanco_screen.dart`:
- Around line 75-89: Wrap the static UI constructors with const to improve
rebuild performance: change EdgeInsets.all(16) to const EdgeInsets.all(16) and,
if LoadingButton's text parameter accepts a Widget, pass const Text('Pay with
Multibanco') instead of a non-const Text; leave the text as a String if
LoadingButton.text is typed as String. Update occurrences in the build method of
ExampleScaffold/LoadingButton where EdgeInsets.all(16) and the button text are
created.

In
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt`:
- Around line 122-129: The serverUpdateContinuations map is not cleaned up in
invalidate(), which can leak CancellableContinuation<Result<Unit>> entries if JS
never calls checkoutRunServerUpdateComplete; update the invalidate() method to
iterate serverUpdateContinuations.values and cancel or resume each continuation
with an error (for example continuation.cancel(CancellationException("module
invalidated")) or continuation.resumeWith(Result.failure(...))), then clear
serverUpdateContinuations; reference the serverUpdateContinuations property and
the invalidate() function and ensure this cleanup runs alongside the existing
cleanup of checkoutInstances and checkoutStateObservers.

In `@packages/stripe_platform_interface/lib/src/models/payment_methods.dart`:
- Around line 500-520: PaymentMethodDataMultibanco is a duplicate of
PaymentMethodData and appears unused (see PaymentMethodParams referencing
PaymentMethodData); either remove the PaymentMethodDataMultibanco declaration
and any references to it (ensuring PaymentMethodParams and serializers continue
to use PaymentMethodData and run codegen), or if you intend to reserve it for
future Multibanco-specific fields, keep the class but add a concise comment
above PaymentMethodDataMultibanco stating it exists for future
Multibanco-specific extensions and why it intentionally mirrors
PaymentMethodData now (update JSON serializers if necessary).

In `@packages/stripe/lib/src/widgets/embedded_payment_element.dart`:
- Around line 167-175: The confirm() method currently re-registers handlers that
are already set in initState and maintained in didUpdateWidget: remove the
redundant Stripe.instance.setConfirmHandler(...) and
Stripe.instance.setConfirmTokenHandler(...) calls from confirm(), or if there is
a specific timing/race-condition reason to re-register here, add a concise
comment above those lines explaining the native-side issue and why
re-registration is required; reference the confirm() method and the
initState/didUpdateWidget lifecycle handling of
intentConfiguration.confirmHandler and intentConfiguration.confirmTokenHandler
when making the change.
🪄 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

Run ID: 85c976e5-f4f9-4e09-bbd3-a55f8585a777

📥 Commits

Reviewing files that changed from the base of the PR and between 8b8d0eb and 2af9b1b.

📒 Files selected for processing (87)
  • docs.json
  • docs/multibanco.mdx
  • example/lib/screens/payment_sheet/embedded_payment_element_screen.dart
  • example/lib/screens/regional_payment_methods/multibanco_screen.dart
  • example/lib/screens/screens.dart
  • packages/stripe/lib/src/stripe.dart
  • packages/stripe/lib/src/widgets/embedded_payment_element.dart
  • packages/stripe_android/android/build.gradle
  • packages/stripe_android/android/src/main/kotlin/com/facebook/react/modules/core/DeviceEventManagerModule.java
  • packages/stripe_android/android/src/main/kotlin/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt
  • packages/stripe_android/android/src/main/kotlin/com/facebook/react/viewmanagers/EmbeddedPaymentElementViewManagerInterface.java
  • packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/AuBECSDebitFormView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/AuBECSDebitFormViewManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFormView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EmbeddedPaymentElementView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EventEmitterCompat.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/FinancialConnectionsSheetManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayButtonManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayButtonView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayLauncherManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/NavigationBarView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentElementConfig.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentLauncherManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentOptionDisplayDataMapper.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetAppearance.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeAbstractComposeView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/addresssheet/AddressLauncherManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/addresssheet/AddressSheetView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/CustomerSheetManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerSessionProvider.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/CheckoutAddressUpdate.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/CheckoutMappers.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/DefaultActivityLifecycleCallbacks.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Errors.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Extensions.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Mappers.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/PostalCodeUtilities.kt
  • packages/stripe_ios/ios/stripe_ios.podspec
  • packages/stripe_ios/ios/stripe_ios/Package.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/RCTBridge.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/AddressSheet/AddressSheetUtils.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/AddressSheet/AddressSheetView.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ApplePayUtils.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ApplePayViewController.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ConnectAccountOnboarding/ConnectAccountOnboardingViewController.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ConnectAccountOnboarding/ConnectAccountOnboardingViewManager.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/EmbeddedPaymentElementView.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/Mappers+Checkout.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/Mappers.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/PaymentMethodFactory.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/PaymentOptionDisplayData+ReactNative.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/PaymentSheetAppearance.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkEmitter.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Checkout.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+CustomerSheet.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+PaymentSheet.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/UIColorExtension.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/pushprovisioning/AddToWalletButtonView.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/pushprovisioning/PushProvisioningUtils.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/StripePlugin.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_objc/Stripe Sdk/StripeSdk.h
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_objc/Stripe Sdk/StripeSdkEventEmitterCompat.h
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_objc/Stripe Sdk/StripeSdkEventEmitterCompat.m
  • packages/stripe_platform_interface/lib/src/method_channel_stripe.dart
  • packages/stripe_platform_interface/lib/src/models/ach_params.g.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.freezed.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.g.dart
  • packages/stripe_platform_interface/lib/src/models/payment_sheet.dart
  • packages/stripe_platform_interface/lib/src/models/radar_session.dart
  • packages/stripe_platform_interface/lib/src/models/setup_intent.g.dart
  • packages/stripe_platform_interface/lib/src/stripe_platform_interface.dart
  • packages/stripe_platform_interface/test/test_data.dart
  • packages/stripe_web/lib/src/web_stripe.dart

Comment on lines +14 to +29
Future<Map<String, dynamic>> _createPaymentIntent() async {
final url = Uri.parse('$kApiUrl/create-payment-intent');
final response = await http.post(
url,
headers: {
'Content-Type': 'application/json',
},
body: json.encode({
'currency': 'eur',
'payment_method_types': ['multibanco'],
'amount': 1099
}),
);

return json.decode(response.body);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate HTTP response status before decoding.

The method doesn't check the HTTP response status code. A 4xx or 5xx response could contain error JSON that gets silently decoded and used, leading to unexpected behavior or crashes downstream.

🛡️ Proposed fix to add status validation
   final response = await http.post(
     url,
     headers: {
       'Content-Type': 'application/json',
     },
     body: json.encode({
       'currency': 'eur',
       'payment_method_types': ['multibanco'],
       'amount': 1099
     }),
   );
+  if (response.statusCode != 200) {
+    throw Exception('Failed to create payment intent: ${response.body}');
+  }

   return json.decode(response.body);
🤖 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 `@example/lib/screens/regional_payment_methods/multibanco_screen.dart` around
lines 14 - 29, The _createPaymentIntent method currently decodes response.body
without validating HTTP status; update it to check response.statusCode (e.g.,
ensure 200/201) after the http.post and before json.decode, and if the status is
not successful, throw or return a structured error (including
response.statusCode and response.body) so callers of _createPaymentIntent can
handle failures instead of working with unexpected decoded error payloads.

Comment on lines +31 to +48
Future<void> _pay(BuildContext context) async {
// 1. Create payment intent on the backend.
final scaffoldMessenger = ScaffoldMessenger.of(context);
final result = await _createPaymentIntent();
final clientSecret = result['clientSecret'];

// 2. Use the client secret to confirm the payment.
try {
await Stripe.instance.confirmPayment(
paymentIntentClientSecret: clientSecret,
data: PaymentMethodParams.multibanco(
paymentMethodData: PaymentMethodData(
billingDetails: BillingDetails(
email: 'multibanco@example.com',
),
),
),
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add null safety check for clientSecret.

The code accesses result['clientSecret'] without verifying it exists. If the backend response is malformed or missing this field, the app will crash when passing null to confirmPayment.

🛡️ Proposed fix to add null check
   final scaffoldMessenger = ScaffoldMessenger.of(context);
   final result = await _createPaymentIntent();
-  final clientSecret = result['clientSecret'];
+  final clientSecret = result['clientSecret'];
+  if (clientSecret == null) {
+    throw Exception('Client secret not found in response');
+  }

   // 2. Use the client secret to confirm the payment.
🤖 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 `@example/lib/screens/regional_payment_methods/multibanco_screen.dart` around
lines 31 - 48, The code reads result['clientSecret'] in _pay without verifying
it's present; update _pay to validate the clientSecret returned from
_createPaymentIntent (e.g., check that result != null and result['clientSecret']
is non-null/non-empty) before calling Stripe.instance.confirmPayment, and if
missing show an error via the scaffoldMessenger or return early to avoid passing
null to confirmPayment; reference the clientSecret variable, the
_createPaymentIntent call, and the Stripe.instance.confirmPayment invocation
when making the check and early-exit handling.

Comment on lines 42 to 52
public void emit(String eventName, Object params) {
uiThreadHandler.post(() -> channel.invokeMethod(eventName, params));
MethodChannel target = channel;
for (String prefix : prefixChannelMap.keySet()) {
if (eventName.startsWith(prefix)) {
target = prefixChannelMap.get(prefix);
break;
}
}
final MethodChannel finalTarget = target;
uiThreadHandler.post(() -> finalTarget.invokeMethod(eventName, params));
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Possible NPE: get(prefix) can return null after a concurrent unregister.

You iterate prefixChannelMap.keySet() and then call prefixChannelMap.get(prefix) separately. If another thread calls unregisterChannelForPrefix(prefix) between the startsWith check and the get, then target becomes null and finalTarget.invokeMethod(...) will throw an NPE on the UI thread. Iterate the entrySet() to read the value atomically with the key.

🔒 Proposed fix
-        public void emit(String eventName, Object params) {
-            MethodChannel target = channel;
-            for (String prefix : prefixChannelMap.keySet()) {
-                if (eventName.startsWith(prefix)) {
-                    target = prefixChannelMap.get(prefix);
-                    break;
-                }
-            }
-            final MethodChannel finalTarget = target;
-            uiThreadHandler.post(() -> finalTarget.invokeMethod(eventName, params));
+        public void emit(String eventName, Object params) {
+            MethodChannel target = channel;
+            for (java.util.Map.Entry<String, MethodChannel> entry : prefixChannelMap.entrySet()) {
+                if (eventName.startsWith(entry.getKey())) {
+                    target = entry.getValue();
+                    break;
+                }
+            }
+            final MethodChannel finalTarget = target;
+            uiThreadHandler.post(() -> finalTarget.invokeMethod(eventName, params));
         }
🤖 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
`@packages/stripe_android/android/src/main/kotlin/com/facebook/react/modules/core/DeviceEventManagerModule.java`
around lines 42 - 52, In emit(String eventName, Object params) the code iterates
prefixChannelMap.keySet() and then calls prefixChannelMap.get(prefix), which can
return null if unregisterChannelForPrefix runs concurrently; change iteration to
prefixChannelMap.entrySet() so you obtain both key and value atomically (check
entry.getKey().startsWith(eventName) or vice versa as appropriate), assign the
MethodChannel from entry.getValue(), and before calling uiThreadHandler.post
ensure the selected MethodChannel is non-null (skip/return if null) so
finalTarget.invokeMethod(...) cannot NPE on the UI thread.


spanStyle.color.takeIf { it != Color.Unspecified }?.let { color ->
val hexColor = String.format("#%06X", 0xFFFFFF and color.toArgb())
val hexColor = String.format("#%06X", RGB_MASK and color.toArgb())

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Specify locale for String.format to ensure consistent hex color output.

The String.format calls use the system default locale, which could produce inconsistent results on devices with different locale settings. RGB hex colors should always format consistently.

🌐 Proposed fix using explicit locale
-    val hexColor = String.format("#%06X", RGB_MASK and color.toArgb())
+    val hexColor = String.format(java.util.Locale.US, "#%06X", RGB_MASK and color.toArgb())
-    val hexBgColor = String.format("#%06X", RGB_MASK and bgColor.toArgb())
+    val hexBgColor = String.format(java.util.Locale.US, "#%06X", RGB_MASK and bgColor.toArgb())

Also applies to: 186-186

🧰 Tools
🪛 detekt (1.23.8)

[warning] 167-167: String.format("#%06X", RGB_MASK and color.toArgb()) uses implicitly default locale for string formatting.

(detekt.potential-bugs.ImplicitDefaultLocale)

🤖 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
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentOptionDisplayDataMapper.kt`
at line 167, String.format is using the system default locale when creating hex
color strings in PaymentOptionDisplayDataMapper (e.g., the hexColor assignment
and the similar call further down), which can produce inconsistent output on
some locales; change those String.format calls to use an explicit Locale (use
java.util.Locale.US) so the format specifier "#%06X" always yields a consistent
hex color string across devices. Ensure both occurrences in
PaymentOptionDisplayDataMapper are updated to String.format(Locale.US, ...)
while keeping the existing RGB_MASK and color.toArgb() arguments unchanged.

Comment on lines +228 to +243
embeddedElement.confirm { confirmResult in
let payload: [String: Any]
switch confirmResult {
case .completed:
payload = ["status": "completed"]
case .canceled:
payload = ["status": "canceled"]
case .failed(let error):
payload = [
"status": "failed",
"error": error.localizedDescription,
]
}
self.channel.invokeMethod("onConfirmComplete", arguments: payload)
result(payload)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how the Dart embedded element widget consumes confirm results.
fd -t f 'embedded_payment_element.dart' | xargs -I{} rg -nC3 -e 'onConfirmComplete' -e "invokeMethod\('confirm'" -e 'confirm\(' {}

Repository: flutter-stripe/flutter_stripe

Length of output: 906


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the dart file(s)
fd -t f 'embedded_payment_element.dart' | head -n 20

# Pick the first match and print the confirm() implementation area
FILE="$(fd -t f 'embedded_payment_element.dart' | head -n 1)"
echo "Using: $FILE"

# Show confirm() implementation
python3 - <<'PY'
import re,sys,os
path=os.popen("fd -t f 'embedded_payment_element.dart' | head -n 1").read().strip()
print("FILE:",path)
PY

# Print around confirm() and around the onConfirmComplete switch case
sed -n '140,220p' "$FILE"
sed -n '220,320p' "$FILE"

Repository: flutter-stripe/flutter_stripe

Length of output: 6340


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden --no-ignore-vcs "onFormSheetConfirmComplete" packages/stripe/lib/src -S
rg -n --hidden --no-ignore-vcs "\.confirm\(\)" packages/stripe/lib/src -S
rg -n --hidden --no-ignore-vcs "EmbeddedPaymentElement.*confirm" packages/stripe/lib/src -S
rg -n --hidden --no-ignore-vcs "embeddedPaymentElement.*confirm" packages/stripe/lib/src -S

Repository: flutter-stripe/flutter_stripe

Length of output: 660


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect controller
FILE="packages/stripe/lib/src/widgets/embedded_payment_element_controller.dart"
sed -n '1,200p' "$FILE"

# Inspect callback field + relevant parts of EmbeddedPaymentElement
FILE2="packages/stripe/lib/src/widgets/embedded_payment_element.dart"
sed -n '1,140p' "$FILE2"
sed -n '200,280p' "$FILE2"

# Find all usages of onFormSheetConfirmComplete across the repo
rg -n "onFormSheetConfirmComplete" -S .

# Find usages of EmbeddedPaymentElementController.confirm() / confirm() return handling
rg -n "EmbeddedPaymentElementController" -S packages/stripe | head -n 200
rg -n "\.confirm\(\)" -S packages/stripe/lib/src/widgets | head -n 200
rg -n "\.confirm\(" -S examples test tests integration_design packages | head -n 200 || true

Repository: flutter-stripe/flutter_stripe

Length of output: 12587


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="example/lib/screens/payment_sheet/embedded_payment_element_screen.dart"
sed -n '1,220p' "$FILE"
rg -n "controller\.confirm\(" "$FILE"
rg -n "onFormSheetConfirmComplete" "$FILE"

Repository: flutter-stripe/flutter_stripe

Length of output: 5799


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Find where onConfirmComplete is sent on iOS/Android
rg -n "onConfirmComplete" packages/stripe_ios packages/stripe_android -S

# Inspect iOS confirm handler implementation around the snippet
rg -n "embeddedElement\.confirm|invokeMethod\(\"onConfirmComplete\"|invokeMethod\('onConfirmComplete'" packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios -S
rg -n "invokeMethod\(\"onConfirmComplete\"|result\(payload\)|result\(.*payload" packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios -S

Repository: flutter-stripe/flutter_stripe

Length of output: 899


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "onConfirmComplete" packages/stripe_android -S
rg -n "case ['\"]onConfirmComplete['\"]|invokeMethod\\(.*onConfirmComplete" packages/stripe_android -S || true

# Also search confirm() handler returning payload
rg -n "fun confirm|embedded\\.confirm|invoke\\w*\\(\"confirm\"" packages/stripe_android -S

Repository: flutter-stripe/flutter_stripe

Length of output: 55


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Look for any native-to-Dart "confirm complete" method invocations on Android
rg -n "invokeMethod\\(.*(onConfirmComplete|onFormSheetConfirmComplete|embeddedPaymentElementFormSheetConfirmComplete)" packages/stripe_android -S || true

# Also search for method names as plain strings
rg -n "(onConfirmComplete|onFormSheetConfirmComplete|embeddedPaymentElementFormSheetConfirmComplete)" packages/stripe_android -S || true

Repository: flutter-stripe/flutter_stripe

Length of output: 239


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EventEmitterCompat.kt"
nl -ba "$FILE" | sed -n '50,120p'

# Search for where embeddedPaymentElementFormSheetConfirmComplete is emitted during confirm
rg -n "embeddedPaymentElementFormSheetConfirmComplete" packages/stripe_android -S

# Show relevant sections around those references
for f in $(rg -l "embeddedPaymentElementFormSheetConfirmComplete" packages/stripe_android -S); do
  echo "---- $f ----"
  nl -ba "$f" | sed -n '1,260p'
done

Repository: flutter-stripe/flutter_stripe

Length of output: 116


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EventEmitterCompat.kt"
echo "---- $FILE ----"
sed -n '50,110p' "$FILE" | cat -n

echo
echo "References to embeddedPaymentElementFormSheetConfirmComplete:"
rg -n "embeddedPaymentElementFormSheetConfirmComplete" packages/stripe_android -S

echo
for f in $(rg -l "embeddedPaymentElementFormSheetConfirmComplete" packages/stripe_android -S); do
  echo "---- $f ----"
  sed -n '1,260p' "$f" | cat -n
done

Repository: flutter-stripe/flutter_stripe

Length of output: 7565


confirm outcome is emitted both as the confirm() return value and as an onConfirmComplete callback (can cause duplicate handling).

  • iOS native code builds a single payload, calls self.channel.invokeMethod("onConfirmComplete", arguments: payload), and then returns the same payload via result(payload).
  • Dart EmbeddedPaymentElement.confirm() awaits the 'confirm' method result and returns it, while _handleMethodCall routes onConfirmComplete to onFormSheetConfirmComplete.
  • The example screen wires both onFormSheetConfirmComplete and awaits _controller.confirm(), so the same confirmation status is surfaced twice for one user action.

Clarify the intended consumption pattern (return value vs callback) or suppress one path to avoid duplicate state transitions.

🤖 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
`@packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift`
around lines 228 - 243, The native iOS code currently emits the confirm outcome
twice by both invoking the Flutter channel ("onConfirmComplete") and returning
the same payload via result(payload) from EmbeddedPaymentElement.confirm's
completion block; pick a single delivery mechanism and remove the duplicate:
either stop calling self.channel.invokeMethod("onConfirmComplete", ...) inside
the completion handler (so the Dart side only receives the outcome via the
method result that EmbeddedPaymentElement.confirm() returns), or stop calling
result(payload) and only send the outcome via "onConfirmComplete" (and ensure
Dart's _handleMethodCall routes/deserializes it to onFormSheetConfirmComplete or
the awaiting call accordingly). Update the code in the confirmation completion
block (the embeddedElement.confirm { confirmResult in ... } callback) to
implement the chosen single path and keep the existing payload construction and
result/error handling logic intact.

Comment on lines +5351 to 5417
@JsonSerializable(explicitToJson: true)
class _PaymentMethodParamsMultibanco implements PaymentMethodParams {
const _PaymentMethodParamsMultibanco({required this.paymentMethodData, final String? $type}): $type = $type ?? 'Multibanco';
factory _PaymentMethodParamsMultibanco.fromJson(Map<String, dynamic> json) => _$PaymentMethodParamsMultibancoFromJson(json);

/// Paymentmethod data for this paymentmethod.
@override final PaymentMethodData paymentMethodData;

@JsonKey(name: 'paymentMethodType')
final String $type;


/// Create a copy of PaymentMethodParams
/// with the given fields replaced by the non-null parameter values.
@JsonKey(includeFromJson: false, includeToJson: false)
@pragma('vm:prefer-inline')
$PaymentMethodDataCopyWith<PaymentMethodData> get copyWith => _$PaymentMethodDataCopyWithImpl<PaymentMethodData>(this as PaymentMethodData, _$identity);

/// Serializes this PaymentMethodData to a JSON map.
Map<String, dynamic> toJson();
_$PaymentMethodParamsMultibancoCopyWith<_PaymentMethodParamsMultibanco> get copyWith => __$PaymentMethodParamsMultibancoCopyWithImpl<_PaymentMethodParamsMultibanco>(this, _$identity);

@override
Map<String, dynamic> toJson() {
return _$PaymentMethodParamsMultibancoToJson(this, );
}

@override
bool operator ==(Object other) {
return identical(this, other) || (other.runtimeType == runtimeType&&other is PaymentMethodData&&(identical(other.billingDetails, billingDetails) || other.billingDetails == billingDetails)&&(identical(other.shippingDetails, shippingDetails) || other.shippingDetails == shippingDetails)&&(identical(other.mandateData, mandateData) || other.mandateData == mandateData)&&const DeepCollectionEquality().equals(other.metadata, metadata));
return identical(this, other) || (other.runtimeType == runtimeType&&other is _PaymentMethodParamsMultibanco&&(identical(other.paymentMethodData, paymentMethodData) || other.paymentMethodData == paymentMethodData));
}

@JsonKey(includeFromJson: false, includeToJson: false)
@override
int get hashCode => Object.hash(runtimeType,billingDetails,shippingDetails,mandateData,const DeepCollectionEquality().hash(metadata));
int get hashCode => Object.hash(runtimeType,paymentMethodData);

@override
String toString() {
return 'PaymentMethodData(billingDetails: $billingDetails, shippingDetails: $shippingDetails, mandateData: $mandateData, metadata: $metadata)';
return 'PaymentMethodParams.multibanco(paymentMethodData: $paymentMethodData)';
}


}

/// @nodoc
abstract mixin class $PaymentMethodDataCopyWith<$Res> {
factory $PaymentMethodDataCopyWith(PaymentMethodData value, $Res Function(PaymentMethodData) _then) = _$PaymentMethodDataCopyWithImpl;
abstract mixin class _$PaymentMethodParamsMultibancoCopyWith<$Res> implements $PaymentMethodParamsCopyWith<$Res> {
factory _$PaymentMethodParamsMultibancoCopyWith(_PaymentMethodParamsMultibanco value, $Res Function(_PaymentMethodParamsMultibanco) _then) = __$PaymentMethodParamsMultibancoCopyWithImpl;
@useResult
$Res call({
BillingDetails? billingDetails, ShippingDetails? shippingDetails, MandateData? mandateData, Map<String, String>? metadata
PaymentMethodData paymentMethodData
});


$BillingDetailsCopyWith<$Res>? get billingDetails;$ShippingDetailsCopyWith<$Res>? get shippingDetails;$MandateDataCopyWith<$Res>? get mandateData;
$PaymentMethodDataCopyWith<$Res> get paymentMethodData;

}
/// @nodoc
class _$PaymentMethodDataCopyWithImpl<$Res>
implements $PaymentMethodDataCopyWith<$Res> {
_$PaymentMethodDataCopyWithImpl(this._self, this._then);
class __$PaymentMethodParamsMultibancoCopyWithImpl<$Res>
implements _$PaymentMethodParamsMultibancoCopyWith<$Res> {
__$PaymentMethodParamsMultibancoCopyWithImpl(this._self, this._then);

final PaymentMethodData _self;
final $Res Function(PaymentMethodData) _then;
final _PaymentMethodParamsMultibanco _self;
final $Res Function(_PaymentMethodParamsMultibanco) _then;

/// Create a copy of PaymentMethodData
/// Create a copy of PaymentMethodParams
/// with the given fields replaced by the non-null parameter values.
@pragma('vm:prefer-inline') @override $Res call({Object? billingDetails = freezed,Object? shippingDetails = freezed,Object? mandateData = freezed,Object? metadata = freezed,}) {
return _then(_self.copyWith(
billingDetails: freezed == billingDetails ? _self.billingDetails : billingDetails // ignore: cast_nullable_to_non_nullable
as BillingDetails?,shippingDetails: freezed == shippingDetails ? _self.shippingDetails : shippingDetails // ignore: cast_nullable_to_non_nullable
as ShippingDetails?,mandateData: freezed == mandateData ? _self.mandateData : mandateData // ignore: cast_nullable_to_non_nullable
as MandateData?,metadata: freezed == metadata ? _self.metadata : metadata // ignore: cast_nullable_to_non_nullable
as Map<String, String>?,
@pragma('vm:prefer-inline') $Res call({Object? paymentMethodData = null,}) {
return _then(_PaymentMethodParamsMultibanco(
paymentMethodData: null == paymentMethodData ? _self.paymentMethodData : paymentMethodData // ignore: cast_nullable_to_non_nullable
as PaymentMethodData,
));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Align the Multibanco params variant with the new data model.

_PaymentMethodParamsMultibanco still accepts PaymentMethodData, but the newly generated _PaymentMethodDataMultibanco hierarchy does not implement PaymentMethodData. That makes the dedicated Multibanco data type unusable with the new params variant, and the generated when/maybeWhen APIs stay downcast to the generic base type.

Please either change the Multibanco params factory to use PaymentMethodDataMultibanco or remove the dedicated Multibanco data class and regenerate, so the public API is internally consistent.

Also applies to: 5794-6152

🤖 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
`@packages/stripe_platform_interface/lib/src/models/payment_methods.freezed.dart`
around lines 5351 - 5417, The _PaymentMethodParamsMultibanco variant is typed to
accept the generic PaymentMethodData but your new concrete Multibanco type is
PaymentMethodDataMultibanco (which no longer implements PaymentMethodData),
causing API inconsistency and downcasting in when/maybeWhen; update the
Multibanco params to use PaymentMethodDataMultibanco everywhere
(constructor/factory, field type, copyWith impl, equality/hashCode,
toJson/fromJson) or alternatively remove the PaymentMethodDataMultibanco class
and regenerate so the params keep PaymentMethodData—make the change for
_PaymentMethodParamsMultibanco and its copyWith implementation
(__$PaymentMethodParamsMultibancoCopyWithImpl) and repeat the same fix for the
other affected Multibanco ranges referenced (lines ~5794-6152).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Mappers.kt (1)

559-565: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Remove duplicate DisplayPayNowDetails entry in mapNextAction.

NextActionType.DisplayPayNowDetails is included in the grouped return null when branch and also handled by its own NextActionType.DisplayPayNowDetails -> { ... } branch, causing a duplicate when entry and blocking the intended mapping.

Suggested fix
     NextActionType.CashAppRedirect,
     NextActionType.BlikAuthorize,
     NextActionType.UseStripeSdk,
-    NextActionType.DisplayPayNowDetails,
     NextActionType.DisplayPromptPayDetails,
     null,
     -> {
       return null
     }
🤖 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
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Mappers.kt`
around lines 559 - 565, The when-branch for mapNextAction contains a duplicate
NextActionType.DisplayPayNowDetails: remove DisplayPayNowDetails from the
grouped branch that returns null and leave only the dedicated
NextActionType.DisplayPayNowDetails -> { ... } case; update the grouped list
(which currently includes NextActionType.CashAppRedirect,
NextActionType.BlikAuthorize, NextActionType.UseStripeSdk,
NextActionType.DisplayPayNowDetails, NextActionType.DisplayPromptPayDetails,
null, -> { ... }) to exclude NextActionType.DisplayPayNowDetails so the specific
handler in mapNextAction is the only mapping for that enum value.
🧹 Nitpick comments (1)
example/integration_test/payment_method_test.dart (1)

44-44: ⚡ Quick win

Avoid hard-coded card expiry years in integration tests.

At Line 44 and the other updated expiry/assertion lines, using a fixed year (2028) will cause the same CI breakage again once that date passes. Prefer a shared future-safe value reused across test setup and assertions.

♻️ Suggested patch
+const _testCardExpiryYear = 2099;
+
 ...
-        expirationYear: 2028,
+        expirationYear: _testCardExpiryYear,
 ...
-      expect(paymentMethod.card.expYear, equals(2028));
+      expect(paymentMethod.card.expYear, equals(_testCardExpiryYear));
 ...
-        expirationYear: 2028,
+        expirationYear: _testCardExpiryYear,
 ...
-      expect(paymentMethod.card.expYear, equals(2028));
+      expect(paymentMethod.card.expYear, equals(_testCardExpiryYear));
 ...
-        expirationYear: 2028,
+        expirationYear: _testCardExpiryYear,
 ...
-      expect(paymentMethod.card.expYear, equals(2028));
+      expect(paymentMethod.card.expYear, equals(_testCardExpiryYear));

Also applies to: 66-66, 81-81, 90-90, 113-113, 122-122, 140-140

🤖 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 `@example/integration_test/payment_method_test.dart` at line 44, Tests use a
hard-coded expirationYear (2028) in multiple places (e.g., the payment method
setup and related assertions) which will break after that date; replace all
literal 2028 occurrences with a single shared future-safe value (e.g., a
top-level constant or helper like futureYear = DateTime.now().year + N) and
update the payment method creation and every assertion that references
expirationYear to use that shared symbol so the test stays valid over time.
🤖 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 `@packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe`
Sdk/StripeSdkImpl+Embedded.swift:
- Around line 92-104: Calls to UI-affecting methods attachEmbedded and
emitLoadingFailed (and the resolve callbacks) are currently executed off the
main thread; wrap those calls in a main-thread context. Inside the Task's
do/catch, after awaiting EmbeddedPaymentElement.create, call await MainActor.run
{ attachEmbedded(embeddedPaymentElement); resolve(nil) } and in the catch call
await MainActor.run { emitLoadingFailed(error: error); resolve(nil) } so
attachEmbedded, emitLoadingFailed and resolve run on the main thread.
- Around line 56-70: The attachEmbedded call (and the subsequent resolve) in
createEmbeddedPaymentElement are running off the main actor and must be executed
on the main thread because attachEmbedded sets UIKit properties and triggers
layout (see attachEmbedded, embeddedPaymentElementDidUpdateHeight, and
EmbeddedPaymentElement.view.systemLayoutSizeFitting); change the Task body to
await MainActor.run { attachEmbedded(embeddedPaymentElement); resolve(nil) } in
the success path and likewise await MainActor.run { resolve(nil) } in the catch
path (after emitLoadingFailed(error:)) so both attachment and resolution happen
on the MainActor.

In `@packages/stripe_platform_interface/lib/src/stripe_platform_interface.dart`:
- Around line 210-211: Change the new abstract declaration of
setConfirmTokenHandler on StripePlatform into a concrete method that throws
UnimplementedError to preserve additive compatibility; locate the StripePlatform
class and replace the abstract signature void
setConfirmTokenHandler(ConfirmTokenHandler? handler); with a concrete
implementation that throws UnimplementedError (so existing downstream custom
implementations don't break), leaving existing overrides in
method_channel_stripe.dart and web_stripe.dart unchanged.

---

Outside diff comments:
In
`@packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Mappers.kt`:
- Around line 559-565: The when-branch for mapNextAction contains a duplicate
NextActionType.DisplayPayNowDetails: remove DisplayPayNowDetails from the
grouped branch that returns null and leave only the dedicated
NextActionType.DisplayPayNowDetails -> { ... } case; update the grouped list
(which currently includes NextActionType.CashAppRedirect,
NextActionType.BlikAuthorize, NextActionType.UseStripeSdk,
NextActionType.DisplayPayNowDetails, NextActionType.DisplayPromptPayDetails,
null, -> { ... }) to exclude NextActionType.DisplayPayNowDetails so the specific
handler in mapNextAction is the only mapping for that enum value.

---

Nitpick comments:
In `@example/integration_test/payment_method_test.dart`:
- Line 44: Tests use a hard-coded expirationYear (2028) in multiple places
(e.g., the payment method setup and related assertions) which will break after
that date; replace all literal 2028 occurrences with a single shared future-safe
value (e.g., a top-level constant or helper like futureYear =
DateTime.now().year + N) and update the payment method creation and every
assertion that references expirationYear to use that shared symbol so the test
stays valid over time.
🪄 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

Run ID: 989e373f-8d10-46cb-b49d-3c14f847c0f3

📥 Commits

Reviewing files that changed from the base of the PR and between 4738a1f and 311eac1.

📒 Files selected for processing (88)
  • docs.json
  • docs/multibanco.mdx
  • example/integration_test/payment_method_test.dart
  • example/lib/screens/payment_sheet/embedded_payment_element_screen.dart
  • example/lib/screens/regional_payment_methods/multibanco_screen.dart
  • example/lib/screens/screens.dart
  • packages/stripe/lib/src/stripe.dart
  • packages/stripe/lib/src/widgets/embedded_payment_element.dart
  • packages/stripe_android/android/build.gradle
  • packages/stripe_android/android/src/main/kotlin/com/facebook/react/modules/core/DeviceEventManagerModule.java
  • packages/stripe_android/android/src/main/kotlin/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt
  • packages/stripe_android/android/src/main/kotlin/com/facebook/react/viewmanagers/EmbeddedPaymentElementViewManagerInterface.java
  • packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/AuBECSDebitFormView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/AuBECSDebitFormViewManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFormView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EmbeddedPaymentElementView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EventEmitterCompat.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/FinancialConnectionsSheetManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayButtonManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayButtonView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayLauncherManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/NavigationBarView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentElementConfig.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentLauncherManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentMethodCreateParamsFactory.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentOptionDisplayDataMapper.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetAppearance.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeAbstractComposeView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/addresssheet/AddressLauncherManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/addresssheet/AddressSheetView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/CustomerSheetManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerSessionProvider.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/CheckoutAddressUpdate.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/CheckoutMappers.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/DefaultActivityLifecycleCallbacks.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Errors.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Extensions.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Mappers.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/PostalCodeUtilities.kt
  • packages/stripe_ios/ios/stripe_ios.podspec
  • packages/stripe_ios/ios/stripe_ios/Package.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/RCTBridge.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/AddressSheet/AddressSheetUtils.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/AddressSheet/AddressSheetView.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ApplePayUtils.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ApplePayViewController.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ConnectAccountOnboarding/ConnectAccountOnboardingViewController.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ConnectAccountOnboarding/ConnectAccountOnboardingViewManager.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/EmbeddedPaymentElementView.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/Mappers+Checkout.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/Mappers.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/PaymentMethodFactory.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/PaymentOptionDisplayData+ReactNative.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/PaymentSheetAppearance.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkEmitter.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Checkout.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+CustomerSheet.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+PaymentSheet.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/UIColorExtension.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/pushprovisioning/AddToWalletButtonView.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/pushprovisioning/PushProvisioningUtils.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/StripePlugin.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_objc/Stripe Sdk/StripeSdk.h
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_objc/Stripe Sdk/StripeSdkEventEmitterCompat.h
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_objc/Stripe Sdk/StripeSdkEventEmitterCompat.m
  • packages/stripe_platform_interface/lib/src/method_channel_stripe.dart
  • packages/stripe_platform_interface/lib/src/models/ach_params.g.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.freezed.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.g.dart
  • packages/stripe_platform_interface/lib/src/models/payment_sheet.dart
  • packages/stripe_platform_interface/lib/src/models/radar_session.dart
  • packages/stripe_platform_interface/lib/src/models/setup_intent.g.dart
  • packages/stripe_platform_interface/lib/src/stripe_platform_interface.dart
  • packages/stripe_platform_interface/test/test_data.dart
  • packages/stripe_web/lib/src/web_stripe.dart
✅ Files skipped from review due to trivial changes (23)
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/pushprovisioning/PushProvisioningUtils.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerSessionProvider.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ApplePayViewController.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/AuBECSDebitFormView.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ApplePayUtils.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayButtonManager.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_objc/Stripe Sdk/StripeSdkEventEmitterCompat.h
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/ReactNativeCustomerAdapter.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+CustomerSheet.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonManager.kt
  • packages/stripe_platform_interface/lib/src/models/setup_intent.g.dart
  • packages/stripe_platform_interface/lib/src/models/radar_session.dart
  • docs/multibanco.mdx
  • packages/stripe_ios/ios/stripe_ios/Package.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/UIColorExtension.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/GooglePayLauncherManager.kt
  • packages/stripe_platform_interface/lib/src/models/payment_sheet.dart
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFormView.kt
  • docs.json
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/pushprovisioning/AddToWalletButtonView.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Errors.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/FinancialConnectionsSheetManager.kt
  • packages/stripe_platform_interface/lib/src/models/payment_methods.freezed.dart
🚧 Files skipped from review as they are similar to previous changes (52)
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ConnectAccountOnboarding/ConnectAccountOnboardingViewManager.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/Extensions.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeAbstractComposeView.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/PaymentOptionDisplayData+ReactNative.swift
  • packages/stripe_platform_interface/lib/src/method_channel_stripe.dart
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkEmitter.swift
  • packages/stripe/lib/src/stripe.dart
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/AuBECSDebitFormViewManager.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_objc/Stripe Sdk/StripeSdkEventEmitterCompat.m
  • packages/stripe_ios/ios/stripe_ios.podspec
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EventEmitterCompat.kt
  • packages/stripe_platform_interface/lib/src/models/ach_params.g.dart
  • packages/stripe_android/android/src/main/kotlin/com/facebook/react/modules/systeminfo/ReactNativeVersion.kt
  • example/lib/screens/screens.dart
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/AddressSheet/AddressSheetUtils.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/StripePlugin.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/DefaultActivityLifecycleCallbacks.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/addresssheet/AddressLauncherManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetAppearance.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/EmbeddedPaymentElementView.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/CardFieldView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/NavigationBarView.kt
  • example/lib/screens/regional_payment_methods/multibanco_screen.dart
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/ConnectAccountOnboarding/ConnectAccountOnboardingViewController.swift
  • packages/stripe_android/android/build.gradle
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/CheckoutAddressUpdate.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/PaymentMethodFactory.swift
  • packages/stripe_android/android/src/main/kotlin/com/facebook/react/modules/core/DeviceEventManagerModule.java
  • packages/stripe_web/lib/src/web_stripe.dart
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentElementConfig.kt
  • packages/stripe/lib/src/widgets/embedded_payment_element.dart
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/utils/CheckoutMappers.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentLauncherManager.kt
  • example/lib/screens/payment_sheet/embedded_payment_element_screen.dart
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+PaymentSheet.swift
  • packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/Mappers+Checkout.swift
  • packages/stripe_platform_interface/lib/src/models/payment_methods.dart
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/pushprovisioning/AddToWalletButtonView.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/NativeStripeSdkModuleSpec.java
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/customersheet/CustomerSheetManager.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/AddressSheet/AddressSheetView.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Checkout.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/PaymentSheetManager.kt
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EmbeddedPaymentElementView.kt
  • packages/stripe_platform_interface/lib/src/models/payment_methods.g.dart
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/Mappers.swift
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/EmbeddedPaymentElementViewManager.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_objc/Stripe Sdk/StripeSdk.h
  • packages/stripe_android/android/src/main/kotlin/com/reactnativestripesdk/StripeSdkModule.kt

Comment on lines +56 to 70
Task {
do {
let embeddedPaymentElement = try await EmbeddedPaymentElement.create(
intentConfiguration: intentConfig,
configuration: configuration
)
attachEmbedded(embeddedPaymentElement)
resolve(nil)
} catch {
emitLoadingFailed(error: error)
// Resolve so the JS hook can finish loading; loading errors are
// surfaced through the embeddedPaymentElementLoadingFailed event.
resolve(nil)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check if EmbeddedPaymentElement.create guarantees main-thread callback
# or if other similar code in this repo uses DispatchQueue.main.async after Task
rg -n "attachEmbedded|EmbeddedPaymentElement.create" --type swift -C 3

Repository: flutter-stripe/flutter_stripe

Length of output: 5787


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant portion of StripeSdkImpl+Embedded.swift around attachEmbedded
FILE="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift"
sed -n '1,220p' "$FILE" | nl -ba | sed -n '40,170p'

# Find where emitLoadingFailed/resolve are implemented/annotated
rg -n "func emitLoadingFailed|emitLoadingFailed\\(|func resolve\\(|resolve\\(nil\\)" "$FILE"

# Inspect EmbeddedPaymentElementFactory where attachEmbeddedView is `@MainActor`
FACTORY="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift"
sed -n '130,240p' "$FACTORY" | nl -ba

Repository: flutter-stripe/flutter_stripe

Length of output: 116


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FILE="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift"
FACTORY="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift"

echo "== StripeSdkImpl+Embedded.swift (lines ~1-220) =="
sed -n '1,220p' "$FILE" | cat -n | sed -n '40,170p'

echo
echo "== StripeSdkImpl+Embedded.swift: attachEmbedded definition =="
rg -n "private func attachEmbedded" "$FILE"
rg -n "attachEmbedded\\(" "$FILE"

echo
echo "== StripeSdkImpl+Embedded.swift: emitLoadingFailed/resolve usage =="
rg -n "emitLoadingFailed\\(|func emitLoadingFailed|resolve\\(nil\\)" "$FILE"

echo
echo "== EmbeddedPaymentElementFactory.swift: attachEmbeddedView annotations =="
rg -n "attachEmbeddedView\\(" "$FACTORY"
sed -n '130,230p' "$FACTORY" | cat -n | sed -n '120,140p'

Repository: flutter-stripe/flutter_stripe

Length of output: 7480


🏁 Script executed:

#!/bin/bash
set -euo pipefail

FACTORY="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift"

# Show the attachEmbeddedView implementation (around the `@MainActor` and method body)
sed -n '160,230p' "$FACTORY" | cat -n

# Also locate any other UI-thread dispatches in StripeSdkImpl+Embedded.swift
SDK="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift"
rg -n "DispatchQueue\\.main\\.async|MainActor\\.run|`@MainActor`" "$SDK"

Repository: flutter-stripe/flutter_stripe

Length of output: 3957


🏁 Script executed:

#!/bin/bash
set -euo pipefail

SDK="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift"
# Find delegate type/instance used by attachEmbedded
rg -n "embeddedInstanceDelegate" "$SDK"

# Locate where embeddedInstanceDelegate is defined/created (in same file or nearby)
rg -n "EmbeddedPaymentElementDelegate|presentingViewController|embeddedPaymentElementDidUpdateHeight|embeddedPaymentElementDidUpdatePaymentOption" "$SDK"

# Search for the delegate methods implementation in the repo
rg -n "embeddedPaymentElementDidUpdateHeight|embeddedPaymentElementDidUpdatePaymentOption" packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios --type swift

# Search for RCTPresentedViewController usage in the repo
rg -n "RCTPresentedViewController\\(" packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios --type swift

Repository: flutter-stripe/flutter_stripe

Length of output: 3390


🏁 Script executed:

#!/bin/bash
set -euo pipefail

SDK="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift"

# Display the delegate implementation around the reported line numbers
sed -n '220,340p' "$SDK" | cat -n

# Inspect any class/actor annotations near embeddedInstanceDelegate definition
rg -n "embeddedInstanceDelegate|class StripeSdkEmbeddedPaymentElementDelegate" "$SDK"

Repository: flutter-stripe/flutter_stripe

Length of output: 6551


🏁 Script executed:

#!/bin/bash
set -euo pipefail

SDK="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift"
PLUGIN="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/StripePlugin.swift"

echo "== createEmbeddedPaymentElement method signature/context =="
rg -n "createEmbeddedPaymentElement\\(" "$SDK"
# show around the function start (first ~30 lines near match)
MATCH_LINE=$(rg -n "createEmbeddedPaymentElement\\(" "$SDK" | head -n1 | cut -d: -f1)
START=$((MATCH_LINE-15))
END=$((MATCH_LINE+80))
sed -n "${START},${END}p" "$SDK" | cat -n

echo
echo "== Where emitEmbeddedPaymentElementDidUpdateHeight is implemented =="
rg -n "emitEmbeddedPaymentElementDidUpdateHeight" "$SDK" "$PLUGIN"
# show around in StripePlugin.swift if present
LINE=$(rg -n "embeddedPaymentElementDidUpdateHeight" "$PLUGIN" | head -n1 | cut -d: -f1)
START=$((LINE-15))
END=$((LINE+40))
sed -n "${START},${END}p" "$PLUGIN" | cat -n

echo
echo "== emitEmbeddedPaymentElementDidUpdatePaymentOption implementation =="
rg -n "embeddedPaymentElementDidUpdatePaymentOption|emitEmbeddedPaymentElementDidUpdatePaymentOption" "$PLUGIN"
LINE=$(rg -n "embeddedPaymentElementDidUpdatePaymentOption" "$PLUGIN" | head -n1 | cut -d: -f1)
START=$((LINE-15))
END=$((LINE+40))
sed -n "${START},${END}p" "$PLUGIN" | cat -n

echo
echo "== Any `@MainActor/MainActor.run/DispatchQueue.main` around embedded creation? =="
rg -n "`@MainActor`|MainActor\\.run|DispatchQueue\\.main\\.async" "$SDK"

Repository: flutter-stripe/flutter_stripe

Length of output: 10584


🏁 Script executed:

#!/bin/bash
set -euo pipefail

REPO_ROOT="."

SDK="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe Sdk/StripeSdkImpl+Embedded.swift"
PLUGIN="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/StripePlugin.swift"
PLUGIN_DIR="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios"

echo "== Find emitter type/properties =="
rg -n "var emitter\\b|emitter\\?" "$SDK" "$PLUGIN_DIR" --type swift

echo
echo "== Inspect StripePlugin.swift: sendEvent/Emitter implementation =="
rg -n "sendEvent\\(withName:|class .*Emitter|struct .*Emitter|emitEmbeddedPaymentElement" "$PLUGIN_DIR" --type swift

# Show the StripePlugin event dispatch implementation around sendEvent
SEND_LINE=$(rg -n "func sendEvent\\(|sendEvent\\(withName:" "$PLUGIN" | head -n1 | cut -d: -f1 || true)
if [[ -n "${SEND_LINE:-}" ]]; then
  START=$((SEND_LINE-40)); END=$((SEND_LINE+60))
  sed -n "${START},${END}p" "$PLUGIN" | cat -n
fi

echo
echo "== Check if sendEvent is dispatched to main =="
rg -n "DispatchQueue\\.main\\.|`@MainActor`|MainActor\\.run" "$PLUGIN" --type swift

Repository: flutter-stripe/flutter_stripe

Length of output: 18166


🏁 Script executed:

#!/bin/bash
set -euo pipefail

REPO="packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios"
rg -n "EmbeddedPaymentElement\\.create\\(" "$REPO" --type swift -S

echo "---"
# See if any call-sites wrap attachEmbedded in DispatchQueue.main
rg -n "attachEmbedded\\(" "$REPO" --type swift -S

Repository: flutter-stripe/flutter_stripe

Length of output: 880


Ensure attachEmbedded runs on the main thread (UIKit/layout)

  • createEmbeddedPaymentElement calls attachEmbedded(embeddedPaymentElement) directly from a Task, without MainActor isolation.
  • attachEmbedded sets element.presentingViewController = RCTPresentedViewController() and immediately calls embeddedInstanceDelegate.embeddedPaymentElementDidUpdateHeight(...), which computes layout via embeddedPaymentElement.view.systemLayoutSizeFitting(...) (UIKit).
  • sendEvent(...) already hops to the main thread for event delivery, but these UIKit/layout operations still run on the Task’s executor; wrap the attachment (and resolve) in MainActor.
Proposed fix
     Task {
       do {
         let embeddedPaymentElement = try await EmbeddedPaymentElement.create(
           intentConfiguration: intentConfig,
           configuration: configuration
         )
-        attachEmbedded(embeddedPaymentElement)
-        resolve(nil)
+        await MainActor.run {
+          attachEmbedded(embeddedPaymentElement)
+          resolve(nil)
+        }
       } catch {
-        emitLoadingFailed(error: error)
-        // Resolve so the JS hook can finish loading; loading errors are
-        // surfaced through the embeddedPaymentElementLoadingFailed event.
-        resolve(nil)
+        await MainActor.run {
+          emitLoadingFailed(error: error)
+          resolve(nil)
+        }
       }
     }
🤖 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 `@packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe`
Sdk/StripeSdkImpl+Embedded.swift around lines 56 - 70, The attachEmbedded call
(and the subsequent resolve) in createEmbeddedPaymentElement are running off the
main actor and must be executed on the main thread because attachEmbedded sets
UIKit properties and triggers layout (see attachEmbedded,
embeddedPaymentElementDidUpdateHeight, and
EmbeddedPaymentElement.view.systemLayoutSizeFitting); change the Task body to
await MainActor.run { attachEmbedded(embeddedPaymentElement); resolve(nil) } in
the success path and likewise await MainActor.run { resolve(nil) } in the catch
path (after emitLoadingFailed(error:)) so both attachment and resolution happen
on the MainActor.

Comment on lines 92 to 104
Task {
do {
let embeddedPaymentElement = try await EmbeddedPaymentElement.create(
intentConfiguration: intentConfig,
checkout: checkout,
configuration: configuration
)
embeddedPaymentElement.delegate = embeddedInstanceDelegate
embeddedPaymentElement.presentingViewController = RCTPresentedViewController()
self.embeddedInstance = embeddedPaymentElement

attachEmbedded(embeddedPaymentElement)
resolve(nil)

embeddedInstanceDelegate.embeddedPaymentElementDidUpdateHeight(embeddedPaymentElement: embeddedPaymentElement)
embeddedInstanceDelegate.embeddedPaymentElementDidUpdatePaymentOption(embeddedPaymentElement: embeddedPaymentElement)
} catch {
let errorPayload = Errors.createError(ErrorType.Failed, error)
let errorDetails = errorPayload["error"] as? NSDictionary
let (message, code) = extractEmbeddedPaymentElementErrorInfo(
from: errorDetails,
fallbackMessage: error.localizedDescription,
fallbackCode: ErrorType.Failed
)
dispatchEmbeddedPaymentElementLoadingFailed(
message: message,
code: code,
details: errorDetails
)
resolve(errorPayload)
return
emitLoadingFailed(error: error)
resolve(nil)
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Same main-thread concern as createEmbeddedPaymentElement.

The attachEmbedded and event emission calls at lines 98-102 should also be dispatched to the main thread, consistent with the fix suggested for createEmbeddedPaymentElement.

🤖 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 `@packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/Stripe`
Sdk/StripeSdkImpl+Embedded.swift around lines 92 - 104, Calls to UI-affecting
methods attachEmbedded and emitLoadingFailed (and the resolve callbacks) are
currently executed off the main thread; wrap those calls in a main-thread
context. Inside the Task's do/catch, after awaiting
EmbeddedPaymentElement.create, call await MainActor.run {
attachEmbedded(embeddedPaymentElement); resolve(nil) } and in the catch call
await MainActor.run { emitLoadingFailed(error: error); resolve(nil) } so
attachEmbedded, emitLoadingFailed and resolve run on the main thread.

Comment on lines +210 to +211
/// Set the confirmation token handler for embedded payment elements
void setConfirmTokenHandler(ConfirmTokenHandler? handler);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
python - <<'PY'
import pathlib, re

dart_files = list(pathlib.Path(".").rglob("*.dart"))
impls = []
for f in dart_files:
    text = f.read_text(encoding="utf-8", errors="ignore")
    if re.search(r'\bclass\s+\w+\s+extends\s+StripePlatform\b', text):
        impls.append((f, text))

print("StripePlatform implementers:")
for f, _ in impls:
    print(f"- {f}")

print("\nOverride status for setConfirmTokenHandler:")
for f, text in impls:
    ok = bool(re.search(r'\bvoid\s+setConfirmTokenHandler\s*\(', text))
    print(f"{f}: {'OK' if ok else 'MISSING'}")
PY

Repository: flutter-stripe/flutter_stripe

Length of output: 636


🏁 Script executed:

#!/bin/bash
python - <<'PY'
import pathlib, re

dart_files = [p for p in pathlib.Path(".").rglob("*.dart") if p.is_file()]
impls = []
for f in dart_files:
    text = f.read_text(encoding="utf-8", errors="ignore")
    if re.search(r'\bclass\s+\w+\s+extends\s+StripePlatform\b', text):
        impls.append((f, text))

print("StripePlatform implementers:")
for f, _ in impls:
    print(f"- {f}")

print("\nOverride status for setConfirmTokenHandler:")
for f, text in impls:
    ok = bool(re.search(r'\bvoid\s+setConfirmTokenHandler\s*\(', text))
    print(f"{f}: {'OK' if ok else 'MISSING'}")
PY

Repository: flutter-stripe/flutter_stripe

Length of output: 387


Prevent downstream compile-time break from new abstract StripePlatform method

Even though the repo’s StripePlatform implementations (method_channel_stripe.dart, web_stripe.dart) already override setConfirmTokenHandler, adding it as an abstract method still forces any external downstream StripePlatform implementations to update immediately. Provide a concrete default that throws UnimplementedError to preserve additive compatibility.

💡 Compatibility-preserving shape
-  /// Set the confirmation token handler for embedded payment elements
-  void setConfirmTokenHandler(ConfirmTokenHandler? handler);
+  /// Set the confirmation token handler for embedded payment elements
+  void setConfirmTokenHandler(ConfirmTokenHandler? handler) {
+    throw UnimplementedError(
+      'setConfirmTokenHandler() has not been implemented.',
+    );
+  }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
/// Set the confirmation token handler for embedded payment elements
void setConfirmTokenHandler(ConfirmTokenHandler? handler);
/// Set the confirmation token handler for embedded payment elements
void setConfirmTokenHandler(ConfirmTokenHandler? handler) {
throw UnimplementedError(
'setConfirmTokenHandler() has not been implemented.',
);
}
🤖 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 `@packages/stripe_platform_interface/lib/src/stripe_platform_interface.dart`
around lines 210 - 211, Change the new abstract declaration of
setConfirmTokenHandler on StripePlatform into a concrete method that throws
UnimplementedError to preserve additive compatibility; locate the StripePlatform
class and replace the abstract signature void
setConfirmTokenHandler(ConfirmTokenHandler? handler); with a concrete
implementation that throws UnimplementedError (so existing downstream custom
implementations don't break), leaving existing overrides in
method_channel_stripe.dart and web_stripe.dart unchanged.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/stripe_platform_interface/lib/src/models/payment_methods.dart (1)

459-465: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wire PaymentMethodDataMultibanco into PaymentMethodParams.multibanco (or remove it)

  • PaymentMethodParams.multibanco (lines 459-465) uses required PaymentMethodData paymentMethodData, but PaymentMethodDataMultibanco is only defined here (lines ~502-519) and referenced by its own generated code—no other usage—unlike ideal, which correctly uses PaymentMethodDataIdeal.
  • Either change PaymentMethodParams.multibanco to use PaymentMethodDataMultibanco for type consistency, or remove the unused PaymentMethodDataMultibanco type.
  • PaymentMethod makes Multibanco? multibanco nullable while other payment-method-specific fields are required; align nullability or document why Multibanco differs.
  • The “Multibanco” typo warning is not supported: Multibanco is used consistently as a JSON key and in PaymentMethodType.
🤖 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 `@packages/stripe_platform_interface/lib/src/models/payment_methods.dart`
around lines 459 - 465, PaymentMethodParams.multibanco currently types its field
as PaymentMethodData but you’ve defined a dedicated PaymentMethodDataMultibanco
type that is never used; update the factory signature for
PaymentMethodParams.multibanco to accept required PaymentMethodDataMultibanco
paymentMethodData (replacing PaymentMethodData) so the specific Multibanco data
shape is wired into the union, or alternatively delete the unused
PaymentMethodDataMultibanco type if you intend to keep the generic
PaymentMethodData; additionally review the PaymentMethod class where Multibanco?
multibanco is nullable and either make it required like the other specific
fields or add a comment documenting why Multibanco is optional to keep
consistency with the rest of the models.
🤖 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.

Outside diff comments:
In `@packages/stripe_platform_interface/lib/src/models/payment_methods.dart`:
- Around line 459-465: PaymentMethodParams.multibanco currently types its field
as PaymentMethodData but you’ve defined a dedicated PaymentMethodDataMultibanco
type that is never used; update the factory signature for
PaymentMethodParams.multibanco to accept required PaymentMethodDataMultibanco
paymentMethodData (replacing PaymentMethodData) so the specific Multibanco data
shape is wired into the union, or alternatively delete the unused
PaymentMethodDataMultibanco type if you intend to keep the generic
PaymentMethodData; additionally review the PaymentMethod class where Multibanco?
multibanco is nullable and either make it required like the other specific
fields or add a comment documenting why Multibanco is optional to keep
consistency with the rest of the models.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 200e464a-6291-49d6-8cac-09542df1c8c1

📥 Commits

Reviewing files that changed from the base of the PR and between 311eac1 and a96de3f.

📒 Files selected for processing (5)
  • example/integration_test/payment_method_test.dart
  • packages/stripe_ios/ios/stripe_ios/Package.swift
  • packages/stripe_platform_interface/lib/src/models/payment_methods.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.freezed.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.g.dart
✅ Files skipped from review due to trivial changes (1)
  • packages/stripe_platform_interface/lib/src/models/payment_methods.g.dart
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/stripe_ios/ios/stripe_ios/Package.swift

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/stripe_platform_interface/lib/src/models/payment_methods.dart (1)

460-465: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Enforce Multibanco email at the Dart contract boundary.

Lines 462-465 accept generic PaymentMethodData, and Lines 500-516 keep billingDetails optional. That permits payloads without billingDetails.email, but both native paths require email and throw when it is missing (Android PaymentMethodCreateParamsFactory.kt:136-153, iOS PaymentMethodFactory.swift:304-314).

Suggested direction
   `@JsonSerializable`(explicitToJson: true)
   `@FreezedUnionValue`('Multibanco')
   /// Config parameters for Multibanco payment method.
   const factory PaymentMethodParams.multibanco({
     /// Paymentmethod data for this paymentmethod.
-    required PaymentMethodData paymentMethodData,
+    required PaymentMethodDataMultibanco paymentMethodData,
   }) = _PaymentMethodParamsMultibanco;
 `@freezed`
 /// Multi banco payment method data
 abstract class PaymentMethodDataMultibanco with _$PaymentMethodDataMultibanco {
-  `@JsonSerializable`(explicitToJson: true)
-  const factory PaymentMethodDataMultibanco({
-    /// Billing information.
-    BillingDetails? billingDetails,
+  const PaymentMethodDataMultibanco._();
+  `@JsonSerializable`(explicitToJson: true)
+  factory PaymentMethodDataMultibanco({
+    /// Billing information (email is required for Multibanco).
+    required BillingDetails billingDetails,
 
     /// Shipping details
     ShippingDetails? shippingDetails,
@@
-    Map<String, String>? metadata,
-  }) = _PaymentMethodDataMultibanco;
+    Map<String, String>? metadata,
+  }) {
+    final email = billingDetails.email;
+    if (email == null || email.isEmpty) {
+      throw ArgumentError('Multibanco requires billingDetails.email');
+    }
+    return _PaymentMethodDataMultibanco(
+      billingDetails: billingDetails,
+      shippingDetails: shippingDetails,
+      mandateData: mandateData,
+      metadata: metadata,
+    );
+  }

Also applies to: 500-520

🤖 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 `@packages/stripe_platform_interface/lib/src/models/payment_methods.dart`
around lines 460 - 465, The Multibanco factory currently accepts a generic
PaymentMethodData which allows missing billingDetails.email even though native
Android (PaymentMethodCreateParamsFactory.kt) and iOS
(PaymentMethodFactory.swift) require it; update the Dart contract so that
PaymentMethodParams.multibanco requires a PaymentMethodData that enforces
billingDetails.email (e.g., a dedicated PaymentMethodDataMultibanco type or make
billingDetails.email a non-nullable field when constructing the multibanco
variant) and mirror the same non-null requirement for the other
multibanco-related factory/constructors referenced around lines 500-520; ensure
the constructors/Freezed union uses the stricter type or validation so callers
cannot create a Multibanco params instance without billingDetails.email.
🧹 Nitpick comments (1)
example/integration_test/app_test.dart (1)

71-71: ⚡ Quick win

Avoid another hardcoded expiry rollover in this integration test.

Line 71 still hardcodes a year, so this will age out again. Prefer a relative year to keep the test stable long-term.

Proposed change
       final cardDetails = CardDetails(
         number: '4242424242424242',
         cvc: '424',
         expirationMonth: 04,
-        expirationYear: 2028,
+        expirationYear: DateTime.now().year + 2,
       );
🤖 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 `@example/integration_test/app_test.dart` at line 71, The test currently
hardcodes expirationYear: 2028 which will age out; update the integration test
in app_test.dart to compute a relative future year instead (e.g., use
DateTime.now().year + N) so the expirationYear value is always in the future;
locate the object/property setting named expirationYear and replace the literal
2028 with a computed future year based on DateTime.now().year (choose N to keep
the card valid for the test duration).
🤖 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 `@packages/stripe_ios/ios/stripe_ios/Package.swift`:
- Line 15: The Package.resolved lockfile is out of sync with the manifest which
now requires stripe-ios-spm exact "25.15.0"; update
packages/stripe_ios/ios/stripe_ios/Package.resolved to pin
"https://github.com/stripe/stripe-ios-spm" at version 25.15.0 (replacing the
current 25.9.0 entry) so dependency resolution matches the Package.swift
manifest.

---

Outside diff comments:
In `@packages/stripe_platform_interface/lib/src/models/payment_methods.dart`:
- Around line 460-465: The Multibanco factory currently accepts a generic
PaymentMethodData which allows missing billingDetails.email even though native
Android (PaymentMethodCreateParamsFactory.kt) and iOS
(PaymentMethodFactory.swift) require it; update the Dart contract so that
PaymentMethodParams.multibanco requires a PaymentMethodData that enforces
billingDetails.email (e.g., a dedicated PaymentMethodDataMultibanco type or make
billingDetails.email a non-nullable field when constructing the multibanco
variant) and mirror the same non-null requirement for the other
multibanco-related factory/constructors referenced around lines 500-520; ensure
the constructors/Freezed union uses the stricter type or validation so callers
cannot create a Multibanco params instance without billingDetails.email.

---

Nitpick comments:
In `@example/integration_test/app_test.dart`:
- Line 71: The test currently hardcodes expirationYear: 2028 which will age out;
update the integration test in app_test.dart to compute a relative future year
instead (e.g., use DateTime.now().year + N) so the expirationYear value is
always in the future; locate the object/property setting named expirationYear
and replace the literal 2028 with a computed future year based on
DateTime.now().year (choose N to keep the card valid for the test duration).
🪄 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

Run ID: 5bced921-1765-41c6-87f5-2da983007f31

📥 Commits

Reviewing files that changed from the base of the PR and between a96de3f and 7edd2d9.

📒 Files selected for processing (6)
  • example/integration_test/app_test.dart
  • example/integration_test/payment_method_test.dart
  • packages/stripe_ios/ios/stripe_ios/Package.swift
  • packages/stripe_platform_interface/lib/src/models/payment_methods.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.freezed.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.g.dart
🚧 Files skipped from review as they are similar to previous changes (2)
  • example/integration_test/payment_method_test.dart
  • packages/stripe_platform_interface/lib/src/models/payment_methods.g.dart

.library(name: "stripe-ios", targets: ["stripe_ios"])
],
dependencies: [
.package(url: "https://github.com/stripe/stripe-ios-spm", exact: "25.15.0"),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python - <<'PY'
import json
import pathlib
import re

pkg = pathlib.Path("packages/stripe_ios/ios/stripe_ios/Package.swift")
resolved = pathlib.Path("packages/stripe_ios/ios/stripe_ios/Package.resolved")

manifest = pkg.read_text(encoding="utf-8")
m = re.search(r'stripe-ios-spm",\s*exact:\s*"([^"]+)"', manifest)
manifest_version = m.group(1) if m else None

lock = json.loads(resolved.read_text(encoding="utf-8"))
resolved_version = None
for pin in lock.get("pins", []):
    if pin.get("identity") == "stripe-ios-spm":
        resolved_version = pin.get("state", {}).get("version")
        break

print(f"Package.swift version:    {manifest_version}")
print(f"Package.resolved version: {resolved_version}")
print("MATCH" if manifest_version == resolved_version else "MISMATCH")
PY

Repository: flutter-stripe/flutter_stripe

Length of output: 149


Sync SwiftPM lockfile with the manifest bump (stripe-ios-spm).

        .package(url: "https://github.com/stripe/stripe-ios-spm", exact: "25.15.0"),

packages/stripe_ios/ios/stripe_ios/Package.swift now requires stripe-ios-spm 25.15.0, but packages/stripe_ios/ios/stripe_ios/Package.resolved still pins 25.9.0, creating drift that can lead to inconsistent dependency resolution across environments. Update Package.resolved to 25.15.0.

🤖 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 `@packages/stripe_ios/ios/stripe_ios/Package.swift` at line 15, The
Package.resolved lockfile is out of sync with the manifest which now requires
stripe-ios-spm exact "25.15.0"; update
packages/stripe_ios/ios/stripe_ios/Package.resolved to pin
"https://github.com/stripe/stripe-ios-spm" at version 25.15.0 (replacing the
current 25.9.0 entry) so dependency resolution matches the Package.swift
manifest.

@remonh87 remonh87 merged commit 01d3599 into main Jun 4, 2026
9 checks passed
@remonh87 remonh87 deleted the sync_v0.66.0 branch June 4, 2026 18:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants