h5微信支付
h5的微信支付一般分为2种情况:
- 在微信中直接用链接打开或者从公众号打开h5页面(微信内支付)。
- 在手机浏览器中打开h5进行微信支付(微信外支付)
一、微信内支付(JSAPI支付)
参考链接:https://www.cnblogs.com/ganws/p/11139149.html
微信支付文档链接:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_3
1.商户号配置
请确保实际支付时的请求目录与后台配置的目录一致(现在已经支持配置根目录,配置后有一定的生效时间,一般5分钟内生效),否则将无法成功唤起微信支付。
1.设置支付目录:
在微信商户平台(pay.weixin.qq.com),设置您的JSAPI支付支付目录,设置路径:商户平台–>产品中心–>开发配置,如图7.7所示。JSAPI支付在请求支付的时候会校验请求来源是否有在商户平台做了配置,所以必须确保支付目录已经正确的被配置,否则将验证失败,请求支付不成功。配置如下图:
![](https://img-blog.csdnimg.cn/2019112915184314.jpg)
2.设置授权域名:
开发JSAPI时,在统一下单接口中要求必传用户openid
,而获取openid
则需要您在公众平台设置获取openid
的域名,只有被设置过的域名才是一个有效的获取openid
的域名,否则将获取失败。具体界面:
![](https://img-blog.csdnimg.cn/20191129151932500.jpg)
![](https://img-blog.csdnimg.cn/20191129151959650.jpg)
网页授权域名是用来向微信获取code
3.微信网页授权
openid是微信用户在公众号appid下的唯一用户标识(appid不同,则获取到的openid就不同),可用于永久标记一个用户,同时也是微信JSAPI支付的必传参数。openid
的获取需要先获取用户授权后的code
,将code
传给服务端,以换取prepay_id
(ps:订单id。后面会用到)。
openid
获取文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_4
如果用户在微信客户端中访问第三方网页,公众号可以通过微信网页授权机制,来获取用户基本信息,进而实现业务逻辑
关于网页授权的两种scope的区别说明
3.1、以snsapi_base为scope发起的网页授权,是用来获取进入页面的用户的openid的,并且是静默授权并自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)
3.2、以snsapi_userinfo为scope发起的网页授权,是用来获取用户的基本信息的。但这种授权需要用户手动同意,并且由于用户同意过,所以无须关注,就可在授权后获取该用户的基本信息。
3.3、用户管理类接口中的“获取用户基本信息接口”,是在用户和公众号产生消息交互或关注后事件推送后,才能根据用户OpenID来获取用户基本信息。这个接口,包括其他微信接口,都是需要该用户(即openid)关注了公众号后,才能调用成功的。
由于jsapi
支付只需要获取code调用后台接口换取openid
,并不需要获取昵称用户信息,所以这里用第一种方式即可。
第一步:用户同意授权,获取code
// APPID:需将 APPID 换成你使用的微信公众号的 appid 。
// REDIRECT_URI换成用户授权回调成功的页面地址,一般来讲都是当前支付页面的地址,需要对地址进行encode处理(PS:处理方法:encodeURIComponent(link))
// STATE可以向回调页面传参,但是只能传一个字符串,如:state=123。若无参数,默认STATE就可以。
// 这个链接自己拼接的其他参数不会被带到回调页面
// 若提示“该链接无法访问”,请检查参数是否填写错误,是否拥有scope参数对应的授权作用域权限
https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect
用户同意授权后
如果用户同意授权,页面将跳转至 redirect_uri/?code=CODE&state=STATE。
注意:由于授权操作安全等级较高,所以在发起授权请求时,微信会对授权链接做正则强匹配校验,如果链接的参数顺序不对,授权页面将无法正常访问
注意:跳转回调redirect_uri,应当使用https链接来确保授权code的安全性。
code说明 : code作为换取access_token的票据,每次用户授权带上的code将不一样,code只能使用一次,5分钟未被使用自动过期。
4.在微信浏览器内窒息感打开的h5的支付,接口在输入输出的数据格式为JSON。
注意:WeixinJSBridge内置对象在其他浏览器中无效。
拿到openid
之后,可以进行支付了,
支付参数生成文档:https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6
// 检测当前环境是否支持 WeixinJSBridge,也可以理解为判断当前环境是否微信
// 这部分也可以直接替换成判断是否是微信环境,在微信内才能进行支付
// 如果 WeixinJSBridge 判断有问题,可以试试,调用 window.WeixinJSBridge 去判断
if (typeof WeixinJSBridge == "undefined"){
if( document.addEventListener ){
document.addEventListener('WeixinJSBridgeReady', onBridgeReady, false);
}else if (document.attachEvent){
document.attachEvent('WeixinJSBridgeReady', onBridgeReady);
document.attachEvent('onWeixinJSBridgeReady', onBridgeReady);
}
}else{
onBridgeReady();
}
// 客户端依据支付文档生成参数
function onBridgeReady(){
WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId":"wx2421b1c4370ec43b", //公众号名称,由商户传入
"timeStamp":"1395712654", //时间戳,自1970年以来的秒数
"nonceStr":"e61463f8efa94090b1f366cccfbbb444", //随机串
"package":"prepay_id=u802345jgfjsdfgsdg888",
"signType":"MD5", //微信签名方式:
"paySign":"70EA570631E4BB79628FBCA90534C63FF7FADD89" //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ){
// 使用以上方式判断前端返回,微信团队郑重提示:
// res.err_msg将在用户支付成功后返回ok,但并不保证它绝对可靠。
// 支付成功的回调
}else if (res.err_msg === "get_brand_wcpay_request:cancel") {
Toast.info('用户取消支付')
// 用户取消支付回调
} else if (res.err_msg === "get_brand_wcpay_request:fail") {
// 支付失败回调
}
}
);
}
下面是具体的逻辑
// 页面初始
var wx = null;
isWeixin();
// 判断是否是微信环境,代替文档中的 typeof WeixinJSBridge == "undefined" 这部分判断
function isWeixin(){
// wx为true 则为微信环境,为false则微信外环境
wx = (function(){
return navigator.userAgent.toLowerCase().indexOf('micromessenger') !== -1
})();
}
// 点击立即支付执行
if(wx){
// 获取回调code,code是回调页面的地址参数
let code = urlParam('code');
if(!code){
// code的有效期只有5分钟,所以这里我在点击立即支付的时候拉取用户授权
window.location.href = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=snsapi_base&state=STATE#wechat_redirect'
}else {
// 拿code换取openid
getOpenid(code);
}
}
// 拉取支付
function getOpenid(code){
...
let openid = null;
// 用code换取openid
axios.post()
.then((res) => {
openid = res.openid;
})
...
// 拿到openid之后,就可以拉起支付了
let appid = "wx2421b1c4370ec43b";
let secret = SECRET; // 微信公众号秘钥,生成签名需要
let timestamp = String(new Date().getTime() / 1000);
let nonceStr = Math.random().toString(36).substr(2, 10);
let prepay_id = openid;
let paySign = '';
var string = 'appId=' + appid + '&nonceStr=' + nonceStr + '&package=prepay_id=' + prepay_id + '&signType=MD5&timeStamp=' + timestamp + '&key=' + secret;
paySign = (MD5(string).toString()).toUpperCase();
window.WeixinJSBridge.invoke(
'getBrandWCPayRequest', {
"appId": appid, //公众号名称,由商户传入
"timeStamp": timestamp, //时间戳,自1970年以来的秒数
"nonceStr": nonceStr, //随机串
"package":"prepay_id=" + prepay_id,
"signType":"MD5", //微信签名方式
"paySign": paySign //微信签名
},
function(res){
if(res.err_msg == "get_brand_wcpay_request:ok" ){
// 支付成功回调
} else if (res.err_msg === "get_brand_wcpay_request:cancel") {
// 用户取消支付
} else if (res.err_msg === "get_brand_wcpay_request:fail") {
// 支付失败
}
}
);
}
Tip
1. 微信内支付 getBrandWCPayRequest
参数生成方式,timestamp,nonceStr, paySign 的计算方式可以直接用。
2.config:invalid signature
,问题,检查页面的域名有没有添加到微信公众号ip白名单,根据文档检查签名生成。
3. code
,如果页面在本地打开,用户授权回调的code,复制出来的链接是看不到的(ps: 但不影响页面接着执行)。将项目放在线上的测试环境就可以。
二、微信外支付(ps: h5支付)
参考链接:https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_1
同JSAPI支付一样: 需要先申请商户号,并在商户中设置h5支付的权限。
在公众号将页面用的IP添加到域名白名单中。
通过后台返回mweb_url
,拉起微信支付收银台第三方页面。可通过访问该url来拉起微信客户端,完成支付,mweb_url的有效期为5分钟。
参考链接: https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=9_20&index=1
// data 为拉起支付
axios.post(url, data)
.then(function(res){
let jsonobj = JSON.parse(utils.DecryptEcb(res.data));// Decrypt
console.log(jsonobj)
if(jsonobj.success === 1){
// jsonobj.data.mweb_url 则是返回给我门用来拉起微信支付的链接。
// redirect_url 是微信支付完成后的指定回调页面。由于H5外部支付完成无法返回付款是否完成的状态(ps:这一点文档也有说明)。所以文档给了一个折中的方法,指定回调页面,在回调页面询问用户是否完成付款,由用户自己操作。
window.location.href = jsonobj.data.mweb_url + '&redirect_url=REDIRECT_URL?' + order= + _this.state.orderInfo.order_num;
}
})
传送门: https://pay.weixin.qq.com/wiki/doc/api/H5.php?chapter=15_4
![](https://i-blog.csdnimg.cn/blog_migrate/9fc9cdf5effff8632c9c484ac07219fc.jpeg)
end 😈