美文网首页
对接农业银行支付(微信和支付宝)的总结(三)

对接农业银行支付(微信和支付宝)的总结(三)

作者: 天草二十六_简村人 | 来源:发表于2021-06-16 14:53 被阅读0次

最后总结下农行支付的几个需要注意的地方:

1.退款接口

由于是同步接口,一般返回的状态都是"受理成功",而不是"交易成功"。
农行文档(6.6的第6点)清晰描述道:
"若交易成功,则商户可以取得交易结果对象的其他属性来进行后续的作业。
注意:GetKeyValue("ReturnCode")返回 0000 可能代表退款成功,也可能代表退款受理成功。
还要看GetKeyValue("ErrorMessage")返回的中文信息是“交易成功”还是“受理成功”。"

意味着用户的退款状态需要由业务方主动查询退款结果才更新,对用户而言有一定的延迟。
(钱到帐了,但是订单和支付状态未变)
而且没有第三方退款流水号iRspRef和退款时间HostDate/HostTime等值,这样意味着我们必须轮询农行方才行。

2.查询退款接口

成功的示例见下:只返回了平台退款流水号iRspRef和退款时间OrderDate/OrderTime
,其中状态的判断不是文档里描述的05--已退款。
另外,有些字段名可能前后会携带空格字符。

{
    "PayTypeID":"AliRefund",
    "OrderNo":"R0621061B0155736016065",
    "OrderDate":"2021/06/10",
    "OrderTime":"16:07:02",
    "RefundAmount":"0.01",
    "Status":"04",
    "iRspRef":"6AECEP01160423007350",
    " MerRefundAccountNo":"19015601040024091",
    " MerRefundAccountName":"浙江学海教育科技有限公司",
    "SplitAccInfoItems":[

    ]
}

最大的坑当属PayTypeID了,如果你只操作了支付宝的退款,又怎么能确定微信等退款返回的是何值。
所以我在程序里是根据contains()包含关系来判断是否为退款。
这里的不确定性,也带来了很多未知的bug。

农行文档(附录二-响应码一览表的com.abc.pay.client.ebus.QueryOrderRequest)对PayTypeID写道:

ImmediatePay:直接支付
PreAuthPay:预授权支付
DividedPay:分期支付
AgentPay:授权支付
Refund:退款
DefrayPay:付款
PreAuthed:预授权确认
PreAuthCancel:预授权取消

从初步的联调结果来看,这里的词典遗漏不少。

最后贴出我对接查询交易结果的示例:

/**
         * 返回代码.
         * 0000-代表成功
         */
        String returnCode = json.GetKeyValue(AbcBankConfig.RETURN_CODE);
        /**
         * 返回信息
         */
        String errorMessage = json.GetKeyValue(AbcBankConfig.ERROR_MESSAGE);

        if (AbcBankConfig.RC_SUCCESS.equalsIgnoreCase(returnCode)) {

            String orderDecodeStr = Base64Code.Decode64(json.GetKeyValue("Order"));
            
            JSONObject orderObject = com.alibaba.fastjson.JSON.parseObject(orderDecodeStr);

            //格式: YYYY/MM/DD
            String txDate = orderObject.getString("OrderDate").replaceAll("/", "-");
            //格式:HH:MM:SS
            String txTime = orderObject.getString("OrderTime");

            //01-未支付;02-无回应;03-微信和支付宝支付成功;04-农行清算或支付成功;05-已退款;07-授权确认成功;00-授权已取消;99-失败;
            String status = orderObject.getString("Status");

            String payTypeID = orderObject.getString("PayTypeID");

            //PayTypeID 根据交易类型判断是退款还是支付
            // AliRefund-支付宝退款
            // WeiXinRefund-微信退款
            if (payTypeID.contains(AbcBankConfig.PayType.PAY_TYPE_REFUND)) {
             //退款处理   
            } else {
            //支付处理    
            }
        }

3.查询退款结果和查询支付结果,是共用一个接口,这里的关键入参OrderNo,前者应该传平台退款流水号,后者应该传入平台支付流水号。

当回调出现故障时,我们必须调用主动查询接口来更新支付状态,所以我们需要知道第三方支付流水号,
也就是说,查询字段QueryDetail必须是1,不能填写0(不会返回第三方支付流水号)。等于0的场景是用不上的,不明白农行的设计意图何在?

4.退款接口的入参,OrderNo是平台支付流水号,NewOrderNo是平台退款流水号。

如果OrderNo传值错误,会报错说“无此账单”。

{
    "TrxResponse":{
        "ReturnCode":"2307",
        "ErrorMessage":"无此账单!",
        "TrxType":"Refund",
        "OrderNo":"006210B6101501522608N",
        "NewOrderNo":"R0621061B0155736016065",
        "TrxAmount":"0.01",
        "BatchNo":"",
        "VoucherNo":"",
        "HostDate":"",
        "HostTime":"",
        "iRspRef":""
    }
}

这里,特别指出,不是放在第一点一起说,是因为字段的命名让我们容易差生错觉。

5.支付回调接口

HTTP的POST方式,这里获取入参的方式比较特别,放着请求体不用,
偏偏要通过表单和链接传递参数,让你程序必须使用getParameter来接收报文。

public class Notify{ 
    public String abcRefundNotifyRes(HttpServletRequest request, HttpServletResponse response)  {
        String msg = request.getParameter("MSG");
        
        //而我们一般对接微信和其他银行的接收入参是如下写法
        String xmlResult = IOUtils.toString(request.getInputStream(),
            request.getCharacterEncoding());
    }
}

6.农行对接文档的吐槽

有一个不足之处是,缺少请求和响应的示例报文。导致我们写程序只能走一步调试一步。
还有就是在总体设计的时候,不去注重描述协议的规范和格式,甚至把他们自认为有用的UML贴出来给你看。
有这个功夫,不是应该提供一个SDK,让接入方直接引入,然后Builder模式一把就可以发送报文,
易于接入才是王道,偏要让接入方去反编译他的class文件。

然后你提出不要jar中的固化打印一些无用的调试日志,他们会跟你说,那是你自己的个性化需求,需要自己实现。

我想去掉不打印日志,都没得开关,让接入方如何不去“个性化”实现。。。(日志不是slf4j打印,
且是自己单独写服务器的某文件中,不携带任何业务信息,就算采集到ELK,也根本不能帮助排查问题。)

7.最后想要吐嘈的是他们的验签方法。

有两个地方需要尤为注意:
第一、编码格式,并不全是utf-8;
发送json格式请求的时候是utf-8,验证签名却应该是gbk。支付回调报文的验签却又不同,应该是gb2312.
第二、支付回调的报文必须采用他们给的XMLDocument.java才验证通过。

这一块,如果不给出示例代码,对接的过程中会踩不少坑!!

8.最后梳理下他们的报文协议

https+证书,发http采用HttpClient框架,读取证书文件在应用启动后,
保存在应用内存缓存起来,后期只需要根据account账户来缓存里取即可。
既然要使用https,就必须初始化农行的根证书abc.truststore,取得javax.net.ssl.SSLContext后赋值给HttpClient的http连接池。
发送报文前,必须要使用商户私钥(使用密码读取pfx文件,取出私钥内容)加密,计算出签名。
接收报文的时候,使用农行的支付平台证书TrustPay.cer进行加密,对比两者的签名是否一致。

9.没有SDK的痛苦!!

写道这里,有没有一种感觉,需要理解透这些协议,以及字段的含义,才能够开始接入联调。农行给你整一堆的jsp和html页面,以及jar包等着你去反编译呢~~
提供一个官网可下载的sdk,以及spring boot版本的sample,就是那么难~~

相关文章

网友评论

      本文标题:对接农业银行支付(微信和支付宝)的总结(三)

      本文链接:https://www.haomeiwen.com/subject/pjaxyltx.html