微信支付接入全流程与常见坑点实战解析
为啥要折腾微信支付的方案?
做移动端项目,尤其是 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 支付,省心。
以上是我踩坑后的总结,希望对你有帮助。有更优的实现方式欢迎评论区交流。

暂无评论