H5 页面调起微信支付总结

最近的工作是一个微信公众号的开发项目。有个小需求是在公众号内的H5页面调起微信支付,这里简单概括一下开发流程。详细的步骤可以直接查看微信支付官方开发文档

前期准备

在开发之前需要准备一个拥有支付权限的微信公众号。下文以普通的商户服务号为例。服务号开通微信支付权限之后,会发邮件告知以下信息:

* 微信支付商户号(MCH_ID)
* 微信公众账号ID(APP_ID)
* 微信支付API秘钥(MCK_KEY)
* 微信公众账号Secret(APP_SECRET)

保存好这些信息,会在之后的接口请求中使用。

业务流程

公众号内支付主要有以下业务流程:

获取微信用户openid

使用 APP_ID 和 APP_SECRET 获取微信用户openid,该 ID 仅在当前公众号有效。跟 APP_ID 绑定。

前端向后端发起支付请求

例如用户点击了支付按钮,前端参数请求 GET wx/config (自拟)接口以获取需要在客户端调起 JSAPI 的参数。

后端收到支付请求后访问微信统一下单接口

统一下单支付接口

参考上面的文档传入一下参数:

* appid 公众账号ID
* mac_id 商户号
* nonce_str 随机字符串,用来给签名加盐
* sign 签名
* body 商品描述,用以支付成功后微信给用户返回支付结果
* out_trade_no 随机字符串,作为微信订单流水号
* total_fee 支付总价,注意单位为分
* spbill_create_ip 用户终端IP
* notify_url 微信回调地址,用以接收微信支付结果
* trade_type 交易类型,公众号内H5页面为 JSAPI
* openid 用户唯一标示

一般情况下这里容易出现以下错误:

* 签名错误,一定要注意参数名的大小写,务必与文档一致
* openid 与 appid \ mch_id 不符
* total_fee 用了浮点数。该参数单位为分,不可使用小数

请求成功后,微信会返回成功信息,我们主要用到里面的prepay\_id字段。

这里我们可以在业务数据库中插入一条交易信息,以管理这些微信支付的订单。具体的表字段可以看自己的需求设计。

后端准备前端调用 JSAPI 所需的参数

前端调起微信支付 JSAPI 所需的参数如下:

raw = dict(
appId=self.app_id,
timeStamp=timestamp,
nonceStr=nonce_str,
package=package,
signType="MD5")
# 签名
sign = self.sign(raw)

签名时注意大小写。

前端调起微信支付

前端使用上一步传入的参数使用 JSAPI 调起微信支付,直接请求微信支付接口,微信会返回给前端以下情况:

* get_brand_wcpay_request:ok 支付成功
* get_brand_wcpay_request:cancel 支付取消
* get_brand_wcpay_request:fail 支付失败
* 调用支付JSAPI缺少参数:total_fee

前端需要针对不同的返回做不同的处理,尽量不要直接将错误反馈给用户。

[异步] 微信返回支付结果给后端

通过在统一下单接口中的notify_url回调地址,微信会发送支付的结果给这个接口。后端对应的接口就需要根据微信返回的结果处理自身的业务数据,比如我们是生成某种会员卡,然后完善数据库之前下单时生成的交易订单信息。

处理完这些后,需要给微信反馈你处理的结果。具体格式参见微信支付结果通知文档

这里如果没有正确发送给微信相应的通知,微信会重复发送请求,导致业务数据的混乱。

注意事项

有一个非常容易出错的地方一定要注意:

* 签名的算法需要再三验证准确性
* 签名所需的参数名一定要和微信所需要的参数名大小写一致,否则会导致签名验证失败
* 支付金额参数的单位是分
* 处理回调时需要考虑微信重复发送支付结果的情况(主要是业务数据的去重)