微信⼩程序⽀付及退款整体流程
最近做了微信⽀付及退款⼀系列操作,微信⽂档写的也⽐较简略,⽹上博客也并不详细,也踩了⼀些坑,在这⾥记录下。当然主要还是得根
据微信⼩程序⽂档⼀步⼀步来。
⼀、tPayment
发起微信⽀付。了解更多信息,请查看
所谓的发起微信⽀付,指的是⽤户侧这边唤起微信⽀付窗⼝的api,这个api需要按规范传参数
tPayment({
timeStamp:'',
nonceStr:'',
package:'',
signType:'MD5',
paySign:'',
success(res){},
fail(res){}
})
这些参数均需要从后台获取。那么我们进⼊“微信⽀付接⼝⽂档”查看是怎么个流程
⼆、微信⽀付具体流程
⽂档也写的很清楚,不细说,主要看下⾯这个流程
商户系统和微信⽀付系统主要交互:
1、⼩程序内调⽤登录接⼝,获取到⽤户的openid,api参见公共api【】
2、商户rver调⽤⽀付统⼀下单,api参见公共api【】
3、商户rver调⽤再次签名,api参见公共api【】
4、商户rver接收⽀付通知,api参见公共api【】
5、商户rver查询⽀付结果,api参见公共api【】
1、调⽤获取code,然后通过code,调取微信三⽅接⼝,获取openid。如果⽤户系统有openid记录,可以省略这步操作。
主要是因为下⾯的统⼀下单api⾥的参数配置:
openid参数:trade_type=JSAPI,此参数必传,⽤户在商户appid下的唯⼀标识。openid如何获取,可参考【】。
2、统⼀下单api、⼆次签名api返回参数
看⽂档⾥的参数,传那些参数,调⽤微信三⽅接⼝即可。⼀般不会有啥问题,主要问题也会在于2次签名。
实例代码如下
//统⼀下单
letunifiedorder=async(params={},ctx)=>{
letbody='......'//商品描述
letnotify_url='https://....../wxPayBack'//⽀付成功的回调地址可访问不带参数
letnonce_str=ceStr()//随机数
letout_trade_no=ode//商户订单号(⽤户系统⾃定义的商户订单号)
lettotal_fee=ay*100//订单价格单位是分
letbodyData='
bodyData+=`
bodyData+=`
bodyData+=`
${body}`//商品描述bodyData+=`
bodyData+=`
bodyData+=`
bodyData+=`
bodyData+=`
bodyData+=`
bodyData+='
//签名(根据上⾯这些参数,有个签名算法,⽂档⾥也有描述)
varsign=njsapi(
,
body,
_id,
nonce_str,
notify_url,
,
out_trade_no,
,
total_fee
);
bodyData+='
bodyData+=''
//微信⼩程序统⼀下单接⼝
varurlStr='/pay/unifiedorder'
letoption={
method:'POST',
uri:urlStr,
body:bodyData
}
letresult=awaitrp(option)
letreturnValue={}
parString(result,function(err,result){
if(_code[0]=='SUCCESS'){
_trade_no=out_trade_no;//商户订单号
//⼩程序客户端⽀付需要nonceStr,timestamp,package,paySign这四个参数
tr=_str[0];//随机字符串
amp=(newDate().getTime()/1000)+'';
e='prepay_id='+_id[0];//统⼀下单接⼝返回的prepay_id参数值
n=njs(
,
tr,
e,
'MD5',
amp
)//签名
//emitToSocket(total_fee)
={
success:true,
msg:'操作成功',
data:returnValue
}
}el{
=_msg[0]
={
success:fal,
msg:'操作失败',
data:returnValue
}
}
})
}
写的⼀个微信⽀付的配置项
constcryptoMO=require('crypto')//MD5算法
/*微信参数AppID和Secret*/
constwxConfig={
AppID:"......",//⼩程序ID
Secret:"......",//⼩程序Secret
Mch_id:"......",//商户号
Mch_key:"......",//商户key
//⽣成商户订单号
getWxPayOrdrID:function(){
letmyDate=newDate();
letyear=lYear();
letmouth=th()+1;
letday=e();
lethour=rs();
letminute=utes();
letcond=onds();
letmcond=liconds();//获取当前毫秒数(0-999)
if(mouth<10){/*⽉份⼩于10就在前⾯加个0*/
mouth=String(String(0)+String(mouth));
}
if(day<10){/*⽇期⼩于10就在前⾯加个0*/
day=String(String(0)+String(day));
}
if(hour<10){/*时⼩于10就在前⾯加个0*/
hour=String(String(0)+String(hour));
}
if(minute<10){/*分⼩于10就在前⾯加个0*/
minute=String(String(0)+String(minute));
}
if(cond<10){/*秒⼩于10就在前⾯加个0*/
cond=String(String(0)+String(cond));
}
if(mcond<10){
mcond=String(String('00')+String(cond));
}elif(mcond>=10&&mcond<100){
mcond=String(String(0)+String(cond));
}
letcurrentDate=String(year)+String(mouth)+String(day)+String(hour)+String(minute)+String(cond)+String(mcond);
returncurrentDate
},
//获取随机字符串
getNonceStr(){
().toString(36).substr(2,15)
},
//统⼀下单签名
paysignjsapi(appid,body,mch_id,nonce_str,notify_url,openid,out_trade_no,spbill_create_ip,total_fee){
letret={
appid:appid,
body:body,
mch_id:mch_id,
nonce_str:nonce_str,
notify_url:notify_url,
openid:openid,
out_trade_no:out_trade_no,
spbill_create_ip:spbill_create_ip,
total_fee:total_fee,
trade_type:'JSAPI'
}
letstr=(ret,true)
str=str+'&key='+_key
letmd5Str=Hash('md5').update(str,'utf-8').digest('hex')
md5Str=rCa()
returnmd5Str
},
raw(args,lower){
letkeys=(args)
keys=()
letnewArgs={}
h(key=>{
lower?newArgs[rCa()]=args[key]:newArgs[key]=args[key]
})
letstr=''
for(letkinnewArgs){
str+='&'+k+'='+newArgs[k]
}
str=(1)
returnstr
},
//⼩程序⽀付签名
paysignjs(appid,nonceStr,packages,signType,timeStamp){
letret={
appId:appid,
nonceStr:nonceStr,
package:packages,
signType:signType,
timeStamp:timeStamp
}
letstr=(ret)
str=str+'&key='+_key
letmd5Str=Hash('md5').update(str,'utf-8').digest('hex')
md5Str=rCa()
returnmd5Str
},
//校验⽀付成功回调签名
validPayBacksign(xml){
letret={}
let_paysign=[0]
for(letkeyinxml){
if(key!=='sign'&&xml[key][0])ret[key]=xml[key][0]
}
letstr=(ret,true)
str=str+'&key='+_key
letmd5Str=Hash('md5').update(str,'utf-8').digest('hex')
md5Str=rCa()
return_paysign===md5Str
},
//确认退款签名
refundOrderSign(appid,mch_id,nonce_str,op_ur_id,out_refund_no,out_trade_no,refund_fee,total_fee){
letret={
appid:appid,
mch_id:mch_id,
nonce_str:nonce_str,
op_ur_id:op_ur_id,
out_refund_no:out_refund_no,
out_trade_no:out_trade_no,
refund_fee:refund_fee,
total_fee:total_fee
}
letstr=(ret,true)
str=str+'&key='+_key
letmd5Str=Hash('md5').update(str,'utf-8').digest('hex')
md5Str=rCa()
returnmd5Str
}
}
这个配置项⾥的就是raw⽅法得注意下,有个区分,有的签名是key值全⼩写,有的签名就是⽀付⼆次签名校验的时候,key值是要保持驼
峰,所以加了点区分。
当时在此处确实遇到了问题,查了很多博客,解决办法都模棱两可并没有效。其实,微信提供了签名校验⼯具,可以将⾃⼰的参数传⼊
看和⽣成的是否⼀致,然后就可以单步调试看是哪⾥出了问题,⽐较⽅便快捷。
从上⾯代码也可以看出流程:
根据⽂档需要传的参数——⽣成下单签名——签名与参数⼀起传⼊——调⽤微信统⼀下单api——返回下单接⼝的XML——解析
XML返回数据参数,再次⽣成签名——数据返回前台供tPayment()调⽤
⾄此微信⽀付就可以正常唤起窗⼝付款了。但是还有个重要的问题,就是下单成功通知。也就是下统⼀下单⾥传⼊的notify_url:⽀付
成功回答地址
3、⽀付成功结果通知
我们需要提供⼀个接⼝供微信⽀付成功回调:'POST/order/wxPayBack':wxPayBack,//微信⽀付成功回调
constparString=require('xml2js').parString//xml转js对象
letwxPayBack=async(ctx,next)=>{
('wxPayBack',)//我们可以打印看下微信返回的xml长啥样
parString(,function(err,result){
payBack(,ctx)
})
}
letpayBack=async(xml,ctx)=>{
if(_code[0]=='SUCCESS'){
letout_trade_no=_trade_no[0]//商户订单号
lettotal_free=_fee[0]//付款总价
('订单:',out_trade_no,'价格:',total_free)
if(ayBacksign(xml)){
letout_order=({
where:{
orderCode:out_trade_no
}
})
if(out_order&&(out_ay*100)-total_free===0&&out_tate===1){
({orderState:2},{
where:{
orderCode:out_trade_no
}
})
//emitToSocket(total_fee)
=`
}
}
}
=`
}
ayBacksign(xml),这⾥⼀定要校验下⽀付成功的回调签名。校验规则就是微信返回的xml⾥除了sign不放⼊参数校验外,
其他的均要拿出key-value值进⾏⽣产md5加密,然后与微信返回的sign值⽐对即可。
校验成功之后,修改订单表对应数据的状态即可。
三、申请退款和确认退款
申请退款其实没什么说的,就是⽤户侧申请退款,然后更改⽤户侧订单的状态,主要说⼀下商家确认退款给买家的流程。
特别需要注意的是:请求需要双向证书。详见
实例代码:
//确认退款
letconfirmRefund=async(ctx,next)=>{
let_body=
letout_trade_no=_ode//商户订单号
letnonce_str=ceStr()
lettotal_fee=_ay*100//订单价格单位是分
letrefund_fee=_ay*100
letbodyData='
bodyData+='
bodyData+='
bodyData+='
bodyData+='
bodyData+='
bodyData+='
bodyData+='
bodyData+='
//签名
letsign=OrderSign(
,
_id,
nonce_str,
_id,
nonce_str,//商户退款单号给⼀个随机字符串即可out_refund_no
out_trade_no,
refund_fee,
total_fee
)
bodyData+='
bodyData+=''
letagentOptions={
pfx:leSync((__dirname,'/wx_pay/apiclient_cert.p12')),
passphra:_id,
}
//微信⼩程序退款接⼝
leturlStr='/capi/pay/refund'
letoption={
method:'POST',
uri:urlStr,
body:bodyData,
agentOptions:agentOptions
}
letresult=awaitrp(option)
parString(result,function(err,result){
if(_code[0]=='SUCCESS'){
refundBack(_)
={
success:true,
msg:'操作成功'
}
}el{
={
success:fal,
msg:_code_des[0]
}
}
})
}
letrefundBack=async(orderId)=>{
({orderState:8},{
where:{id:orderId}
})
letorderfoods=l({
where:{orderId:orderId}
})
h(food=>{
dealFood(food,'plus')
})
}
可以看到:随机字符串nonce_str,商户退款单号out_refund_no,我们⽤的是同⼀个随机串。
然后经过校验之后,获取证书内容及商户号,作为参数传给微信提供的申请退款接⼝接⼝。返回退款成功之后,做⾃⼰⽤户侧的相关
业务处理即可。
本文发布于:2023-03-12 15:19:27,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/1678605568227118.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:退款流程.doc
本文 PDF 下载地址:退款流程.pdf
留言与评论(共有 0 条评论) |