微信支付接入全流程与常见坑点实战解析

司空景景 移动 阅读 1,454
赞 26 收藏
二维码
手机扫码查看
反馈

为啥要折腾微信支付的方案?

做移动端项目,尤其是 H5 商城类应用,绕不开微信支付。但微信官方文档写得有点“绕”,不同场景下能用的接入方式还不一样,搞得我每次新项目都得重新翻文档、查历史代码。折腾多了才发现,其实主流就三种路子:公众号 JSAPI、H5 支付(也就是 mweb)、还有小程序支付。今天我就从实战角度,说说我踩过的坑、偏好的选择,以及什么情况下该用哪个。

微信支付接入全流程与常见坑点实战解析

谁更灵活?谁更省事?

先说结论:如果是纯 H5 页面(非公众号内),我首选 H5 支付;如果在公众号里,JSAPI 是唯一靠谱选项;小程序另算,基本没得选。

很多人一上来就想用 JSAPI,觉得“官方推荐”就一定好。但 JSAPI 有个致命前提:必须在微信浏览器内打开,且用户已关注公众号(或通过 OAuth2 获取过 openid)。如果你的页面是通过朋友圈、微信群分享出去的,用户点开时没关注公众号,那 JSAPI 根本跑不起来——这时候你只能引导用户关注,体验极差。

而 H5 支付(mweb)就没这限制。它本质是跳转到微信的支付中间页,不管用户在哪打开(哪怕是 Safari、Chrome),只要手机装了微信 App,就能拉起支付。虽然多一步跳转,但胜在通用。我做过一个活动页,80% 流量来自外部链接,果断用了 H5 支付,上线后支付成功率比预想高不少。

核心代码就这几行,但坑不少

先看 H5 支付的后端逻辑(以 Node.js 为例,但原理通用):

// 后端生成 mweb 支付参数
const params = {
  body: '商品描述',
  out_trade_no: '订单号',
  total_fee: 1, // 单位分
  spbill_create_ip: req.ip,
  notify_url: 'https://jztheme.com/api/wechat-notify',
  trade_type: 'MWEB',
  scene_info: JSON.stringify({
    h5_info: {
      type: 'Wap'
    }
  })
};

const payUrl = await wechat.unifiedOrder(params);
// 返回 mweb_url 给前端
res.json({ mweb_url: payUrl.mweb_url });

前端拿到 mweb_url 后直接跳转:

// 前端调起支付
window.location.href = data.mweb_url;

看起来简单吧?但这里有两个大坑我踩过好几次:

  • scene_info 必须传,而且 type 只能是 Wap。早期文档没强调,我漏了这字段,结果在安卓上能支付,iOS 直接报错“商户参数错误”。
  • 跳转后不能立即关闭页面。有些同学为了体验流畅,在跳转后马上执行 window.close(),但微信支付中间页需要回跳(通过 redirect_url),如果你提前关了,用户付完钱回不到原页面,容易以为失败重付。

相比之下,JSAPI 的前端调用更“原生”一点:

// 前端调用微信 JSAPI
WeixinJSBridge.invoke('getBrandWCPayRequest', {
  appId: res.appId,
  timeStamp: res.timeStamp,
  nonceStr: res.nonceStr,
  package: res.package,
  signType: res.signType,
  paySign: res.paySign
}, function(result) {
  if (result.err_msg === 'get_brand_wcpay_request:ok') {
    // 支付成功
  }
});

但问题在于,这段代码必须在微信浏览器里才能执行。如果你在普通浏览器里测试,WeixinJSBridge 根本不存在,得加判断:

if (typeof WeixinJSBridge === 'undefined') {
  alert('请在微信中打开');
  return;
}

更麻烦的是,JSAPI 需要提前通过 OAuth2 拿到用户的 openid,这意味着你得先做一次授权跳转。对于追求转化率的活动页来说,多一步授权就可能流失一批用户。

我的选型逻辑

我现在的选型标准很简单:看流量来源和用户路径。

如果这个页面大概率是在公众号菜单、模板消息里打开的(比如会员中心、订单页),那用 JSAPI,体验最顺滑——支付弹窗直接在当前页弹出,不用跳转。

但如果页面会通过外部渠道传播(比如海报扫码、短信链接、其他 App 内嵌),那就毫不犹豫上 H5 支付。虽然多了个中间页,但至少能保证所有用户都能走到支付环节。至于小程序,那完全是另一套体系,调用 wx.requestPayment 就行,没啥可比性。

还有一种混合场景:同一个 H5 页面,既可能在公众号内打开,也可能在外链打开。这时候我会动态判断环境:

function isWechatBrowser() {
  return /MicroMessenger/i.test(navigator.userAgent);
}

function isSubscribed() {
  // 这里需要你提前通过接口判断用户是否已关注(比如后端存了 openid)
  // 简化处理:假设我们有个全局变量 user.subscribed
  return window.user?.subscribed;
}

if (isWechatBrowser() && isSubscribed()) {
  // 走 JSAPI
  callJSAPI();
} else {
  // 走 H5 支付
  redirectToMweb();
}

这种方案虽然复杂点,但兼顾了两种场景。不过要注意,isSubscribed() 的实现依赖后端支持,如果你没做用户绑定,那就只能退化为“只要是微信浏览器就试 JSAPI,失败再 fallback 到 H5 支付”。

别被文档吓到,其实没那么难

微信支付文档确实啰嗦,但核心流程就两步:后端统一下单,前端调起支付。关键是要搞清楚你的用户从哪来、在哪付。我见过太多团队死磕 JSAPI,结果外链用户根本付不了,最后还得返工切 H5 方案。

另外提醒一点:无论哪种方案,异步通知(notify_url)一定要做好幂等处理。微信可能会多次回调,别让用户重复发货或扣款。我在 jztheme.com 的示例里就吃过亏,没加幂等,测试时刷出三笔订单……

总的来说,H5 支付虽然“土”一点,但胜在皮实;JSAPI 更优雅,但依赖强。没有绝对优劣,只有适不适合。我现在做新项目,除非明确限定在公众号生态内,否则一律默认 H5 支付,省心。

以上是我踩坑后的总结,希望对你有帮助。有更优的实现方式欢迎评论区交流。

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

暂无评论