flutterwebviewflutterwebviewpluginmastercardmpgs-session-js

Error while integrating MPGs with flutter


I'm having an issue when working with MPGs. The payment source provided the HTML code first, and I obtained the season ID via an API request. https://mcb.gateway.mastercard.com/api/rest/version/75/merchant/{merchant-id}/session

The HTML read the session ID but I got those errors while the web view was supposed to show the payment form

I/flutter (17252): JavaScript Console: Uncaught SecurityError: Failed to read the 'sessionStorage' property from 'Window': Access is denied for this document.

I/flutter (17252): JavaScript Console: Possible Unhandled Promise Rejection: SecurityError: Failed to read the 'sessionStorage' property from 'Window': Access is denied for this document.

What is the issue that's causing that?

Code

 InAppWebView(
                                
       initialUrlRequest: URLRequest(
        url: Uri.parse(''),
        ),
        initialOptions: InAppWebViewGroupOptions(
        crossPlatform: InAppWebViewOptions(
        cacheEnabled: true,
        useShouldOverrideUrlLoading: true,
        javaScriptEnabled: true,
        javaScriptCanOpenWindowsAutomatically: true,
        mediaPlaybackRequiresUserGesture: false,
        ),
        android: AndroidInAppWebViewOptions(
        useHybridComposition: true,
        domStorageEnabled: true,
        ),
        ios: IOSInAppWebViewOptions(
        allowsInlineMediaPlayback: true,
        )),
        androidOnPermissionRequest: (controller, origin, resources)          async {
        return PermissionRequestResponse(
               resources: resources,
               action: PermissionRequestResponseAction.GRANT,
                );
                 },
         onWebViewCreated: (controller) async {
         webView = controller;
         await webView?.loadData(
    //html content
         data: '''
         <html>
         <head>
         <meta name="viewport" content="width=device-width, initial-         scale=1">
        <script src="https://mcb.gateway.mastercard.com/static/checkout/checkout.min.js" data-error="errorCallback" data-cancel="cancelCallback"></script>
        <script type="text/javascript">
            function errorCallback(error) {
                  console.log(JSON.stringify(error));
            }
            function cancelCallback() {
                  console.log('Payment cancelled');
            }
            Checkout.configure({
              session: { 
                id: '${state.id}'
                }
            });
        </script>
    </head>
    <body>
       ...
      <div id="embed-target"> </div> 
      <input type="button" value="Pay with Embedded Page" onclick="Checkout.showEmbeddedPage('#embed-target');" />
      <input type="button" value="Pay with Payment Page" onclick="Checkout.showPaymentPage();" />
       ...
    </body>
</html>
             ''',
    mimeType: 'text/html',
                                    );
                                 
                                  },
         onLoadError: (controller, url, code, message) {
         print('Error loading $url: $code, $message');
         },
         onLoadStop: (controller, url) {},
         onConsoleMessage: (controller, consoleMessage) {
         final logMessage = consoleMessage.message;
         print('JavaScript Console: $logMessage');
                                  },
                                );

Solution

  • I understand we are both working on integrating MPGS payments in Flutter.

    In my recent work, I also came across the limitation of the flutter_inappwebview package not supporting local storage. You can make sure from this issue opened on Github.

    To address this, I evaluated a few solutions that I tried them and may help you to move your project forward:

    Solution 1:

    I have created an HTML page that contains a script and data needed to load the MPGS integration.

    <html>
    <head>
        <script
                src="https://test-gateway.mastercard.com/static/checkout/checkout.min.js"
                data-error="errorCallback"
                data-cancel="cancelCallback"
        ></script>
        <script type="text/javascript">
    
           const params = new URLSearchParams(document.location.search);
           const sessionId = params.get("session_id");
    
          function errorCallback(error) {
            console.log(JSON.stringify(error));
          }
          function cancelCallback() {
            console.log("Payment cancelled");
          }
    
          Checkout.configure({
            session: {
              id: `${sessionId}`,
            },
          }).then(() => {
            Checkout.showPaymentPage();
          });
        
        </script>
    </head>
    </html>
    

    This page is uploaded to our server.

    From the mobile side, I request the HTML page from the server and pass the session ID as a query parameter in the URL. For example:

    www.example.com/mpgs.html?session_id=xxxxxxxx
    

    The MPGS script on the HTML page can then retrieve the session ID from the query string to initialize the payment flow. This allows me to load the MPGS webview content from a remote URL on our own server, while still passing the necessary session ID from the mobile app. The webview can then communicate back payment statuses and tokens.

    Solution 2:

    I have added the above page to App assets project and use the In-App Localhost Server to host the page HTML locally, pass the session_id like above (query parameter) and load the script.

    The following code I have implemented:

    Define the variable

      final InAppLocalhostServer localhostServer = InAppLocalhostServer();
    

    Then initialize it

        Future.microtask(() async => await localhostServer.start());
    

    After that, added the following widget

    InAppWebView(
          initialUrlRequest: URLRequest(
              url: Uri.parse(
                  "http://localhost:8080/assets/html_files/paymentGateWay.html?session_id=SESSION0002174711341F46647517L2")),
       
          initialOptions: uiController.webViewOptions,
          onProgressChanged: (controller, progress) =>
              uiController.setLoadingProgress(progress / 100),
          androidOnPermissionRequest: (controller, origin, resources) =>
              uiController.getPermissionRequestResponse(resources),
          onLoadError: (controller, url, code, message) {
            print('Error loading $url: $code, $message');
          },
          onUpdateVisitedHistory: (controller, url, androidIsReload) {
    
            print(url);
          },
          onLoadStop: (controller, url) {},
          onConsoleMessage: (controller, consoleMessage) {
            final logMessage = consoleMessage.message;
            print('JavaScript Console: $logMessage');
          },
        );
    

    While hosting the HTML page locally and redirecting to MPGS is a workable solution, there is constraint to consider in my opinion : The app would need to allow for HTTP URL schemes in order to host the HTML page locally. However, for security and privacy reasons, most apps do not allow non-HTTPS schemes by default.

    Let me know what your opinion on the Solution 2.