项目 App 需要做到支付功能,首先接入的是 WeChat Pay。业务逻辑需要,支付功能需要在 WebView 中实现,也就是使用 H5 支付。

我只要写一个搭载 WebView 组件的 Activity 并初始化好相关设置,然后剩下的交给 Web 开发就好了。

首先,想要使用 WeChat Pay 的接口,要先向微信支付商户平台提交申请,因为我们是需要在 H5 中调起微信支付,所以就需要申请 H5 支付的权限,提交申请材料后需要等待 3~5 个工作日后才能知道申请结果,由于是测试版本的原因,内容填写的并不是太完善,但是审核也不是太严格。

接下来就是在 H5 中按照微信官方提供的开发文档来搭建这个支付接口了。

Android 方面,也需要配置一定的代码,直接请求的话会出现以下错误:

H5 支付错误 - 商家参数格式有误

微信支付的开发文档有相关的错误描述:商家参数格式有误,请联系商家解决。

官方提供的解决方法如下:

  1. 当前调起H5支付的referer为空导致,一般是因为直接访问页面调起H5支付,请按正常流程进行页面跳转后发起支付,或自行抓包确认referer值是否为空
  2. 如果是APP里调起H5支付,需要在webview中手动设置referer,如(
    Map extraHeaders = new HashMap();
    extraHeaders.put("Referer", "商户申请H5时提交的授权域名");//例如 http://www.baidu.com ))

不过这个示例也对小白太不友好了,就这么两行代码,十分考验想象力。

在网上查了下前辈们的做法,得出以下代码:

webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);

// 判断为微信支付头则调起微信支付
webView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        if (url.startsWith("weixin://wap/pay?")) {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);
            intent.setData(Uri.parse(url));
            startActivity(intent);
            return true;
        } else {
            Map extraHeaders = new HashMap();
            extraHeaders.put("Referer", domain);            // 商户申请H5时提交的授权域名
            view.loadUrl(url, extraHeaders);
        }
        return super.shouldOverrideUrlLoading(view, url);
    }
});
webView.loadUrl(payUrl);

这里重写了 WebViewClient 内的 shouldOverrideUrlLoading(WebView, String) 方法,使其检测 URL 中是否带有微信支付头,即 weixin://wap/pay?,如果是,则调起 WeChat Pay,而在实测中,基本都会跑到这个判断中,而不会跑到下方微信提供的方法,所以暂时不知道其有何作用。

另外,使用 shouldOverrideUrlLoading(WebView, String) 的时候发现该方法被标记为已过时,虽然暂时只是不推荐使用,未被抛弃,不过还是尽快转移到更新的方法上比较好,我在文档中看到有一个相似的方法 shouldOverrideUrlLoading(WebView, WebResourceRequest),粗略的浏览了一下,似乎还没有人使用该方法替代上面的方法,于是自行尝试了一下,发现可以把上方的代码改写为:

@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
        String url = request.getUrl().toString();
        if (url.startsWith("weixin://wap/pay?")) {
            Intent intent = new Intent();
            intent.setAction(Intent.ACTION_VIEW);
            intent.setData(Uri.parse(url));
            startActivity(intent);
            return true;
        } else {
            Map extraHeaders = new HashMap();
            extraHeaders.put("Referer", DOMAIN);            // 商户申请H5时提交的授权域名
            view.loadUrl(url, extraHeaders);
        }
    }
    return super.shouldOverrideUrlLoading(view, request);
}

其实差别也不大,只不过 URL 不能从形参中直接获取而是要从 WebResourceRequest 中获取罢了。

有时候不行可能是因为 Referer 没写进去,可以使用以下方法抓取 Referer

MyWebViewClient myWebViewClient = new MyWebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        ...
        Log.d("===WebViewURL===", view.getUrl());       // 打印当前页面的 Url
        Uri ref = getReferrer();     // 抓取 Referer
        if (ref != null) {
            Log.d("====REFERER====", ref.toString());
        } else {
            Log.e("====REFERER====", "NULL");
        }
        return super.shouldOverrideUrlLoading(view, url);
    }
};