之前公司做的app的订单退款是,客户打电话给公司客服退款。这个一旦用订单量大了就是一件很要命的事情,工作量大,效率低且用户体验不好。
题外话
这里不是黑ofo。之前本着体验一下ofo的小黄车,就注册了一个小黄车账号,充值了20送了5快。后来发现我上下班每天经过的路段就很好少有小黄车,有也是被个人占用了。一怒之下就退了押金,这个退款到还是很快。后来退账户余额就是一件很恼火的事情。打ofo的400电话,不是没人接就是接了之后就断线或者有人跟进了也说了要退款还是没退下来。前后搞了一个月之久。最后退款只能退账户自己充值的部分,而且之前消费都是消费的自己充值的部分,他送的钱还没消费。比如我充值20,送5块,起了几次车消费了2块,最后退款只退你18,你说坑不坑,也没哪里说消费规格,退款规则,这钱送给我了应该就是我的了。现在即使周边车,很多了,我依然不会再选择小黄车了。似乎很黑了。
应用场景
当交易发生之后一段时间内,由于买家或者卖家的原因需要退款时,卖家可以通过退款接口将支付款退还给买家,微信支付将在收到退款请求并且验证成功之后,按照退款规则将支付款按原路退到买家帐号上。
注意:
1、交易时间超过一年的订单无法提交退款;
2、微信支付退款支持单笔交易分多次退款,多次退款需要提交原支付订单的商户订单号和设置不同的退款单号。一笔退款失败后重新提交,要采用原来的退款单号。总退款金额不能超过用户实际支付金额。
接口说明
这里退款还是根据商户订单号-out_trade_no去微信那边做申请退款。
代码实现
这个是需要证书了,
。
证书的使用。
我的后端使用的node.js,后端网路请求使用的是request,这里需要看看这部分Using options.agentOptions。
/*
* 根据商户订单号out_trade_no 申请退款
*/
router.all('/api/wxpay/refundOrder', function(req, res, next) {
var param = req.query || req.params;
var out_trade_no = param.out_trade_no;
var nonce_str = getNonceStr();
var refundFee = '1'; // 退款金额 退款金额小于等于订单金额
var totalFee = '1'; // 订单总金额
var bodyData = '<xml>';
bodyData += '<appid>' + wxConfig.AppID + '</appid>';
bodyData += '<mch_id>' + wxConfig.Mch_id + '</mch_id>';
bodyData += '<nonce_str>' + nonce_str + '</nonce_str>';
bodyData += '<op_user_id>' + wxConfig.Mch_id + '</op_user_id>';
bodyData += '<out_refund_no>' + nonce_str + '</out_refund_no>';
bodyData += '<out_trade_no>' + out_trade_no + '</out_trade_no>';
bodyData += '<refund_fee>' + refundFee + '</refund_fee>';
bodyData += '<total_fee>' + totalFee + '</total_fee>';
var sign = refundOrderSign(
wxConfig.AppID,
wxConfig.Mch_id,
nonce_str,
wxConfig.Mch_id,
nonce_str, // 商户退款单号 给一个随机字符串即可out_refund_no
out_trade_no,
refundFee,
totalFee
);
bodyData += '<sign>' + sign + '</sign>';
bodyData += '</xml>';
var agentOptions = {
pfx: fs.readFileSync('./wx_pay/apiclient_cert.p12'),
passphrase: wxConfig.Mch_id,
};
var urlStr = 'https://api.mch.weixin.qq.com/secapi/pay/refund';
request({
url: urlStr,
method: 'POST',
body: bodyData,
agentOptions: agentOptions
}, function (error, response, body) {
if (!error && response.statusCode == 200) {
var returnValue = {};
parseString(body, function (err, result) {
if (result.xml.return_code[0] == 'SUCCESS' && result.xml.result_code[0] == 'SUCCESS') {
returnValue.msg = '操作成功';
returnValue.status = '100';
returnValue.result = result;
res.end(JSON.stringify(returnValue));
} else{
returnValue.msg = result.xml.return_msg[0];
returnValue.status = '102';
returnValue.result = result;
res.end(JSON.stringify(returnValue));
}
});
}else{
res.end(JSON.stringify({
msg: "请求失败",
status: "103"
}));
}
})
});
// 订单退款 签名算法
function refundOrderSign(appid,mch_id,nonce_str,op_user_id,out_refund_no,out_trade_no,refund_fee,total_fee) {
var ret = {
appid: appid,
mch_id: mch_id,
nonce_str: nonce_str,
op_user_id: op_user_id,
out_refund_no: out_refund_no,
out_trade_no: out_trade_no,
refund_fee: refund_fee,
total_fee: total_fee
};
var str = raw(ret);
str = str + '&key='+key;
var md5Str = cryptoMO.createHash('md5').update(str).digest('hex');
md5Str = md5Str.toUpperCase();
return md5Str;
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
总结
这里的难点是证书的使用,但是如果你看了request,这里需要看看这部分Using options.agentOptions,就不难了。