废话不多说,直切如题。
前几天写了一篇对接微信支付的,因为项目和农行合作,需要对接农行支付,在对接的过程中遇到太多坑了,和我对接的是客户经理,我需要资料和咨询技术方面的东西都需要他传递,效率大大降低。好在项目周期比较充足,连猜带蒙终于搞定了。下面介绍具体流程,给后来的老哥闭闭坑~~~~~~~~~
首先农行会提供对接文档,农行根证书,商户证书,网上支付平台证书等必要资料
里面会有基本的配置介绍。
环境配置:
会有两个配置文件,将它们放入到项目的resources文件夹下
按照里面的要求配置好参数,然后将TurstPayClient-V3.1.8.jar导入项目;
提示:农行支付接口支持多商户模式,就是说同个项目可以配置多个商户号,交易的时候可以指定使用哪一个商户号。
因为实际项目需要微信支付和农行掌银支付,所以以下内容会介绍这两张支付模式。
一切都配置好了之后开始敲代码了。
农行微信支付:
在微信中调用农行的微信支付,也就是农行对接微信,我对接农行。体验是和微信支付一模一样的。
将参数按照要求封装之后调用jar包里的微信下单接口,实例代码如下
public JSON abcWxOrder(String outTradeNo, String appId, String openId, String totalFee, String body) {
//封装参数
UnifiedPaymentRequest unifiedPaymentRequest = new UnifiedPaymentRequest();
//设置订单属性
//微信支付类型,1:NATIVE 2:JSAPI 3:APP 4:MICROPAY
unifiedPaymentRequest.dicOrder.put("PayTypeID", Constants.PAY_TYPE_JSAPI);
//订单日期
unifiedPaymentRequest.dicOrder.put("OrderDate", DateUtil.format(new Date(), "yyyy/MM/dd"));
//订单时间
unifiedPaymentRequest.dicOrder.put("OrderTime", DateUtil.format(new Date(), "HH:mm:ss"));
//订单号
unifiedPaymentRequest.dicOrder.put("OrderNo", outTradeNo);
//OpenID
unifiedPaymentRequest.dicOrder.put("OpenID", openId);
//交易币种 156-人民币
unifiedPaymentRequest.dicOrder.put("CurrencyCode", "156");
//交易金额
unifiedPaymentRequest.dicOrder.put("OrderAmount", NumberUtil.roundStr(Double.parseDouble(totalFee) / 100, 2));
//刷卡支付时,请上送授权码;公众号、小程序、APP支付时,请上送应用的APPID
unifiedPaymentRequest.dicOrder.put("AccountNo", appId);
//订单描述
unifiedPaymentRequest.dicOrder.put("OrderDesc", body);
//支付请求对象属性
//支付账户类型,8:微信支付
unifiedPaymentRequest.dicRequest.put("PaymentType", "8");
//支付交易渠道,1:internet网络接入 2:手机网络接入 3:数字电视网络接入 4:智能客户端 5:线下渠道
unifiedPaymentRequest.dicRequest.put("PaymentLinkType", "2");
//支付结果通知方式,0:仅URL页面通知 1:服务器通知和URL页面通知
unifiedPaymentRequest.dicRequest.put("NotifyType", "1");
//商户接收支付结果通知地址,商户自己填写
unifiedPaymentRequest.dicRequest.put("ResultNotifyURL", systemConfig.getNotifyUrl() + "/unitePay/abcNotify.api");
//商品类型
unifiedPaymentRequest.dicRequest.put("CommodityType", "0401");
//农行模板分账或者平台商户的二级商户分账时必输,1:进行分账 0:不进行分账
unifiedPaymentRequest.dicRequest.put("IsBreakAccount", "0");
try {
JSON json = unifiedPaymentRequest.postRequest();
return json;
} catch (Exception e) {
log.error("农行微信支付下单接口异常:{}", e.getMessage());
throw new RuntimeException();
}
}
正确拿到返回参数JSAPI,这是微信APP支付唤起支付控件所需字段,
String JSAPI = json.GetKeyValue("JSAPI");
JSAPI返回示例如下:
"JSAPI": {
"sub_appId": "wxa4f5c9918ecXXXX",
"timeStamp": "1576556760",
"nonceStr": "017f7061615d7812a6b102644c11e",
"package": "prepay_id=wx1712270edf3298e1734690300",
"signType": "RSA",
"timestamp": "1576556760",
"paySign": "0MrX0DKYR+JN5xqL1wp++h80i+zoU1K+8tR//LMur9bsg06Z08L3BuhlAqk7IdQ0HxWR5MOHeh4Gu7n+GYzlAyfMmNFUtm7zxAmcdb7mOgpCrbSD8eUPpjXviZYisK/8MEnNBekC1L0QtwujqgImdzvNpvtShCjPsdPOTfvu7sHQ3hhve6YdF9d5c7M7K3D0iONucohGBiairWO7ObAh6N2nUg7w++XB+/lupl3iIlmDCROew=="
},
然后将这些参数给前端,调用微信支付控件就能唤醒微信支付了。
唤醒农行掌银APP支付:
首先使用农行的一码多扫下单接口,先拼接参数,再调用jar里的方法,示例代码如下:
public JSON abcUniteOrder(String orderNo, String totalFee) {
try {
PaymentRequest paymentRequest = new PaymentRequest();
paymentRequest.dicRequest.put("PaymentType", "A");
paymentRequest.dicRequest.put("PaymentLinkType", "2");
paymentRequest.dicRequest.put("NotifyType", "1");
paymentRequest.dicRequest.put("ResultNotifyURL", "");
paymentRequest.dicRequest.put("IsBreakAccount", "0");
paymentRequest.dicOrder.put("PayTypeID", "ImmediatePay");
paymentRequest.dicOrder.put("OrderDate", DateUtil.format(new Date(), "yyyy/MM/dd"));
paymentRequest.dicOrder.put("OrderTime", DateUtil.format(new Date(), "HH:mm:ss"));
paymentRequest.dicOrder.put("OrderNo", orderNo);
paymentRequest.dicOrder.put("CurrencyCode", "156");
paymentRequest.dicOrder.put("OrderAmount", NumberUtil.roundStr(Double.parseDouble(totalFee) / 100, 2));
paymentRequest.dicOrder.put("CommodityType", "0401");
paymentRequest.dicOrder.put("BuyIP", InetAddress.getLocalHost().getHostAddress());
LinkedHashMap orderItems = new LinkedHashMap();
orderItems.put("ProductName", "水费");
paymentRequest.orderitems.put("ProductName", orderItems);
JSON json = paymentRequest.postRequest();
return json;
} catch (Exception e) {
log.error("农行支付下单接口请求异常:{}", e.getMessage());
throw new RuntimeException();
}
}
然后拿到返回参数PaymentURL,如:method=invokePayFromBrowser&tokenID=144057162720037182
String paymentURL = json.GetKeyValue("PaymentURL");
接着按照文档要求拼接唤醒掌银app的url,示例代码如下,方法里的tokenId是paymentURL中的tokenID值,加密方法DES用的是hutool包里封装的方法(网上能搜到的,hutool工具)
public String getAbcUrlByNew(String tokenId) {
String method = "https://wx.abchina.com/webank/main-view/openTagFor?id=ycA%3D&dynamicData=";
DES des = new DES(Mode.CBC, Padding.PKCS5Padding, "abc".getBytes(), "abc".getBytes());
String s = des.encryptBase64("tokenID="+tokenId);
String encode = URLEncoder.encode(s, "UTF-8");
return method+encode;
}
最后将这个拼接后的url给前端,打开就自动跳转到掌银APP了。
支付回调:
支付下单的时候有回调url传参的,农行会按照这个url进行支付结果通知,示例代码如下
@RequestMapping(value = "/abc.api")
@AuthMethod(type = RequestType.APIOUT)
public String abcNotify(@RequestBody String xmlStr){
System.out.println("-------接收到农行回调信息------"+xmlStr);
// return "<URL>https://xxx.xxx.com/result/index.shtml</URL>";
return commonPayService.abcNotify(xmlStr);
}
public String abcNotify(String xmlStr) {
String decode = URLDecoder.decode(xmlStr, "utf-8");
//支付结果对象
PaymentResult paymentResult = new PaymentResult(decode);
//支付订单号
String orderNo = paymentResult.getValue("OrderNo");
log.info("开始接收农行回调信息,支付订单号:{}",orderNo);
//订单金额(分)
// String amount = (Double.parseDouble(paymentResult.getValue("Amount")) * 100) + "";
BigDecimal aa = new BigDecimal(Double.parseDouble(paymentResult.getValue("Amount")) + "");
BigDecimal bb = new BigDecimal("100");
BigDecimal multiply = aa.multiply(bb);
String amount = multiply.intValue() + "";
//交易批次号
paymentResult.getValue("BatchNo");
//交易凭证号(建议使用iRspRef作为对账依据)
paymentResult.getValue("VoucherNo");
//银行交易日期(YYYY/MM/DD)
String day = paymentResult.getValue("HostDate");
//银行交易时间(HH:MM:SS)
String time = paymentResult.getValue("HostTime");
String timeEnd = day.replace("/", "-") + " " + time;
//商户备注信息(商户在支付请求时所提交的信息)
paymentResult.getValue("MerchantRemarks");
//消费者支付方式
paymentResult.getValue("PayType");
//支付结果通知方式
paymentResult.getValue("NotifyType");
//银行返回交易流水号
String bankOrderId = paymentResult.getValue("iRspRef");
//付款银行(微信支付时返回)
String bank_type = paymentResult.getValue("bank_type");
//第三方订单号(微信和支付宝)
String thirdOrderNo = paymentResult.getValue("ThirdOrderNo");
WxOrder info = getInfo(orderNo, amount);
if (info == null) {
//平台系统找不到订单
log.error("农行支付回调订单查询不到,订单号:{},银行流水号:{},订单金额:{},支付完成时间:{}",
orderNo, bankOrderId, amount, day + "-" + time);
return null;
}
if (info.getStatus() != 0) {
//订单已经处理,无需进一步操作
return null;
} else {
//处理订单
if (!paymentResult.isSuccess()) {
//支付失败
log.info("农行回调:订单支付失败,订单号:{},失败原因:{}", orderNo, paymentResult.getErrorMessage());
return "<URL>" + systemConfig.getNotifyUrl() + "/result/fail.shtml</URL>";
}
}
return "<URL>" + systemConfig.getNotifyUrl() + "/result/success.shtml</URL>";
}
农行这里有点奇葩的,返回要用<URL>标签包起来,而且我试过这个路径随便写也是能通过的。
还有一点,农行退费没有回调的,跟我对接的老哥告诉我没有,不知道是不是真的没有,文档也没找到。