一、背景
公众号支付作为支付下单的一种交易方式,前端要拉起微信支付,需要哪些数据呢?
当我们对接浦发银行支付的时候,无非是它先去调用微信官方,再转发给我们。
所以它需要指定公众号及其下的openId,返回值就是拉起微信支付的元数据。
示例见下:
"timeStamp": "1713423464",
"packageValue": "prepay_id=wx1814574450566680ed3922b7cc95780000",
"paySign": "Z8kAEVJMVUAoURru3n+EF8awKKXI0O38UZCjYybrU7ZsxapLjGPSWCIm+l6xUcgJrBAuDcttle620iXwfpDV7U+CK4gXYeBrUJjdZUGOE3HIDztgfiF/wgD/ToXCMtunh+37+k41FdfswOZTYbApEEZHK6VntQb3pewWRckJGc9T6wWpQun7xT5Pu85YmuZeOXi5xDMpfPW83HrIF6gaIiUfOuKNq92ojebIGEQ+MDzHXbOof0kbfm+LNtI3ceuXcTkSTkSlpomr8to/doANGGXPdSgRZP4r2YHI4DRaOCdLU+89FHh/nyCpoqM/sDSDPB+7LT2mTDbkem1/XiDexg==",
"appId": "wxd9a9691ecfa23abc",
"signType": "RSA",
"nonceStr": "48c995292ab5452d9b8f09a956221ba8"
本文是调用浦发银行的支付下单接口,无论是请求参数还是返回参数,由于太多了,有点眼花缭乱。 其实我们需要的就那么几个字段。
梳理本文的另一个原因是:浦发银行支付接口的命名实在是不吐不快,不复制粘贴,都要让你怀疑自己学的英语是错的了。
二、支付接口
和扫码付是同一个接口,交易类型不同,且需要openId和appid。
- 接口名称:对公收款支付
- 接口URI:/api/corporateAccounts/payments/orders
- 请求方式:POST
请求报文
公众号支付的几个必填字段,比起扫码付,它需要多传两个字段subMechNoAccctID和usrChildFlg
- subMechNoAcctID:公众号appid
- usrChildFlg:微信用户在公众号下的标识openId(用户子标识?对应的英文不应该是userChildFlag吗?缺e又缺a的~~ 别手敲,复制靠谱些)
- terminalNo:终端号,在申请商户的时候,由浦发银行分配给我们
- cmdtyDsc:商品描述
- tranAmt:交易金额,单位是元
- iPAdress:这个最容易踩坑,地址的英文明明是address,它偏偏要少写一个d;其次该字段在Lombok自动生成getter/setter的时候,会变成“IPAdress”。
- mrchlInfmAdr:支付回调的异步通知地址
- tranType:选择“OK”
- mrchId:商户号,在联调接口前,先在浦发后台申请好商户(注意:不是浦发开放平台,这也是我想不明白的地方:既然有开放平台,怎么不把商户后台相关功能一并开放给商户呢?这一点还是微信商户后台做得好,真的是让银行望尘莫及了)
- mrchOrdrNo:平台支付流水号,这个字段命名也是醉了(好端端的订单编号OrderNo,被他给取成了OrdrNo,每次都生怕写错了)
- mrchTm:商户的交易时间,格式yyyyMMddHHmmss
响应报文
- tranOrdrNo:浦发银行支付流水号,需要保存至支付订单
- trandDate:交易日期,本来没什么用,查询支付结果接口中需要用到它,且必传参数。
- signature:签名数据,对应微信支付的元数据paySign。
- sgnData:微信支付的其他元数据,包括appId、nonceStr、package、signType和timeStamp,只是你需要从本字段再次解析得到。示例:appId=wxd9a9691ecfa23abc&nonceStr=48c995292ab5452d9b8f09a956221ba8&package=prepay_id=wx1814574450566680ed3922b7cc95780000&signType=RSA&timeStamp=1713423464 (有两点需要注意:package的内容是prepay_id=xxx,并且它在java语言中是一个关键字)
示例报文
- 请求报文
{
"terminalNo": "98A00162",
"mrchOrdrNo": "B11240418145729019018",
"subMechNoAcctID": "wxd9a9691ecfa23abc",
"cmdtyDsc": "测试商品",
"tranType": "OA",
"mrchId": "310319982990001",
"iPAdress": "127.0.0.1",
"mrchTm": "20240418145729",
"mrchlInfmAdr": "http://122.xx.xx.xxx:6008/pay/api/v1/xxx/xxx",
"tranAmt": "0.01",
"usrChildFlg": "oX0xrsxe8csXcJ_zieYjk5qrAHu8"
}
- 响应报文
关键的返回信息:
{“tranOrdrNo”: “1901041814563411151652067245”,
“tranDate”: “20240418”,
“signature”: “Z8kAEVJMVUAoURru3n+EF8awKKXI0O38UZCjYybrU7ZsxapLjGPSWCIm+l6xUcgJrBAuDcttle620iXwfpDV7U+CK4gXYeBrUJjdZUGOE3HIDztgfiF/wgD/ToXCMtunh+37+k41FdfswOZTYbApEEZHK6VntQb3pewWRckJGc9T6wWpQun7xT5Pu85YmuZeOXi5xDMpfPW83HrIF6gaIiUfOuKNq92ojebIGEQ+MDzHXbOof0kbfm+LNtI3ceuXcTkSTkSlpomr8to/doANGGXPdSgRZP4r2YHI4DRaOCdLU+89FHh/nyCpoqM/sDSDPB+7LT2mTDbkem1/XiDexg==”,
“sgnData”: “appId=wxd9a9691ecfa23abc&nonceStr=48c995292ab5452d9b8f09a956221ba8&package=prepay_id=wx1814574450566680ed3922b7cc95780000&signType=RSA&timeStamp=1713423464”}
- 详细的响应报文:
{
"statusCode": "0000",
"transNo": "04972404186851457296786904",
"isSbscrbFlg": "",
"isFlag": "",
"pyBnkInfo": "",
"totalAmt": "",
"clueDtl": "",
"byrOfAlipayAcctNO": "",
"actRcvAmt": "",
"byrPayAmt": "",
"usPntsPayAmt": "",
"toUsEstbInvAmt": "",
"inrChlCnlNo": "",
"aLPAYCdBal": "",
"mrchStrNm": "xxx公司",
"userId": "",
"addItInNal": "",
"fldData": "",
"clrgDate": "",
"ordrSt": "09",
"tranType": "OA",
"tranAmt": "0.01",
"thdPtySeq": "",
"tranOrdrNo": "1901041814563411151652067245",
"tranDate": "20240418",
"qRCdLink": "",
"praPayCmmFlg": "",
"mrchId": "310319982990001",
"mrchTm": "20240418145634",
"signature": "Z8kAEVJMVUAoURru3n+EF8awKKXI0O38UZCjYybrU7ZsxapLjGPSWCIm+l6xUcgJrBAuDcttle620iXwfpDV7U+CK4gXYeBrUJjdZUGOE3HIDztgfiF/wgD/ToXCMtunh+37+k41FdfswOZTYbApEEZHK6VntQb3pewWRckJGc9T6wWpQun7xT5Pu85YmuZeOXi5xDMpfPW83HrIF6gaIiUfOuKNq92ojebIGEQ+MDzHXbOof0kbfm+LNtI3ceuXcTkSTkSlpomr8to/doANGGXPdSgRZP4r2YHI4DRaOCdLU+89FHh/nyCpoqM/sDSDPB+7LT2mTDbkem1/XiDexg==",
"sgnData": "appId=wxd9a9691ecfa23abc&nonceStr=48c995292ab5452d9b8f09a956221ba8&package=prepay_id=wx1814574450566680ed3922b7cc95780000&signType=RSA&timeStamp=1713423464",
"aLTrnCrc": "",
"pymtMd": "",
"busInfo": "",
"dscntAmnt": "",
"mdsctAmnt": "",
"byUserType": "",
"apndPyMd": "",
"pblcAcctId": "",
"trdMerMechNo": "",
"weChatSubMechNo": "",
"bussRetInfo": "SUCCESS",
"errCode": "",
"errInfo": "",
"trdChnl": "",
"bnkngbsnssSeqNo": "",
"rsrvFld1": "",
"rsrvFld2": "",
"rsrvFld3": "",
"rsrvFld4": "",
"rsrvFld5": "",
"rsrvFld6": "",
"remark": ""
}
解析签名数据sgnData
注意:字段名是sgnData,签名的英文本来是sign,那么签名数据应该是signData才对,可浦发行命名的时候偏不,硬是要把i去掉,想不明白~
public static Map<String, String> splitPayData(String data) {
Map<String, String> paramMap = new HashMap<>();
if (StringUtils.isBlank(data)) {
return paramMap;
}
// 分割字符串为键值对数组
String[] pairs = data.split("&");
for (String pair : pairs) {
if (!pair.contains("=")) {
continue;
}
String key = pair.substring(0, pair.indexOf("="));
String value = pair.substring(pair.indexOf("=") + 1);
paramMap.put(key, value);
}
return paramMap;
}
三、遇到的问题
如果你是小程序开发,和公众号jsapi支付是相同的请求,但是不存在jsapi授权之说,本文提及的问题大概率也都不存在。。。
1、商户传入的appid参数不正确,请联系商户处理
问题原因:下单支付接口的入参subMechNoAcctID,误写成了subMechNoAccctID (多写了一个c)。对于浦发银行来说,也就是没有传入公众号appId,但接口并不会报错"subMechNoAcctID必填"。接口返回了一个默认的公众号appid及其元数据,我还纳闷了,响应报文中的appid和请求报文的居然不是同一个。
建议:浦发银行能够根据不同的交易类型,对必填字段进行限制,而不是像现在这样不报错,却返回错误的数据。
2、当前页面的URL未注册
在微信商户后台进行添加,【产品中心】–》【开发配置】–》【支付授权目录设置】,添加前端应用的h5页面。
3、下单账号与支付账号不一致,请核实后再支付
解决办法:浦发银行的测试环境要求我们必须把微信用户openId加到白名单。
也就说,我们必须先找到微信公众号appId及其下的openId。