Search code examples
flutterwebviewstripe-payments

"Stripe.js requires 'allow-same-origin' if sandboxed." Error thrown while trying to open Stripe checkout in Flutter webview


I'm working on an app with Stripe payment in it. Since Stripe's checkout session only works on web apps, I've looked for some tutorials and added webview_flutter to open the checkout screen. This is what my checkout page looks like:

class CheckoutPage extends StatefulWidget {
  final String? sessionId;

  const CheckoutPage({Key? key, this.sessionId}) : super(key: key);

  @override
  _CheckoutPageState createState() => _CheckoutPageState();
}

class _CheckoutPageState extends State<CheckoutPage> {
  WebViewController? _controller;
  final navigationService = locator<NavigationService>();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      resizeToAvoidBottomInset: false,
      body: WebView(
        initialUrl: initialUrl,
        javascriptMode: JavascriptMode.unrestricted,
        onWebViewCreated: (webViewController) =>
            _controller = webViewController,
        onPageFinished: (String url) {
          if (url == initialUrl) {
            print('page is ready');
            _redirectToStripe(widget.sessionId!);
          }
        },
        navigationDelegate: (NavigationRequest request) {
          if (request.url.startsWith('http://localhost:8080/#/success')) {
            Navigator.of(context).pushReplacementNamed('/success');
          } else if (request.url.startsWith('http://localhost:8080/#/cancel')) {
             Navigator.of(context).pushReplacementNamed('/cancel');
          }
          return NavigationDecision.navigate;
        },
      ),
    );
  }

  String get initialUrl =>
      'data:text/html;base64,${base64Encode(Utf8Encoder().convert(kStripeHtmlPage))}';

  Future<void> _redirectToStripe(String sessionId) async {
    print('session id: $sessionId');
    final redirectToCheckoutJs = '''
              var stripe = Stripe(\'$publishableKey\');
                  
              stripe.redirectToCheckout({
                sessionId: '$sessionId'
              }).then(function (result) {
                result.error.message = 'Error'
              });
              ''';

    try {
      await _controller!.runJavascriptReturningResult(redirectToCheckoutJs);
    } on PlatformException catch (e) {
      if (!e.details.contains(
          'JavaScript execution returned a result of an unsupported type')) {
        rethrow;
      }
    }
  }
}

const kStripeHtmlPage = '''
<!DOCTYPE html>
<html>
<script src="https://js.stripe.com/v3/"></script>
<head><title>Stripe checkout</title></head>
<body>
Loading...
</body>
</html>
''';

The session id is correct and all so I'm positive there are no issues there. But when this page is opened, it's stuck on the initialUrl page. It won't open the Stripe checkout. These 3 issues can be seen in the console:

"Unrecognized feature: 'payment'.", source: https://js.stripe.com/v3/ (1)
"Stripe.js requires 'allow-same-origin' if sandboxed.", source: https://js.stripe.com/v3/ (1)
"Uncaught SecurityError: Failed to read the 'cookie' property from 'Document': Cookies are disabled inside 'data:' URLs.", source: https://js.stripe.com/v3/ (1)

Any idea what might be causing this issue? Any help will be greatly appreciated.

I have tried searching for different methods or any existing solutions for this problem but nothing solid turned up.


Solution

  • webview_flutter uses iframes to render the view, but Stripe Checkout is not supported in an iframe.

    It requires a full page redirect so you should not try to embed it in an iframe and instead redirect your customer to the Checkout page.