Skip to content

Add EmbeddedPaymentElement update support and fix #2426#2427

Open
programmeraditya wants to merge 4 commits into
flutter-stripe:mainfrom
Quibli-Development:feat/embedded-update
Open

Add EmbeddedPaymentElement update support and fix #2426#2427
programmeraditya wants to merge 4 commits into
flutter-stripe:mainfrom
Quibli-Development:feat/embedded-update

Conversation

@programmeraditya

@programmeraditya programmeraditya commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Adds EmbeddedPaymentElementController.update(...) so Flutter apps can update the embedded payment element intent configuration without recreating the platform view.
Also fixes #2426

Changes

  • Adds update(IntentConfiguration) to EmbeddedPaymentElementController.
  • Adds iOS and Android platform update handling.
  • Uses the initialized iOS plugin SDK instance for embedded element creation/update so Apple Pay configuration is available.
  • Resolves Android confirm/update results from native completion events.
  • Parses Android update config through the same readable map path used during initial creation.

  • 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?

melos run analyze does not succeed but it's unrelated to this PR.

  • Have you developed the feature on the latest stable version of Flutter?

I made these changes while on Flutter version 3.41.5, but they should work on the latest stable version 3.44.0 too because most of the changes are on native side.


  • 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.

While I manually did most of the code changes, I used AI assistance to debug and identify source for #2426. Specifically, I used AI assistance for Android changes. The summary above was also generated with the help of AI.


Summary by CodeRabbit

  • New Features

    • Added update() method to EmbeddedPaymentElement and EmbeddedPaymentElementController, enabling dynamic configuration updates without recreating the payment form.
  • Bug Fixes

    • Improved handling of pending asynchronous operations to prevent dangling futures and memory issues on app disposal.
  • Improvements

    • Enhanced intent configuration processing across all platforms for better consistency and error handling.

@coderabbitai

coderabbitai Bot commented Jun 7, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds async update() capability to EmbeddedPaymentElement across Flutter, Android, and iOS platforms. The Android implementation refactors confirm() to use completers for async result handling. Critically, iOS is refactored to use dependency injection instead of the shared singleton, fixing the merchantIdentifier initialization bug reported in issue #2426.

Changes

EmbeddedPaymentElement update() and iOS dependency injection

Layer / File(s) Summary
Controller API for update()
packages/stripe/lib/src/widgets/embedded_payment_element_controller.dart
EmbeddedPaymentElementController and abstract EmbeddedPaymentElementContext add public update(IntentConfiguration) method signatures.
Async state and lifecycle management
packages/stripe/lib/src/widgets/embedded_payment_element.dart
Widget imports dart:async and maintains _pendingConfirm and _pendingUpdate completers; dispose() safely resolves pending completers with null to prevent dangling futures.
Android update() and confirm() with completers
packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt
Android platform view handles new "update" method by parsing and validating intent configuration, building the config, and updating the embedded view; confirm() is refactored to register a pending completer and return its future, with actual completion driven by platform callbacks.
Dart widget update() method and completion callbacks
packages/stripe/lib/src/widgets/embedded_payment_element.dart
update() implements platform-specific behavior: non-Android directly invokes and returns the result; Android cancels any pending update, registers a new completer, and returns its future. Method-call handler is extended to complete pending update and confirm operations when platform callbacks arrive.
Intent configuration helpers
packages/stripe/lib/src/widgets/embedded_payment_element.dart
Helper methods _completePendingUpdate() and _completePendingConfirm() centralize completer management; _intentConfigurationToJson() transforms IntentConfiguration to JSON with flags for handler presence; build() uses the helper instead of inline transformation.
iOS dependency injection refactor
packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift, packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/StripePlugin.swift
iOS factory and platform view accept injected StripeSdkImpl instance instead of using the shared singleton; all embedded element operations (creation, confirm, update, clearPaymentOption) and intentCreationCallback now route through the injected instance. StripePlugin passes itself when registering the factory, ensuring merchantIdentifier and other SDK state are properly shared.

Sequence Diagram(s)

sequenceDiagram
  participant Controller as EmbeddedPaymentElementController
  participant Widget as EmbeddedPaymentElement (Dart)
  participant Android as Android Platform
  participant iOS as iOS Platform
  
  Controller->>Widget: update(IntentConfiguration)
  
  alt Android
    Widget->>Widget: Register pending update completer
    Widget->>Android: invokeMethod('update', intentConfig)
    Android->>Android: Validate & build config
    Android->>Android: Update embedded view
    Note over Android: Async completion via callback
    Android-->>Widget: embeddedPaymentElementUpdateComplete
    Widget->>Widget: Complete pending completer
    Widget-->>Controller: Future<Map?>
  else iOS
    Widget->>iOS: invokeMethod('update', intentConfig)
    iOS->>iOS: Validate & build config
    iOS->>iOS: Update embedded view
    iOS-->>Widget: Immediate result Map
    Widget-->>Controller: Future<Map?>
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • flutter-stripe/flutter_stripe#2239: Both PRs modify the EmbeddedPaymentElement Flutter widget and its confirm() flow over the method channel; this PR extends the async support added in #2239 with update() capability and completer-based lifecycle management.

Suggested labels

Awaiting response

Suggested reviewers

  • jonasbark
  • remonh87

Poem

🐰 A rabbit hops through async streams,
With completers and update dreams,
iOS sheds its singleton chains,
Dependency flows through injected veins,
Stripe elements dance in proper sync!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: adding update support to EmbeddedPaymentElement and fixing the iOS merchantIdentifier issue (#2426).
Linked Issues check ✅ Passed The PR fully addresses the #2426 objective by ensuring iOS uses the initialized plugin SDK instance so merchantIdentifier is available, and adds the requested update API functionality.
Out of Scope Changes check ✅ Passed All changes are directly related to the objectives: update API implementation, platform support for updates, iOS SDK instance injection, and Android async result handling.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@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.

🧹 Nitpick comments (1)
packages/stripe/lib/src/widgets/embedded_payment_element.dart (1)

187-192: ⚡ Quick win

Consider adding error cleanup for consistency.

The Android confirm() path doesn't clear _pendingConfirm if invokeMethod throws, unlike the update() method (lines 419–426) which explicitly clears _pendingUpdate on exception. While this isn't a memory leak (the next operation will cancel it), adding consistent error cleanup would prevent leaving an uncompleted completer referenced.

♻️ Proposed fix to match update() pattern
 if (defaultTargetPlatform == TargetPlatform.android) {
   _completePendingConfirm({'status': 'canceled'});
   final completer = Completer<Map<String, dynamic>?>();
   _pendingConfirm = completer;
-  await channel.invokeMethod('confirm');
+  try {
+    await channel.invokeMethod('confirm');
+  } catch (_) {
+    if (identical(_pendingConfirm, completer)) {
+      _pendingConfirm = null;
+    }
+    rethrow;
+  }
   return completer.future;
 }
🤖 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
187 - 192, The Android confirm() path can leave _pendingConfirm set if
channel.invokeMethod('confirm') throws; wrap the await
channel.invokeMethod('confirm') call in a try/catch and in the catch clear
_pendingConfirm (set it to null) before rethrowing the error so it matches the
error-cleanup pattern used by update() (and keeps _completePendingConfirm and
the created Completer behavior unchanged).
🤖 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.

Nitpick comments:
In `@packages/stripe/lib/src/widgets/embedded_payment_element.dart`:
- Around line 187-192: The Android confirm() path can leave _pendingConfirm set
if channel.invokeMethod('confirm') throws; wrap the await
channel.invokeMethod('confirm') call in a try/catch and in the catch clear
_pendingConfirm (set it to null) before rethrowing the error so it matches the
error-cleanup pattern used by update() (and keeps _completePendingConfirm and
the created Completer behavior unchanged).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fdbac2da-cb03-4e2a-80ec-841e05ba0a88

📥 Commits

Reviewing files that changed from the base of the PR and between c807baa and 78e4052.

📒 Files selected for processing (5)
  • packages/stripe/lib/src/widgets/embedded_payment_element.dart
  • packages/stripe/lib/src/widgets/embedded_payment_element_controller.dart
  • packages/stripe_android/android/src/main/kotlin/com/flutter/stripe/StripeSdkEmbeddedPaymentElementPlatformView.kt
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/EmbeddedPaymentElementFactory.swift
  • packages/stripe_ios/ios/stripe_ios/Sources/stripe_ios/StripePlugin.swift

@programmeraditya

Copy link
Copy Markdown
Contributor Author

@remonh87 #2426 breaks the embedded payment feature on iOS, so I would appreciate if this PR was reviewed as soon as possible. Thank you!

@realmeylisdev

Copy link
Copy Markdown
Contributor

@remonh87 I traced this on iOS: initialise sets merchantIdentifier on the StripePlugin instance (StripeSdkImpl.swift:180), but the embedded element reads it from the separate StripeSdkImpl.shared singleton (StripeSdkImpl+Embedded.swift:296), so Apple Pay throws .missingMerchantId. This PR routes the embedded element through the initialised instance, which fixes it — mind giving it a review?

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.

EmbeddedPaymentElement fails to load on iOS because merchantIdentifier was not found

2 participants