Android 应用内微信 H5 支付

一般情况下,要实现应用内支付接入 App 支付 SDK 即可满足业务需求,不过考虑到对于一些类似游戏中心的场景,更多是需要支持
H5 支付。相对微信来说,支付宝的对接简单完善很多,所以本篇文章主要说说接入微信 H5 支付的流程和一些问题。

申请流程

按照微信支付官网 H5 支付说明 ,目前是没有直接申请接入 H5 支付的入口,只能在微信商户平台中去另行开通。然鹅~微信商户平台账户也不支持直接注册申请,只能先注册微信开放平台后接入微信 App 支付后才会有商户账号分配。

1. 注册微信开放平台账户

2. 申请开放平台开发者认证

3. 创建一个应用提交申核

需要应用相关资质,主要是为了开通支付功能。

4. 为应用申请微信 App 支付,开通微信支付功能

开通成功后会自动分配微信商户平台账户

5. 登录商户平台申请开通 H5 支付

这里只是简单介绍下申请流程的主要环节,具体操作起来有多麻烦我也不想去体会。

应用内接入

说到这里可能有些人想笑了,既然叫 H5 支付那不是应该跟应用本身没多大关系才对,不就是一个支付链接跳转而已吗。

话是这么说没错,但是具体操作起来还是有些坑需要去踩。由于微信 H5 支付本身就是浏览器网页支付场景下的产物,所以微信官方并不推荐在应用中使用 H5 支付。

跟浏览器不一样,在 WebView 中我们还需要自己处理一些问题。比如为了实现调起微信支付,需要对支付链接进行拦截后才能进行处理,下面就来看看这个流程。

 WebViewClient webViewClient = new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
         // 判断 url 的 scheme 进行相应的处理
            if (url.startsWith("weixin://")){ 
               try{
                    startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
                    return true;
                }catch (Exception e) {
                    //防止手机没有安装处理某个 scheme 开头的 url 的APP导致crash
                    AlertDialog.Builder builder;
                    builder = new AlertDialog.Builder(mActivity);
                    builder.setTitle("支付中心").setMessage("该手机没有安装微信客户端,请安装微信后重新完成支付,或换用支付宝进行支付").setPositiveButton("确定", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialogInterface, int i) {
                            dialogInterface.dismiss();
                        }
                    }).create().show();
                    return true;
                }
            }else if (url.startsWith("alipays://") || url.startsWith("alipay")){ 
                try{
                     startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
                    return true;
                }catch (Exception e) {
                    // 启动支付宝失败,换成网页支付
                    return true;
                }
            }

            if (!(url.startsWith("http") || url.startsWith("https"))) {
                return true;
            }

            view.loadUrl(url, map);
            return true;
        }
}

商家参数格式有误,请联系商家解决

你以为这样就完了是吧,然鹅并没有,实际开发中很大机率会出现微信提示商家参数格式有误,请联系商家解决的问题,而且更尴尬的是:这个问题并不会 iOS 中出现,也不会 Android 浏览器中出现,偏偏就是在应用的 WebView 中出现了。

查看官方文档出错问题介绍,说是当前调起 H5支付的 referer 为空导致,WTF? 难道 Android WebView 打开一个链接的 referer 不知指向当前页面的域名?都说实践是检验真理的唯一标准,抓包看看好像还真的是,很好,再一次感觉到了 Android 系统咖喱味代码。

没办法,这锅也不能甩给微信,只能按照文档说的解决方法自己来背。然而这文档说的也是不明不白的,只是说域名设置要一致,废话不多说,直接动手更简单,下面给出示例代码(已自行检验过,真实可用的)

直接在原有的代码基础上进行更改

 WebViewClient webViewClient = new WebViewClient() {
        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
          
           ...

            if (!(url.startsWith("http") || url.startsWith("https"))) {
                return true;
            }

            // 比如我们申请时填写的是经常用来测试网络连通性的 http://www.baidu.com
            HashMap<String, String> map = new HashMap<String, String>();
            // 指定申请微信 H5 支付时填写的域名,
            map.put("Referer", "http://www.baidu.com");
            view.loadUrl(url, map);
            return true;
        }
}

But... 测试过程中发现一个兼容性问题:在4.4.4、4.4.3的设备上,下单时问题还是复现了,通过抓包发现设置的 Referer 并没有生效

最终参考这篇文章中提到方法成功的解决了问题

if (("4.4.3".equals(android.os.Build.VERSION.RELEASE))
                || ("4.4.4".equals(android.os.Build.VERSION.RELEASE))) {
     //兼容这两个版本设置referer无效的问题
     view.loadDataWithBaseURL("商户申请H5时提交的授权域名",
                    "<script>window.location.href=\"" + targetUrl + "\";</script>",
                    "text/html", "utf-8", null);
 } else {
      Map<String, String> extraHeaders = new HashMap<>();
      extraHeaders.put("Referer", "商户申请H5时提交的授权域名");
      view.loadUrl(targetUrl, extraHeaders);
 }

有朋友反映说用了上面这个方法后,页面处于循环加载的状态,这其实是跟 shouldOverrideUrlLoading 的使用方式有关,有兴趣的同学可以参考这篇文章:关于shouldOverrideUrlLoading方法的一些考证

关于 shouldOverrideUrlLoading 方法返回值的说明:

  • 若没有设置 WebViewClient 则由系统(Activity Manager)处理该 url,通常是使用浏览器打开或弹出浏览器选择对话框。
  • 若设置 WebViewClient 且该方法返回 true ,则说明由应用的代码处理该 url,WebView 不处理,也就是程序员自己做处理。
  • 若设置 WebViewClient 且该方法返回 false,则说明由 WebView 处理该 url,即用 WebView 加载该 url。

下面给出最终修改完成以后的完整代码:

完整解决方案


// 设置微信 H5 支付调用 loadDataWithBaseURL 的标记位,避免循环调用,
// 再次进入微信 H5 支付流程时记得重置此标记位状态 
boolean firstVisitWXH5PayUrl = true;

WebViewClient webViewClient = new WebViewClient() {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {

        if (url.startsWith("weixin://")) {
            try {
                startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
                return true;
            } catch (Exception e) {
                // 防止手机没有安装处理某个 scheme 开头的 url 的 APP 导致 crash
                showToast("该手机没有安装微信");
                return true;
            }
        } else if (url.startsWith("alipays://") || url.startsWith("alipay")) {
            try{
                startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
                return true;
            } catch (Exception e) {
                // 防止手机没有安装处理某个 scheme 开头的 url 的 APP 导致 crash
                // 启动支付宝 App 失败,会自行跳转支付宝网页支付
                return true;
            }
        }

        // 处理普通 http 请求跳转 
        if (!(url.startsWith("http") || url.startsWith("https"))) {
            return true;
        }

        // 处理微信 H5 支付跳转时验证请求头 referer 失效
        // 验证不通过会出现“商家参数格式有误,请联系商家解决”
        if (url.contains("wx.tenpay.com")){

            // 申请微信 H5 支付时填写的域名
            // 比如经常用来测试网络连通性的 http://www.baidu.com
            String referer = GameConfig.WX_H5_PAY_HOST;

            // 兼容 Android 4.4.3 和 4.4.4 两个系统版本设置 referer 无效的问题
            if (("4.4.3".equals(android.os.Build.VERSION.RELEASE))
                    || ("4.4.4".equals(android.os.Build.VERSION.RELEASE))) {
                 if (firstVisitWXH5PayUrl){
                     view.loadDataWithBaseURL(referer, "<script>window.location.href=\"" + url + "\";</script>",
                               "text/html", "utf-8", null);
                     // 修改标记位状态,避免循环调用
                     // 再次进入微信H5支付流程时记得重置状态 firstVisitWXH5PayUrl = true
                     firstVisitWXH5PayUrl = false;
                 }
                 // 返回 false 由系统 WebView 自己处理该 url
                return false;
            } else {
                // HashMap 指定容量初始化,避免不必要的内存消耗
                HashMap<String, String> map = new HashMap<>(1);
                map.put("Referer", referer);
                view.loadUrl(url, map);
                return true;
            }
        }
        return false;
    }
}

Over...

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,568评论 25 707
  • 银联支付,支付宝支付,微信支付的三大总结,之前也有写过两篇。 微信支付,支付宝支付,银联支付——三大支付总结: h...
    LucasAdam阅读 4,789评论 2 23
  • 1 express 2 utility (返回一个utility对象,上面有各种加密方法) 3 supe...
    vera1996阅读 165评论 0 0
  • 月到是秋分外明,又是一年团圆日。 中秋回不了家只能给爸妈转账,略表一下心意,也只能通过这种形式表达自己的一点点心意...
    仰望天空的猫阅读 278评论 0 0
  • 米米说: 那并不是我想要的生活,莫不是上天憎恶我,我也不会如此编写那些只属于我的日子。这个可爱的人呀,在我错误过后...
    李译阅读 887评论 2 19