Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,27 @@ We also support the [Customer Sheet](https://docs.page/flutter-stripe/flutter_st
### Financial connections
We also support Financial connections in our latest sdk. Check out the [docs](https://docs.page/flutter-stripe/flutter_stripe/financial_connections) to learn more on how to set it up.

### Deep linking & coexistence with other deep-link plugins

Redirect-based payment methods (Link, iDEAL, Bancontact, PayPal, etc.) send the user out to a browser and back to your app via a `returnURL`. On iOS, flutter_stripe registers an app- and scene-delegate and forwards incoming URLs to the Stripe SDK. **For URLs that are not a pending Stripe redirect it returns `false` and does not consume them** — it does not block `app_links`, `uni_links`, `go_router`, or any other URL handler.

If a deep-link plugin stops receiving links after adding flutter_stripe, it is almost always one of the following (none of which is flutter_stripe consuming the URL):

- **`app_links` version.** On apps using `UISceneDelegate` (the Flutter default since 3.41), use **`app_links` >= 7.0.0**, which registers a scene delegate. Older versions only register an app delegate and never receive `scene:openURLContexts:`.
- **`FlutterDeepLinkingEnabled`.** If it is `true` in `Info.plist`, Flutter's own router may handle the URL. Either set it to `false`, or forward only your Stripe URLs from your router/deep-link handler (see below) and let everything else flow to your routes.
- **Scheme/returnURL mismatch.** Make sure `CFBundleURLSchemes`, `Stripe.urlScheme`, and your PaymentSheet `returnURL` all use the same scheme.

To complete a Stripe redirect while your app owns deep linking, forward matching URLs to `Stripe.instance.handleURLCallback()`:

```dart
// e.g. from your app_links / go_router listener
if (uri.scheme == 'yourappscheme' && uri.host == 'safepay') {
await Stripe.instance.handleURLCallback(uri.toString());
}
```

A full example (router-based, deep-link-package-based, and the `FlutterDeepLinkingEnabled=false` approach) is in [`example/lib/main.dart`](example/lib/main.dart). See also `Stripe.instance.handleURLCallback` in the [Dart API](#dart-api).

## Stripe initialization

To initialize Stripe in your Flutter app, use the `Stripe` base class.
Expand Down
14 changes: 14 additions & 0 deletions packages/stripe/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## Unreleased

**Breaking Changes**

- Minimum Flutter version is now **3.38** (was 3.0). Required for the iOS scene-lifecycle plugin APIs used to deliver Stripe redirect callbacks under `UISceneDelegate`. (#2422)

**Features**

- iOS: flutter_stripe now also registers as a `UISceneDelegate` (`FlutterSceneLifeCycleDelegate`), so Stripe redirect payments (Link/iDEAL/PayPal/etc.) are delivered on scene-based apps without relying on the engine's app-delegate fallback. (#2422)

**Fixes**

- iOS: the debug log for unhandled URL callbacks no longer fires for unrelated deep links and no longer implies flutter_stripe intercepted them; it now only warns when a URL using your configured Stripe `urlScheme` didn't match a pending redirect, and clarifies that flutter_stripe returns `false` and does not block other URL handlers/plugins. Added a "Deep linking & coexistence" README section. (#2422)

## 13.0.0

**Breaking Changes**
Expand Down
21 changes: 21 additions & 0 deletions packages/stripe/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,27 @@ We also support the [Customer Sheet](https://docs.page/flutter-stripe/flutter_st
### Financial connections
We also support Financial connections in our latest sdk. Check out the [docs](https://docs.page/flutter-stripe/flutter_stripe/financial_connections) to learn more on how to set it up.

### Deep linking & coexistence with other deep-link plugins

Redirect-based payment methods (Link, iDEAL, Bancontact, PayPal, etc.) send the user out to a browser and back to your app via a `returnURL`. On iOS, flutter_stripe registers an app- and scene-delegate and forwards incoming URLs to the Stripe SDK. **For URLs that are not a pending Stripe redirect it returns `false` and does not consume them** — it does not block `app_links`, `uni_links`, `go_router`, or any other URL handler.

If a deep-link plugin stops receiving links after adding flutter_stripe, it is almost always one of the following (none of which is flutter_stripe consuming the URL):

- **`app_links` version.** On apps using `UISceneDelegate` (the Flutter default since 3.41), use **`app_links` >= 7.0.0**, which registers a scene delegate. Older versions only register an app delegate and never receive `scene:openURLContexts:`.
- **`FlutterDeepLinkingEnabled`.** If it is `true` in `Info.plist`, Flutter's own router may handle the URL. Either set it to `false`, or forward only your Stripe URLs from your router/deep-link handler (see below) and let everything else flow to your routes.
- **Scheme/returnURL mismatch.** Make sure `CFBundleURLSchemes`, `Stripe.urlScheme`, and your PaymentSheet `returnURL` all use the same scheme.

To complete a Stripe redirect while your app owns deep linking, forward matching URLs to `Stripe.instance.handleURLCallback()`:

```dart
// e.g. from your app_links / go_router listener
if (uri.scheme == 'yourappscheme' && uri.host == 'safepay') {
await Stripe.instance.handleURLCallback(uri.toString());
}
```

A full example (router-based, deep-link-package-based, and the `FlutterDeepLinkingEnabled=false` approach) is in [`example/lib/main.dart`](https://github.com/flutter-stripe/flutter_stripe/blob/main/example/lib/main.dart). See also `Stripe.instance.handleURLCallback` in the [Dart API](#dart-api).

## Stripe initialization

To initialize Stripe in your Flutter app, use the `Stripe` base class.
Expand Down
11 changes: 11 additions & 0 deletions packages/stripe/lib/src/stripe.dart
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,17 @@ class Stripe {
/// );
/// ```
///
/// ## Coexisting with other deep-link plugins
///
/// On iOS, flutter_stripe also registers an app- and scene-delegate and
/// forwards incoming URLs to the Stripe SDK automatically. For URLs that are
/// not a pending Stripe redirect it returns `false` and does **not** consume
/// them, so it does not block `app_links`, `uni_links`, `go_router`, or other
/// URL handlers. If a deep-link plugin stops receiving links, check your
/// `app_links` version (>= 7.0.0 for scene-based apps), `FlutterDeepLinkingEnabled`,
/// and that your scheme/`returnURL` match. See the "Deep linking & coexistence"
/// section of the README.
///
/// Returns `true` if the URL was successfully handled by the Stripe SDK,
/// `false` otherwise. A `false` return may indicate that no active payment
/// flow was waiting for a callback.
Expand Down
3 changes: 2 additions & 1 deletion packages/stripe/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ repository: https://github.com/flutter-stripe/flutter_stripe

environment:
sdk: ">=3.8.1 <4.0.0"
flutter: ">=3.0.0"
# iOS scene-lifecycle plugin APIs used by stripe_ios require Flutter >= 3.38.
flutter: ">=3.38.0"

flutter:
plugin:
Expand Down
14 changes: 14 additions & 0 deletions packages/stripe_ios/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
## Unreleased

**Breaking Changes**

- Minimum Flutter version is now **3.38** (was 3.0), required for the iOS scene-lifecycle plugin APIs (`FlutterSceneLifeCycleDelegate` / `addSceneDelegate`). (#2422)

**Features**

- Register `StripePlugin` as a `FlutterSceneLifeCycleDelegate` (`scene:openURLContexts:` / `scene:continueUserActivity:`) so redirect callbacks are delivered under `UISceneDelegate`. (#2422)

**Fixes**

- Scope-gate and reword the unhandled-URL debug log so it no longer mis-attributes unrelated deep-link breakage to flutter_stripe. (#2422)

## 13.0.0

**Breaking Changes**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ class StripePlugin: StripeSdkImpl, FlutterPlugin, ViewManagerDelegate {
StripeSdkImpl.shared.emitter = instance
registrar.addMethodCallDelegate(instance, channel: channel)
registrar.addApplicationDelegate(instance)
// Also register as a scene-lifecycle delegate so Stripe redirect callbacks
// (Link/iDEAL/PayPal/etc.) are delivered on apps that adopt UISceneDelegate
// (default since Flutter 3.41), not only via the engine's app-delegate fallback.
registrar.addSceneDelegate(instance)

// Card Field
let cardFieldFactory = CardFieldViewFactory(messenger: registrar.messenger(), delegate:instance)
Expand Down Expand Up @@ -363,12 +367,17 @@ class StripePlugin: StripeSdkImpl, FlutterPlugin, ViewManagerDelegate {
func application(_ application: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let handled = StripeAPI.handleURLCallback(with: url)
#if DEBUG
if !handled {
print("[flutter_stripe] URL callback received but not handled by Stripe SDK: \(url.absoluteString)")
print("[flutter_stripe] If using Link or other redirect-based payment methods, ensure:")
print("[flutter_stripe] 1. The returnURL in PaymentSheet matches your app's URL scheme")
print("[flutter_stripe] 2. CFBundleURLSchemes in Info.plist includes your URL scheme")
print("[flutter_stripe] 3. If using FlutterDeepLinkingEnabled, call Stripe.handleURLCallback() manually from your Flutter deep link handler")
// Only surface a diagnostic when the URL uses the scheme the app configured for
// Stripe redirects but Stripe did not recognize it — i.e. a likely returnURL/scheme
// mismatch. We stay silent for every other scheme so flutter_stripe does not appear
// to "intercept" unrelated deep links. Returning `false` here does NOT block other
// URL handlers or plugins: the iOS/Flutter delegate chain continues past a `false`.
if !handled,
let scheme = url.scheme?.lowercased(),
let configured = urlScheme?.lowercased(),
scheme == configured {
print("[flutter_stripe] Received a '\(scheme)://' URL that didn't match a pending Stripe redirect; ignoring it (returning false). flutter_stripe is NOT blocking other URL handlers or plugins.")
print("[flutter_stripe] If a Stripe redirect payment (Link/iDEAL/PayPal/etc.) isn't completing, verify your returnURL/urlScheme and CFBundleURLSchemes — see the \"Deep linking\" section of the flutter_stripe README.")
}
#endif
return handled
Expand All @@ -385,6 +394,34 @@ class StripePlugin: StripeSdkImpl, FlutterPlugin, ViewManagerDelegate {
}
}

// MARK: - Scene lifecycle parity
//
// Mirrors the `application(_:open:options:)` / `application(_:continue:…)` handlers above for
// apps that adopt UISceneDelegate (default since Flutter 3.41). Without this, a running
// scene-based app delivers redirect URLs via `scene(_:openURLContexts:)`, and flutter_stripe
// (being app-delegate-only) would only be reached through the engine's app-delegate fallback —
// which a custom UISceneDelegate may bypass, breaking Stripe redirect payments. Like the
// app-delegate handlers, these return `false` for non-Stripe URLs so other plugins still run.
extension StripePlugin: FlutterSceneLifeCycleDelegate {
@objc func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) -> Bool {
var handled = false
for context in URLContexts {
handled = StripeAPI.handleURLCallback(with: context.url) || handled
}
return handled
}

// The protocol requirement is imported under the Swift name `scene(_:continue:)`
// (UIKit renames the `scene:continueUserActivity:` selector), so implement it under
// exactly that name and let Swift wire the selector as the protocol witness.
func scene(_ scene: UIScene, continue userActivity: NSUserActivity) -> Bool {
if userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL {
return StripeAPI.handleURLCallback(with: url)
}
return false
}
}

extension StripePlugin: StripeSdkEmitter {
func emitPaymentMethodMessagingElementDidUpdateHeight(_ value: [String : Any]) {
self.sendEvent(withName: "paymentMethodMessagingElementDidUpdateHeight", body: value)
Expand Down
4 changes: 3 additions & 1 deletion packages/stripe_ios/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ homepage: https://pub.dev/packages/flutter_stripe

environment:
sdk: ">=3.8.1 <4.0.0"
flutter: ">=3.0.0"
# Requires the iOS scene-lifecycle plugin APIs (FlutterSceneLifeCycleDelegate /
# addSceneDelegate), available only in Flutter >= 3.38.
flutter: ">=3.38.0"

dependencies:
flutter:
Expand Down
Loading