微信支付接入全流程与常见问题解决方案

梦雅 移动 阅读 961
赞 17 收藏
二维码
手机扫码查看
反馈

又双叒叕被微信支付坑了

前几天接到个需求,要在H5页面里集成微信支付。想着这玩意儿我都做过好几次了,应该没啥问题吧?结果现实狠狠打了我的脸。

微信支付接入全流程与常见问题解决方案

最开始遇到的问题是调用支付接口时一直报错:-1,这个错误码官方文档压根没提。折腾了半天才发现是因为签名生成有问题。

核心代码就这几行

先说解决方案,下面是完整的支付调用代码:

// 微信支付初始化
function initWeChatPay(data) {
  return new Promise((resolve, reject) => {
    if (typeof WeixinJSBridge === "undefined") {
      document.addEventListener('WeixinJSBridgeReady', () => {
        onBridgeReady(data, resolve, reject);
      }, false);
    } else {
      onBridgeReady(data, resolve, reject);
    }
  });
}

function onBridgeReady(data, resolve, reject) {
  WeixinJSBridge.invoke(
    'getBrandWCPayRequest', {
      appId: data.appId,     // 公众号名称
      timeStamp: data.timeStamp, // 时间戳
      nonceStr: data.nonceStr, // 随机串
      package: data.package,  // 统一下单接口返回的 prepay_id
      signType: data.signType,// 签名方式
      paySign: data.paySign   // 支付签名
    },
    function(res) {
      if (res.err_msg === "get_brand_wcpay_request:ok") {
        resolve(true);
      } else {
        reject(res.err_msg);
      }
    }
  );
}

这里我踩了个大坑:签名算法不对

重点说下签名这块,我一开始直接用了后端给的参数,但一直报错。后来试了下发现几个关键点:

  • 字段顺序必须正确:微信要求按照字典序排序,不能乱来
  • 大小写敏感:appId 和 appid 是不一样的
  • 时间戳单位:必须是秒级时间戳

正确的签名生成代码如下:

function generateSign(params, key) {
  const sortedKeys = Object.keys(params).sort();
  let stringA = '';
  
  sortedKeys.forEach(k => {
    if (params[k] !== '' && params[k] != null) {
      stringA += ${k}=${params[k]}&;
    }
  });
  
  const stringSignTemp = stringA + key=${key};
  return md5(stringSignTemp).toUpperCase();
}

支付环境检测也很重要

另一个让我头疼的问题是支付环境判断。在开发过程中发现,如果不在微信内置浏览器里打开,WeixinJSBridge 对象会是 undefined。

这里要注意,微信支付只能在微信内置浏览器中使用。为了确保支付功能正常,最好加上环境检测:

function isWeChatBrowser() {
  const ua = navigator.userAgent.toLowerCase();
  return /micromessenger/.test(ua);
}

if (!isWeChatBrowser()) {
  alert("请在微信客户端中打开");
  return;
}

后台接口对接那些事儿

说到后台接口,这里也有个坑要提醒大家。最初我们后端直接把所有参数都返回给我,我拿过来就用,结果各种报错。

后来和后端同学一起排查发现:

  • 预支付订单接口需要通过服务端调用统一下单接口获取 prepay_id
  • 支付签名必须在服务端生成并返回
  • 前端只需要负责调用支付接口即可

以下是我们最终约定的接口格式:

{
  "appId": "wx1234567890abcdef",
  "timeStamp": "1625436890",
  "nonceStr": "randomstring123456",
  "package": "prepay_id=wx20210705abcdefg",
  "signType": "MD5",
  "paySign": "C380BEC2BFD727A4B6845133519F3AD6"
}

还有一些小细节要注意

虽然主要问题都解决了,但还是遇到了些小状况:

  • Android 和 iOS 的支付回调表现不太一样,iOS 有时会有延迟
  • 用户取消支付后的处理逻辑需要特别注意,不能简单地当成失败
  • 支付成功后的页面跳转需要考虑微信的安全域名限制

比如安全域名这个事,我就踩过坑。记得要把所有可能跳转的域名都配置到微信公众平台的 JS 安全域名列表里:

const API_URL = 'https://jztheme.com/api';
const SUCCESS_URL = 'https://jztheme.com/pay-success';
const FAIL_URL = 'https://jztheme.com/pay-fail';

以上是我踩坑后的总结

微信支付确实是个深坑,特别是对于新手来说。从签名算法到支付环境,从前端调用到后端接口,每个环节都可能出问题。

不过经过这次折腾,我对整个支付流程的理解更深了。虽然过程很痛苦,但解决问题后的成就感还是挺爽的。

如果你有更好的实现方案,或者遇到其他奇葩问题,欢迎在评论区交流。毕竟微信支付这种东西,谁碰谁知道有多复杂。

本文章不代表JZTHEME立场,仅为作者个人观点 / 研究心得 / 经验分享,旨在交流探讨,供读者参考。
发表评论

暂无评论