公司项目需要,需要接入第三方支付,前前后后也搞了很久。虽然微信公众平台文档已经写得很清楚了,但是还是记录一下整个接入的流程。
openid
openid
是微信用户在公众号appid
下的唯一用户标识(appid
不同,则获取到的openid
就不同,所以不同的公众号下有不同的openid
,),可用于永久标记一个用户,同时也是微信公众号支付的必传参数。
code
微信支付,需要用户授权,获取code,通过code
获取网页授权,其实是要取得openid
,需要注意的是,code
只能使用一次,用户每次授权带上的code
都是不一样的,并且五分钟内不使用的话会自动过期。
场景描述
我们在进行支付时,点击支付按钮,首先会跳转了页面然后再跳转回来,然后弹出框让我们输入密码,支付成功或者支付失败。或者, 没有跳转页面,直接就弹出密码输入框
逻辑描述
上面也说了,我们获取code
就是为了获取到openid
然后利用openid
这个必传的参数才能成功唤起支付。
其实呢,让用户每次跳转URL去获取code
,然后传code
参数给后台也是可以的。因为后台的逻辑也是获取了code
之后再利用code
去获取openid
,然后进行进一步的操作。现在只是保存openid
这一操作,从后台放到了前端。 只要我们前端判断openid
存在,我们就不必再跳转URL
进行授权重新获取code
,可以直接进行支付了。
所以可以得出下图:
具体代码
逻辑图写的虽然并不具体,但是很简要。
接下来我们就走一遍流程
1.获取code
点击支付,第一次支付肯定是不存在openid
的,那么就要跳转URL
获取code
官方也已经写得很明白 微信支付网页授权
我们需要拼接此链接:https://open.weixin.qq.com/connect/oauth2/authorize?appid=APPID&redirect_uri=REDIRECT_URI&response_type=code&scope=SCOPE&state=STATE#wechat_redirect
其中 APPID
是商户的APPID,REDIRECT_URI
是页面跳转后跳回来的URL
,state
可以放一些我们自定义的内容,会带在REDIRECT_URI
上。
例如:我要重定向的地址是https://baidu.com/testIndex.html/?#index
那么,我要拼接的地址应该是https://open.weixin.qq.com/connect/oauth2/authorize?appi=XXXXX&redirect_uri=https://baidu.com/testIndex.html/?#index&response_type=code&scope=SCOPE&state={name:guo}#wechat_redirect
(URL
要进行编码encodeURIComponent,这里演示我就省略了)
那么,重点来了,地址根据我们拼接的地址跳转过去,实际跳转回来的地址会变成:https://baidu.com/testIndex.html?code=XXXX&state={name:guo}/?#index
(同样需要解码decodeURIComponent)
code=XXXX&state={name:guo}
这一坨东西跑到中间去了,那我们尝试把它取出来放到后面去
变成这样子https://baidu.com/testIndex.html/?#index?code=XXXX&state={name:guo}
这样一来,我们就能用$route.query
取到参数了
//跳转回来的URL修正,在地址跳转回来的时候调用进行修正
var payUrlReplace = function() {
//修正前: http://XXXX/?code=XXX&state=XXX/?#/XXX
var codeReg = /(?:[?|&]code=)([\w\d]+)&{0,1}/;
var statePayInfoReg= /(?:[?|&]*state=)({{1}.+}{1})&{0,1}/;
var url = decodeURIComponent(window.location.href)
//这里是为了确保#前有?
if (url.indexOf('?#') < 0) {
url = url.replace('#','?#');
}
var urlReg = /\?#(.+)\?+/
//判断URL挂载参数
if( codeReg.test(url) &&
statePayInfoReg.test(url) &&
url.match(statePayInfoReg).length > 0 &&
url.match(codeReg).length > 0
){
var code = url.match(codeReg)[1]
var state = url.match(statePayInfoReg)[1]
url = url.replace(codeReg, '')
url = url.replace(statePayInfoReg, '')
if (urlReg.test(url)) {
url = `${url}&payCode=${code}`
}else {
url = `${url}?payCode=${code}`
}
url = `${url}&payInfo=${state}`
//修正后变成 http://XXX/?#/XXXX?payCode=XXX&payInfo=XXX,这里我换了字段名
window.location.href = url
}
}
这样,payCode
和payInfo
都能取到了。
2.获取openid
有了code
,就可以获取openid
了,然后存在本地(这里我们的后台专门写了传code
获openid
的接口,这是异步操作)
上图就说明了我们传code给后台后,后台再去获取openid
, 后台在传给我们前端。
而我们要使用openid请求后台接口获取支付参数就要保证openid
必须存在,获取openid
是异步操作,我们必须保证先后顺序。值得注意的是,openid一旦获取保存在本地,之后支付就不再需要获取code了。
//检查是否使用了微信支付
var payIsUse = function(query,callback){
//检查这里是否携带需要支付的参数
//?payCode=XXX&payInfo=XXX
if(query && query.payCode){
var code = query.payCode
if(query.payInfo){
var payInfo = JSON.parse(query.payInfo)
}
//判断是否本地存在openID 如果存在则取消回调,由pay方法手动唤起支付
var openID = tokenServer.getOpenId();
if(openID){
return false;
}
//不存在openID用code去接口请求openID,在触发回调去支付
payModel.saveOpenid({
code:code
}).then(function(data){
if( tokenServer.setOpenId(data.data) ){
if(callback){
//将支付参数传给回调函数 具体用的时候是传给pay
callback(payInfo)
}
}
}
})
}
}
3.获取支付参数
后台接口,传参获得的。
4.唤起微信支付
https://pay.weixin.qq.com/wik...
//微信JSDK唤起支付
var WeChatJSDKpay = function(config,onSuceess,onError){
var pay = function(){
WeixinJSBridge.invoke('getBrandWCPayRequest',{
"appId":config.appId,
"timeStamp": config.timeStamp,
"nonceStr": config.nonceStr,
"package": config.package,
"signType": config.signType,
"paySign": config.paySign
},function(res){
if(res.err_msg == "get_brand_wcpay_request:ok"){
//支付成功
onSuceess()
}else{
//支付失败
onError('支付失败')
}
});
}
if(typeof WeixinJSBridge == "object" && typeof WeixinJSBridge.invoke == "function"){
pay()
}else{
if (document.addEventListener) {
document.addEventListener("WeixinJSBridgeReady", pay, false);
} else if (document.attachEvent) {
document.attachEvent("WeixinJSBridgeReady", pay);
document.attachEvent("onWeixinJSBridgeReady", pay);
}
}
}
支付封装的函数
/*
*onSuccess 支付成功的回调
*onError 支付失败的回调
*payInfo 支付参数
*/
var pay = function(onSuceess,onError,payInfo) {
var codeReg = /(?:[?|&]code=)([\w\d]+)&{0,1}/;
var statePayInfoReg= /(?:[?|&]*state=)({{1}.+}{1})&{0,1}/;
var payCodeReg = /(?:[?|&]payCode=)([\w\d]+)&{0,1}/;
var payInfoReg = /(?:[?|&]*payInfo=)({{1}.+}{1})&{0,1}/;
var url = decodeURIComponent(window.location.href)
var openId = tokenServer.getOpenId()
//openid存在
if (openId) {
if (!payInfo){
return false;
}
//整理获取微信支付参数的传参
var defaultParams = {
type: 1, //微信浏览器的标志
openId: openId
}
var _payInfo = tool.extend(defaultParams, payInfo)
(第三步骤-请求后台接口获取支付参数)
payRequest(_payInfo).then(function(data) {
modalLoadingServer.unload();
if(data.appId && data.status === 200){
//第四步骤-调起微信支付JSDK
WeChatJSDKpay(data,function(){
//微信JSDK支付成功 返回验证参数-流水号进行验证
onSuceess(data.outTradeNo)
},onError)
}else{
console.error('获取微信支付参数错误', _payInfo)
onError('获取微信支付参数错误,请稍候再试')
}
})
return false;
}
//如果已经存在
if(codeReg.test(url) && statePayInfoReg.test(url)){
window.location.reload();
return false;
}
//重复请求的话
if(payCodeReg.test(url) && payInfoReg.test(url)){
//处理下url重新请求
url = url.replace(payCodeReg,'');
url = url.replace(payInfoReg,'');
url = encodeURIComponent(url);
}else{
//跳转获取code 回调url
url = encodeURIComponent(window.location.href);
}
var statePay = {};
for(var key in payInfo){
//微信内支付不存在回调URL
if(payInfo.hasOwnProperty(key) && payInfo[key] && key !== 'returnUrl'){
statePay[key] = payInfo[key]
}
}
var statePay = encodeURIComponent(JSON.stringify(statePay));
var getCodeUrl = 'https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxd9242c6d1e249d25&redirect_uri='+url+'&response_type=code&scope=snsapi_base&state='+statePay+'&connect_redirect=1#wechat_redirect';
//获取`code`的跳转链接
window.location.href = getCodeUrl
}
以上是主要的代码逻辑,也不是特别完整能够直接使用,但是能够得到启发。主要还是要看具体的业务和接口怎么给。有问题欢迎一起交流~
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。