Search code examples
flutterpaypalpaypal-sandbox

PayPal WebView in Flutter doesnt work on Physical Device


I have built a function to give my users the ability to pay with PayPal:

class PaypalServicesService {

  String domain = "https://api.sandbox.paypal.com"; // for sandbox mode
  //String domain = "https://api.paypal.com"; // for production mode

  // change clientId and secret with your own, provided by paypal
  String clientId = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
  String secret = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';

  // for getting the access token from Paypal
  Future<String> getAccessToken() async {
    try {
      var client = BasicAuthClient(clientId, secret);
      var response = await client.post('$domain/v1/oauth2/token?grant_type=client_credentials');
      if (response.statusCode == 200) {
        final body = convert.jsonDecode(response.body);
        return body["access_token"];
      }
      return null;
    } catch (e) {
      rethrow;
    }
  }

  // for creating the payment request with Paypal
  Future<Map<String, String>> createPaypalPayment(
      transactions, accessToken) async {
    try {
      var response = await http.post("$domain/v1/payments/payment",
          body: convert.jsonEncode(transactions),
          headers: {
            "content-type": "application/json",
            'Authorization': 'Bearer ' + accessToken
          });

      final body = convert.jsonDecode(response.body);
      if (response.statusCode == 201) {
        if (body["links"] != null && body["links"].length > 0) {
          List links = body["links"];

          String executeUrl = "";
          String approvalUrl = "";
          final item = links.firstWhere((o) => o["rel"] == "approval_url",
              orElse: () => null);
          if (item != null) {
            approvalUrl = item["href"];
          }
          final item1 = links.firstWhere((o) => o["rel"] == "execute",
              orElse: () => null);
          if (item1 != null) {
            executeUrl = item1["href"];
          }
          return {"executeUrl": executeUrl, "approvalUrl": approvalUrl};
        }
        return null;
      } else {
        throw Exception(body["message"]);
      }
    } catch (e) {
      rethrow;
    }
  }

  // for executing the payment transaction
  Future<String> executePayment(url, payerId, accessToken) async {
    try {
      var response = await http.post(url,
          body: convert.jsonEncode({"payer_id": payerId}),
          headers: {
            "content-type": "application/json",
            'Authorization': 'Bearer ' + accessToken
          });

      final body = convert.jsonDecode(response.body);
      if (response.statusCode == 200) {
        return body["id"];
      }
      return null;
    } catch (e) {
      rethrow;
    }
  }
}

On an emulator of Android 10 it works fine for both sandbox and live.

However, on a physical Android 10 device (RedNote pro 9) the payment process is stuck on the PayPal screen, before loading the complete WebView:

Image stuck

Here is the log on RedNote device side:

02-08 10:54:27.819 I/Perf    (17856): Connecting to perf service.
02-08 10:54:27.839 I/FeatureParser(17856): can't find joyeuse.xml in assets/device_features/,it may be in /vendor/etc/device_features
02-08 10:54:27.827 W/m.tortel.syslog(17856): type=1400 audit(0.0:197147): avc: denied { read } for name="u:object_r:vendor_default_prop:s0" dev="tmpfs" ino=12172 scontext=u:r:untrusted_app:s0:c253,c256,c512,c768 tcontext=u:object_r:vendor_default_prop:s0 tclass=file permissive=0
02-08 10:54:27.840 E/libc    (17856): Access denied finding property "ro.vendor.df.effect.conflict"
02-08 10:54:27.866 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.870 I/chatty  (17856): uid=10253(com.tortel.syslog) identical 6 lines
02-08 10:54:27.877 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.877 D/SysLog  (17856): Fragment null, replacing
02-08 10:54:27.879 D/SysLog  (17856): Loading settings
02-08 10:54:27.882 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.894 I/chatty  (17856): uid=10253(com.tortel.syslog) identical 7 lines
02-08 10:54:27.895 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.895 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.897 I/chatty  (17856): uid=10253(com.tortel.syslog) identical 3 lines
02-08 10:54:27.898 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.899 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.900 I/chatty  (17856): uid=10253(com.tortel.syslog) identical 1 line
02-08 10:54:27.907 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.919 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.919 I/chatty  (17856): uid=10253(com.tortel.syslog) identical 1 line
02-08 10:54:27.919 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.925 D/SysLog  (17856): Checking for root
02-08 10:54:27.925 D/SysLog  (17856): Setting the checkboxes
02-08 10:54:27.925 D/SysLog  (17856): Checking if last_kmsg and radio are available
02-08 10:54:27.952 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.953 I/chatty  (17856): uid=10253(com.tortel.syslog) identical 1 line
02-08 10:54:27.954 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.961 I/AdrenoGLES(17856): QUALCOMM build                   : 57da73e, I82b4603a7a
02-08 10:54:27.961 I/AdrenoGLES(17856): Build Date                       : 12/03/20
02-08 10:54:27.961 I/AdrenoGLES(17856): OpenGL ES Shader Compiler Version: EV031.27.05.03
02-08 10:54:27.961 I/AdrenoGLES(17856): Local Branch                     : 
02-08 10:54:27.961 I/AdrenoGLES(17856): Remote Branch                    : refs/tags/AU_LINUX_ANDROID_LA.UM.8.9.R1.10.00.00.558.071
02-08 10:54:27.961 I/AdrenoGLES(17856): Remote Branch                    : NONE
02-08 10:54:27.961 I/AdrenoGLES(17856): Reconstruct Branch               : NOTHING
02-08 10:54:27.961 I/AdrenoGLES(17856): Build Config                     : S P 8.0.12 AArch64
02-08 10:54:27.962 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.963 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:27.963 I/AdrenoGLES(17856): PFP: 0x016ee187, ME: 0x00000000
02-08 10:54:27.986 W/Gralloc3(17856): mapper 3.x is not supported
02-08 10:54:27.999 D/SysLog  (17856): Root not available
02-08 10:54:28.026 D/SysLog  (17856): Enabling log button: true
02-08 10:54:28.031 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:28.031 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:28.032 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:28.039 I/chatty  (17856): uid=10253(com.tortel.syslog) identical 2 lines
02-08 10:54:28.040 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:29.798 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:29.798 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:29.800 V/SysLog  (17856): Path: /data/user/0/com.tortel.syslog/cache/logs/2021-02-08_10.54/
02-08 10:54:29.801 D/ForceDarkHelper(17856): updateByCheckExcludeList: pkg: com.tortel.syslog activity: com.tortel.syslog.FragmentMainActivity@b885376
02-08 10:54:29.806 V/SysLog  (17856): Done grabbing logs

Solution

  • PayPal doesn't support 'WebViews'. Only Chrome Custom Tabs (Android) or Safari View Controllers (iOS), which have an address bar. Ref: https://developer.paypal.com/docs/api/info-security-guidelines/#secure-applications


    Also you are using the original REST v1/payments API, which will still work but is deprecated. The current PayPal Checkout API is v2/checkout/orders, documented here.