• 对接农行支付


    废话不多说,直切如题。

            前几天写了一篇对接微信支付的,因为项目和农行合作,需要对接农行支付,在对接的过程中遇到太多坑了,和我对接的是客户经理,我需要资料和咨询技术方面的东西都需要他传递,效率大大降低。好在项目周期比较充足,连猜带蒙终于搞定了。下面介绍具体流程,给后来的老哥闭闭坑~~~~~~~~~ 

    首先农行会提供对接文档,农行根证书,商户证书,网上支付平台证书等必要资料

     里面会有基本的配置介绍。

    环境配置:

            会有两个配置文件,将它们放入到项目的resources文件夹下

    按照里面的要求配置好参数,然后将TurstPayClient-V3.1.8.jar导入项目;

    提示:农行支付接口支持多商户模式,就是说同个项目可以配置多个商户号,交易的时候可以指定使用哪一个商户号。

    因为实际项目需要微信支付和农行掌银支付,所以以下内容会介绍这两张支付模式。

    一切都配置好了之后开始敲代码了。

    农行微信支付:

            在微信中调用农行的微信支付,也就是农行对接微信,我对接农行。体验是和微信支付一模一样的。

     将参数按照要求封装之后调用jar包里的微信下单接口,实例代码如下

    1. public JSON abcWxOrder(String outTradeNo, String appId, String openId, String totalFee, String body) {
    2. //封装参数
    3. UnifiedPaymentRequest unifiedPaymentRequest = new UnifiedPaymentRequest();
    4. //设置订单属性
    5. //微信支付类型,1:NATIVE 2:JSAPI 3:APP 4:MICROPAY
    6. unifiedPaymentRequest.dicOrder.put("PayTypeID", Constants.PAY_TYPE_JSAPI);
    7. //订单日期
    8. unifiedPaymentRequest.dicOrder.put("OrderDate", DateUtil.format(new Date(), "yyyy/MM/dd"));
    9. //订单时间
    10. unifiedPaymentRequest.dicOrder.put("OrderTime", DateUtil.format(new Date(), "HH:mm:ss"));
    11. //订单号
    12. unifiedPaymentRequest.dicOrder.put("OrderNo", outTradeNo);
    13. //OpenID
    14. unifiedPaymentRequest.dicOrder.put("OpenID", openId);
    15. //交易币种 156-人民币
    16. unifiedPaymentRequest.dicOrder.put("CurrencyCode", "156");
    17. //交易金额
    18. unifiedPaymentRequest.dicOrder.put("OrderAmount", NumberUtil.roundStr(Double.parseDouble(totalFee) / 100, 2));
    19. //刷卡支付时,请上送授权码;公众号、小程序、APP支付时,请上送应用的APPID
    20. unifiedPaymentRequest.dicOrder.put("AccountNo", appId);
    21. //订单描述
    22. unifiedPaymentRequest.dicOrder.put("OrderDesc", body);
    23. //支付请求对象属性
    24. //支付账户类型,8:微信支付
    25. unifiedPaymentRequest.dicRequest.put("PaymentType", "8");
    26. //支付交易渠道,1:internet网络接入 2:手机网络接入 3:数字电视网络接入 4:智能客户端 5:线下渠道
    27. unifiedPaymentRequest.dicRequest.put("PaymentLinkType", "2");
    28. //支付结果通知方式,0:仅URL页面通知 1:服务器通知和URL页面通知
    29. unifiedPaymentRequest.dicRequest.put("NotifyType", "1");
    30. //商户接收支付结果通知地址,商户自己填写
    31. unifiedPaymentRequest.dicRequest.put("ResultNotifyURL", systemConfig.getNotifyUrl() + "/unitePay/abcNotify.api");
    32. //商品类型
    33. unifiedPaymentRequest.dicRequest.put("CommodityType", "0401");
    34. //农行模板分账或者平台商户的二级商户分账时必输,1:进行分账 0:不进行分账
    35. unifiedPaymentRequest.dicRequest.put("IsBreakAccount", "0");
    36. try {
    37. JSON json = unifiedPaymentRequest.postRequest();
    38. return json;
    39. } catch (Exception e) {
    40. log.error("农行微信支付下单接口异常:{}", e.getMessage());
    41. throw new RuntimeException();
    42. }
    43. }

    正确拿到返回参数JSAPI,这是微信APP支付唤起支付控件所需字段,

     String JSAPI = json.GetKeyValue("JSAPI");

    JSAPI返回示例如下:

    1. "JSAPI": {
    2. "sub_appId": "wxa4f5c9918ecXXXX",
    3. "timeStamp": "1576556760",
    4. "nonceStr": "017f7061615d7812a6b102644c11e",
    5. "package": "prepay_id=wx1712270edf3298e1734690300",
    6. "signType": "RSA",
    7. "timestamp": "1576556760",
    8. "paySign": "0MrX0DKYR+JN5xqL1wp++h80i+zoU1K+8tR//LMur9bsg06Z08L3BuhlAqk7IdQ0HxWR5MOHeh4Gu7n+GYzlAyfMmNFUtm7zxAmcdb7mOgpCrbSD8eUPpjXviZYisK/8MEnNBekC1L0QtwujqgImdzvNpvtShCjPsdPOTfvu7sHQ3hhve6YdF9d5c7M7K3D0iONucohGBiairWO7ObAh6N2nUg7w++XB+/lupl3iIlmDCROew=="
    9. },

     然后将这些参数给前端,调用微信支付控件就能唤醒微信支付了。

    唤醒农行掌银APP支付:

            首先使用农行的一码多扫下单接口,先拼接参数,再调用jar里的方法,示例代码如下:

    1. public JSON abcUniteOrder(String orderNo, String totalFee) {
    2. try {
    3. PaymentRequest paymentRequest = new PaymentRequest();
    4. paymentRequest.dicRequest.put("PaymentType", "A");
    5. paymentRequest.dicRequest.put("PaymentLinkType", "2");
    6. paymentRequest.dicRequest.put("NotifyType", "1");
    7. paymentRequest.dicRequest.put("ResultNotifyURL", "");
    8. paymentRequest.dicRequest.put("IsBreakAccount", "0");
    9. paymentRequest.dicOrder.put("PayTypeID", "ImmediatePay");
    10. paymentRequest.dicOrder.put("OrderDate", DateUtil.format(new Date(), "yyyy/MM/dd"));
    11. paymentRequest.dicOrder.put("OrderTime", DateUtil.format(new Date(), "HH:mm:ss"));
    12. paymentRequest.dicOrder.put("OrderNo", orderNo);
    13. paymentRequest.dicOrder.put("CurrencyCode", "156");
    14. paymentRequest.dicOrder.put("OrderAmount", NumberUtil.roundStr(Double.parseDouble(totalFee) / 100, 2));
    15. paymentRequest.dicOrder.put("CommodityType", "0401");
    16. paymentRequest.dicOrder.put("BuyIP", InetAddress.getLocalHost().getHostAddress());
    17. LinkedHashMap orderItems = new LinkedHashMap();
    18. orderItems.put("ProductName", "水费");
    19. paymentRequest.orderitems.put("ProductName", orderItems);
    20. JSON json = paymentRequest.postRequest();
    21. return json;
    22. } catch (Exception e) {
    23. log.error("农行支付下单接口请求异常:{}", e.getMessage());
    24. throw new RuntimeException();
    25. }
    26. }

    然后拿到返回参数PaymentURL,如:method=invokePayFromBrowser&tokenID=144057162720037182

    String paymentURL = json.GetKeyValue("PaymentURL");

    接着按照文档要求拼接唤醒掌银app的url,示例代码如下,方法里的tokenId是paymentURL中的tokenID值,加密方法DES用的是hutool包里封装的方法(网上能搜到的,hutool工具)

    1. public String getAbcUrlByNew(String tokenId) {
    2. String method = "https://wx.abchina.com/webank/main-view/openTagFor?id=ycA%3D&dynamicData=";
    3. DES des = new DES(Mode.CBC, Padding.PKCS5Padding, "abc".getBytes(), "abc".getBytes());
    4. String s = des.encryptBase64("tokenID="+tokenId);
    5. String encode = URLEncoder.encode(s, "UTF-8");
    6. return method+encode;
    7. }

    最后将这个拼接后的url给前端,打开就自动跳转到掌银APP了。

    支付回调:

            支付下单的时候有回调url传参的,农行会按照这个url进行支付结果通知,示例代码如下

    1. @RequestMapping(value = "/abc.api")
    2. @AuthMethod(type = RequestType.APIOUT)
    3. public String abcNotify(@RequestBody String xmlStr){
    4. System.out.println("-------接收到农行回调信息------"+xmlStr);
    5. // return "https://xxx.xxx.com/result/index.shtml";
    6. return commonPayService.abcNotify(xmlStr);
    7. }

            

    1. public String abcNotify(String xmlStr) {
    2. String decode = URLDecoder.decode(xmlStr, "utf-8");
    3. //支付结果对象
    4. PaymentResult paymentResult = new PaymentResult(decode);
    5. //支付订单号
    6. String orderNo = paymentResult.getValue("OrderNo");
    7. log.info("开始接收农行回调信息,支付订单号:{}",orderNo);
    8. //订单金额(分)
    9. // String amount = (Double.parseDouble(paymentResult.getValue("Amount")) * 100) + "";
    10. BigDecimal aa = new BigDecimal(Double.parseDouble(paymentResult.getValue("Amount")) + "");
    11. BigDecimal bb = new BigDecimal("100");
    12. BigDecimal multiply = aa.multiply(bb);
    13. String amount = multiply.intValue() + "";
    14. //交易批次号
    15. paymentResult.getValue("BatchNo");
    16. //交易凭证号(建议使用iRspRef作为对账依据)
    17. paymentResult.getValue("VoucherNo");
    18. //银行交易日期(YYYY/MM/DD)
    19. String day = paymentResult.getValue("HostDate");
    20. //银行交易时间(HH:MM:SS)
    21. String time = paymentResult.getValue("HostTime");
    22. String timeEnd = day.replace("/", "-") + " " + time;
    23. //商户备注信息(商户在支付请求时所提交的信息)
    24. paymentResult.getValue("MerchantRemarks");
    25. //消费者支付方式
    26. paymentResult.getValue("PayType");
    27. //支付结果通知方式
    28. paymentResult.getValue("NotifyType");
    29. //银行返回交易流水号
    30. String bankOrderId = paymentResult.getValue("iRspRef");
    31. //付款银行(微信支付时返回)
    32. String bank_type = paymentResult.getValue("bank_type");
    33. //第三方订单号(微信和支付宝)
    34. String thirdOrderNo = paymentResult.getValue("ThirdOrderNo");
    35. WxOrder info = getInfo(orderNo, amount);
    36. if (info == null) {
    37. //平台系统找不到订单
    38. log.error("农行支付回调订单查询不到,订单号:{},银行流水号:{},订单金额:{},支付完成时间:{}",
    39. orderNo, bankOrderId, amount, day + "-" + time);
    40. return null;
    41. }
    42. if (info.getStatus() != 0) {
    43. //订单已经处理,无需进一步操作
    44. return null;
    45. } else {
    46. //处理订单
    47. if (!paymentResult.isSuccess()) {
    48. //支付失败
    49. log.info("农行回调:订单支付失败,订单号:{},失败原因:{}", orderNo, paymentResult.getErrorMessage());
    50. return "" + systemConfig.getNotifyUrl() + "/result/fail.shtml";
    51. }
    52. }
    53. return "" + systemConfig.getNotifyUrl() + "/result/success.shtml";
    54. }

    农行这里有点奇葩的,返回要用标签包起来,而且我试过这个路径随便写也是能通过的。

    还有一点,农行退费没有回调的,跟我对接的老哥告诉我没有,不知道是不是真的没有,文档也没找到。

  • 相关阅读:
    java学习day18(Java核心类库)多线程
    【Rust日报】2022-09-16 Cloudflare放弃了Nginx,转而使用Rust编写的Pingora
    ORB-SLAM2 ---- Frame::UndistortKeyPoints函数
    TS的内置类型-Pick,Omit
    uni-app小程序 swiper 分页器样式修改
    手把手教你深度学习和实战-----线性回归+梯度下降法
    VTK 标注类Widget 文字标注 vtkCaptionWidget
    大学生旅游风景主题dreamweaver网页设计大作业-陕西渭南HTML+CSS制作网页
    【图灵MySQL】深入理解Mysql索引底层数据结构与算法
    禅道数据库异机访问,远程连接,navicat连接
  • 原文地址:https://blog.csdn.net/a1029573879a/article/details/127790572