Skip to content

【收款】

1. 有些支付渠道需要打开三方app进行支付,如果webView不处理,拉起三方app失败。

问题:

有些支付渠道需要打开三方app进行支付,如果webView不处理,拉起三方app失败。比如:使用GoPay支付时,提示“"ERR_UNKNOWN_URL_SCHEME"错误;LinePay, AirPay没有办法正常拉起相应app。

解答:

如果打开API的容器是Android的WebView,需要复写WebViewClient,下面是示例代码。

java
public class TestWebViewClient extends WebViewClient {
    private static final String TAG = "TestWebViewClient";
    private Activity mContext;
    private List<String> HTTP_SCHEMES = Arrays.asList("http", "https");

    public TestWebViewClient(Activity context, WebView webView) {
        this.mContext = context;
    }

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        Log.d(TAG, "shouldOverrideUrlLoading url1=" + url);
        if(shouldOverrideUrlLoadingInner(view, url)) {
            return true;
        }
        return super.shouldOverrideUrlLoading(view, url);
    }

    @Override
    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
        String url = request != null && request.getUrl() != null ? request.getUrl().toString() : "";
        Log.d(TAG, "shouldOverrideUrlLoading url=" + (request != null ? request.getUrl().toString() : ""));
        if(shouldOverrideUrlLoadingInner(view, url)) {
            return true;
        }
        return super.shouldOverrideUrlLoading(view, request);
    }

    /**
     * Parse the url and open it by system function.
     *   case 1: deal "intent://xxxx" url.
     *   case 2: deal custom scheme. url
     * @param view: WebView
     * @param url
     * @return
     */
    private boolean shouldOverrideUrlLoadingInner(WebView view, String url) {
        if(!TextUtils.isEmpty(url)) {
            Uri uri = Uri.parse(url);
            if(uri != null) {
                if ("intent".equals(uri.getScheme())) {
                    try {
                        Intent intent = Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME);
                        if(intent != null) {
                            PackageManager pm = mContext.getPackageManager();
                            ResolveInfo info = pm.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY);
                            if(info != null) {
                                mContext.startActivity(Intent.parseUri(uri.toString(), Intent.URI_INTENT_SCHEME));
                                return true;
                            }
                            else {
                                String fallbackUrl = intent.getStringExtra("browser_fallback_url");
                                if (!TextUtils.isEmpty(fallbackUrl)) {
                                    if(fallbackUrl.startsWith("market://"))
                                        startAppMarketWithUrl(mContext, fallbackUrl, false);
                                    else
                                        view.loadUrl(fallbackUrl);
                                    return true;
                                }
                            }
                        }
                    } catch (Exception e) {
                    }
                }
                if (!HTTP_SCHEMES.contains(uri.getScheme())) {
                    startUrl(mContext, url, true);
                    return true;
                }
            }
        }

        return false;
    }

    public static void startUrl(Context context, String url, boolean isNewTask) {
        if(context != null && !TextUtils.isEmpty(url)) {
            try {
                Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
                if(isNewTask) {
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                }
                context.startActivity(intent);

            } catch (Exception e) {
            }
        }
    }

    public static boolean hasActivity(Context context, Intent intent, String packageName) {
        PackageManager pm = context.getPackageManager();
        List<ResolveInfo> appList = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);

        for (ResolveInfo info : appList) {
            if (info.activityInfo.packageName.equals(packageName))
                return true;
        }
        return false;
    }

    public static void startAppMarketWithUrl(Context context, String url, boolean forceUseGoogle) {
        try {
            Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
            if (forceUseGoogle || hasActivity(context, intent, "com.android.vending"))
                intent.setPackage("com.android.vending");
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        } catch (Exception e) {
            try {
                startUrl(context, url, true);
            } catch (Exception e1) {}
        }
    }
}

点击下载:TestWebViewClient.java

2. 打开DANA支付时,页面显示空白或"The network connection is unstable. Please try again later."

问题:

API标准入款接入方式, 打开DANA支付方式, 页面提示错误, 看到的现象是空白,实际页面比较大, 移动页面可以看到页面错误:“The network connection is unstable. Please try again later."

解答:

如果打开PaySDK的容器是Android 的WebView,需要设置webView的WebSettings属性,代码如下

webSettings.setDomStorageEnabled(true);
webSettings.setTextZoom(100);
webSettings.setUseWideViewPort(true);

3. 支付成功后想返回自己的APP的方法

解答:

在自己APP内部打开收银台url,在支付完成打开跳转地址(前端回调地址frontCallBackUrl)也是在App内部。 如果用外部浏览器打开收银台URL,在支付完成打开跳转地址(前端回调地址frontCallBackUrl)时是在外部浏览器中打开的,这时如果要跳转到App内部,需要通过(schema DeepLink)跳转。

4. 异步回调通知注意事项

解答:

1、回调地址必须真实存在; 2、协议格式: application/json; 3、协议返回内容:请查看【异步通知】 地址不通的问题(如果商户服务器对外部 IP 地址访问有限制,需要将payermax服务器IP地址添加到商户服务器的白名单中。

5. 点击"确认支付"按钮后,没有打开后续支付页面

问题:

API标准入款接入方式,点击”确认支付”按钮后,没有打开后续支付页面

解答:

如果打开API的容器是Android的WebView,需要对WebView做如下处理,下面是示例代码:

mWebView.setWebViewClient(new WebViewClient());

6. 进入收银台,页面提示“请求参数格式错误”

问题:

商户使用String.format(Locale.default(), "%.2f", price)格式化金额,造成金额格式为乱码

解答:

由于Locale.getDefault()是获取当前设备的所在地区的locale,不同地区格式后的字符串不一样。建议使用 String.format(Locale.ENGLISH, "%.2f", price)

7. 使用UPI支付时,出现界面没有办法退出

问题:

使用UPI支付进入到渠道页,操作然后点击返回键没有办法回到上个页面或者退出后,再次进入打不开收银台

解答:

如果商户在离开webView时,使用“webView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);"关闭当前JS线程。需要修改为“webView.loadUrl("about:blank");”

8. 收银台页面支付方式展示不全或者样式混乱等适配问题

问题:

如果遇到收银台页面支付方式展示不全或者样式混乱,例如:示例.png

解答:

商户需要设置webView的下面参数:
mWebView.getSettings().setTextZoom(100);

9. 支付时软键盘覆盖输入框

问题:

在支付时,有些输入方式需要输入手机号或邮箱,这时候点击输入框,软键盘会覆盖输入框。

解答:

出现上面问题,需要对容器做如下检查:

  1. 不要设置全屏模式
  2. windowSoftInputMode可以不设置或者设置为adjustResize, 不要设置为adjustPan
  3. 如果是沉浸式状态栏,需要在布局中设置fitSystemWindows=true

10. 商户传给PayerMax的URI是大写的APP://开头,为什么我方在跳转的时候变成了app://开头的?

因为浏览器和webview自动转换的,cheme在浏览器里是不分大小写的,会统一转为小写。 在manifest中配置时,scheme 和 host 都要全为小写

11. 页面报这类错误'X-Frame-Options' 是什么原因

一般是渠道侧页面不支持iframe嵌套,请检查下 web服务中是否设置 X-Frame-Options。

12. 如果打开收银台链接地址的容器是Android WebView,请参考下面代码设置

java
        WebSettings webSettings = mWebView.getSettings();
        webSettings.setJavaScriptEnabled(true);

        webSettings.setSupportMultipleWindows(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

        webSettings.setPluginState(WebSettings.PluginState.ON); //enable plugin. Ex: flash. deprecated on API 18
        //whether the zoom controls display on screen.
        webSettings.setBuiltInZoomControls(true);
        webSettings.setSupportZoom(true);
        webSettings.setDisplayZoomControls(false);

        //disable the webview font size changes according the phone font size.
        webSettings.setTextZoom(100);
        webSettings.setSaveFormData(true);
        webSettings.setUseWideViewPort(true);
        webSettings.setLoadWithOverviewMode(true);
        webSettings.setAllowFileAccess(true);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
            CookieManager.getInstance().setAcceptThirdPartyCookies(this, true);
        }
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
            webSettings.setAllowUniversalAccessFromFileURLs(true);
        }

        webSettings.setAppCacheEnabled(true);
        String appCacheDir = getDir("cache", Context.MODE_PRIVATE).getPath();
        webSettings.setAppCachePath(appCacheDir);
        webSettings.setAppCacheMaxSize(1024*1024*20);
        webSettings.setDomStorageEnabled(true);
        webSettings.setDatabaseEnabled(true);
        webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);

        try {
            mWebView.removeJavascriptInterface("searchBoxJavaBridge_");
            mWebView.removeJavascriptInterface("accessibility");
            mWebView.removeJavascriptInterface("accessibilityTraversal");
        } catch (Exception e) {}

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mWebView.enableSlowWholeDocumentDraw();
        }

13. 页面报"net::ERR_CACHE_MISS"

这是因为网络权限配置异常。可以更改 AndroidManifest.xml 中的权限配置尝试。 old: new: 参考:https://stackoverflow.com/questions/30637654/android-webview-gives-neterr-cache-miss-message

14. 系统浏览器正常,用 iOS 的 wkwebview 跳转会失败,无法正常打开页面

这个可能原因是wkwebview 更改 url 编码,导致链接异常,链接中 # 被 urlEncode 为 %23。可以webview 增加代码逻辑,避免 url 被异常编码

OC
-(NSString *)WM_FUNC_urlEncode:(NSString *)urlStr{
NSMutableCharacterSet *set  = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
[set addCharactersInString:@"#"];
return [urlStr stringByAddingPercentEncodingWithAllowedCharacters:set];
}
swift
swift 4.0
func WM_FUNC_urlEncode(_ urlStr:String) -> String {
if urlStr.isEmpty {
   return ""
}
var charSet = CharacterSet.urlQueryAllowed
charSet.insert(charactersIn: "#")
let encodingURLStr = urlStr.addingPercentEncoding(withAllowedCharacters: charSet)
return encodingURLStr ?? ""
}

下面模仿oc写法: CharacterSet 转换 NSMutableCharacterSet 来操作

func WM_FUNC_urlEncode(_ urlStr:String) -> String {
if urlStr.isEmpty {
   return ""
}
let charSet = CharacterSet.urlQueryAllowed as NSCharacterSet
let mutSet = charSet.mutableCopy() as! NSMutableCharacterSet
mutSet.addCharacters(in: "#")
let encodingURLStr = urlStr.addingPercentEncoding(withAllowedCharacters: mutSet as CharacterSet)
return encodingURLStr ?? ""
}

func WM_FUNC_urlEncode(_ urlStr:String) -> String {
if urlStr.isEmpty {
return ""
}
let charSet = NSMutableCharacterSet()
charSet.formUnion(with: CharacterSet.urlQueryAllowed)
charSet.addCharacters(in: "#")
let encodingURLStr = urlStr.addingPercentEncoding(withAllowedCharacters: charSet as CharacterSet)
return encodingURLStr ?? ""
}

参考: https://www.jianshu.com/p/e4938ada31e6

15.安卓9.0 以上手机打不开页面,协议改成 https,9.0 以下的都好了,以上的不行。

这个原因可能是Android P 限制了 http 协议的明文流量的网络请求,非加密的流量请求都会被系统禁止掉。您可以将相关 api / 页面更换成 https 协议 若仍需使用 http,可通过下列方法使 Android webview 支持 http 协议:

方法一: 1.res 目录下新建 xml 目录,xml 目录下新建 network_security_config.xml文件:

xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
  <base-config cleartextTrafficPermitted="true" />
</network-security-config>

2.AndroidManifest.xml

xml
<application android:networkSecurityConfig="@xml/network_security_config">
  <!--android9.0适配-->
  <uses-library android:name="org.apache.http.legacy" android:required="false" />
</application>

方法二: AndroidManifest.xml添加:

xml
<application android:usesCleartextTraffic="true">

参考 https://juejin.cn/post/6844903813929762824

16.webview 加载页面报错,net::ERR_BLOCKED_BY_RESPONSE。

这个原因是该地址不支持在 iframe 中使用。建议您将页面地址直接打开,不在 iframe 中使用。

【付款】

误认为出款接口响应码APPLY_SUCCESS为支付成功

问题:

收到出款接口响应码是APPLY_SUCCESS, 以为支付成功

解答:

响应码APPLY_SUCCESS只是说明当前协议响应是成功的;如果要查询是否支付成功,以服务端回调结果为准(商户传入或配置服务端回调地址)或以主动查询为准。

【参数配置】

1.如何生成密钥?

您可登录商户平台,通过「开发参数」频道生成和重置密钥。配置时,需注意区分测试环境和正式环境。

2.如何添加回调地址?

PayerMax支持两种方式设置回调地址:

  1. 拥有开发参数权限的操作员或超级管理员可在商户平台中的「开发参数」频道生成和修改回调地址。
  2. 在下单时传入回调地址。 注意:若您传入的回调地址和商户平台配置的回调地址有冲突,以您传入的回调地址为准。

3.为何没收到回调通知?

  1. 请确认是否设置回调地址。
  2. 请确认回调地址是否填写正确,若仍有问题,请联系PayerMax平台查明原因。

Released under the MIT License.