雖然之前對於接入第三方支付有比較多的經驗,但是這次接入環迅h5支付還是花了2天半的時間才整入到項目中,遇到了一個坑耽擱了一天半,下面講一下接入注意事項
- 可能大家一般都是接入微信支付寶等官方支付(微信支付寶官方支付全部已調通更換參數即可,下面會有下載資源鏈接),下面是環迅支付接入流程
- 環迅支付文檔請看IPS接口定義技術規範-綜合網關-V2.0.2,裏面自帶demo,demo並不是Javaweb項目,需要使用tomcat運行
demo啓動效果訪問地址:http://localhost:8080/accentry/
h5發起支付後會返回html代碼,該代碼可以在瀏覽器直接加載---------手機號登錄------------------添加銀行卡------------開通快捷支付-----------再次發送驗證碼進行付款-------付款成功後環迅服務器會同步回調以及異步回調
這會直接上接入代碼了:發起h5網關支付類
AccEntryConfig/CollBackResponse/FunctionUtils/MerVerify/Request/SSLClient/SynchronousResponse/Utils這些類在demo中直接複製過來
package com.api.ips;
import java.net.URLEncoder;
import java.util.LinkedHashMap;
import org.junit.Test;
import com.alibaba.fastjson.JSONObject;
/**
* 環迅支付工具
*
* @author leehao
* @date 2019-09-25
*/
public class IpsPayUtils {
/***
* 環迅h5支付
*
* @param commodity 商品
* @param describe 描述
* @param payCode 訂單號
* @param orderPrice 金額(以元爲單位)
* @return
*/
public static String hxPay(String commodity, String describe, String payCode, String orderPrice) {
String ts = AccEntryConfig.date;// AccEntryConfig.date;//時間戳
String nonceStr = Utils.getRandomString(32);// 隨機字符串
String trxDtTm = AccEntryConfig.date;// 商戶日期
// 1.加密data
JSONObject dt = new JSONObject(new LinkedHashMap());
dt.put("platCode", "");// 平臺編碼默認1001
dt.put("account", AccEntryConfig.accCode);// 賬戶號
dt.put("trxId", payCode);// 商戶訂單號
dt.put("trxDtTm", ts);// 訂單日期
dt.put("expireDtTm", "");// 訂單截止時間
dt.put("trxCcyCd", "156");// 幣種枚舉,默認 156
dt.put("trxAmt", orderPrice);// 單位元
dt.put("productType", "");// 產品類型
dt.put("limitPayType", "");// 限制支付方式
dt.put("lang", AccEntryConfig.lang);// 默認爲zh
dt.put("pageUrl", AccEntryConfig.successURL);// 頁面回調地址
dt.put("notifyUrl", AccEntryConfig.s2sURL);// 後臺回調地址
dt.put("attach", commodity);// 商品介紹
dt.put("goodsDesc", describe);// 商品內容
String ds = dt.toString();
// 1
// System.out.println("加密前data: " + ds);
String signData = MerVerify.encryptAES(AccEntryConfig.AES_KEY, ds);
// System.out.println("加密後signData: " + signData);
// 2
Request accEntryRequest = new Request(AccEntryConfig.version, AccEntryConfig.date, AccEntryConfig.merCode,
nonceStr, AccEntryConfig.format, AccEntryConfig.encryptType, signData, AccEntryConfig.signType);
String object = Utils.getParamStr(Utils.objectToMap(accEntryRequest));
// System.out.println("加密前object: " + object);
String signObject = MerVerify.signSHA256withRSA(AccEntryConfig.SHA256_PRIVATE_KEY, object);
// System.out.println("加密後signObject: " + signObject);
JSONObject retObj = new JSONObject();
retObj.put("version", AccEntryConfig.version);
retObj.put("ts", ts);
retObj.put("merCode", AccEntryConfig.merCode);
retObj.put("nonceStr", nonceStr);
retObj.put("format", AccEntryConfig.format);
retObj.put("encryptType", AccEntryConfig.encryptType);
retObj.put("signType", AccEntryConfig.signType);
retObj.put("account", AccEntryConfig.accCode);
retObj.put("trxId", payCode);
retObj.put("trxDtTm", trxDtTm);
retObj.put("trxAmt", orderPrice);
retObj.put("data", signData);
retObj.put("sign", signObject);
String response = null;
try {
response = Utils.postSubmit(AccEntryConfig.ipsURL, retObj);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
return "10001";
}
return response;
}
-
這裏是發起請求接口以及支付回調(同步-異步 驗籤解析data中的數據) 確認交易成功後必須響應環迅支付服務器"ipscheckok"否則會多次回調接口 package com.api.ips; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.net.URLEncoder; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.Map; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.alibaba.fastjson.JSONObject; @RestController public class pay { @GetMapping("pay") public String payh() { String hxPay = IpsPayUtils.hxPay("蘋果", "蝦皮蘋果", "798484848444474", "0.01"); System.out.println(hxPay); return hxPay; } /** * 同步回調 * * @param request * @param response * @throws UnsupportedEncodingException */ @RequestMapping("asynchroHuanxin") public void asynchroHuanxin(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { JSONObject map = Utils.getJsonParam(request); System.out.println("同步回調:" + map.toString()); String data = map.getString("data"); String version = map.getString("version"); String merCode = map.getString("merCode"); String ts = map.getString("ts"); String nonceStr = map.getString("nonceStr"); String format = map.getString("format"); String encryptType = map.getString("encryptType"); String signType = map.getString("signType"); String notifyType = map.getString("notifyType"); String sign = map.getString("sign"); String verifyObject = null; String decryptData = null; boolean verifyResult = false; try { if (!Utils.isBlank(sign)) { CollBackResponse backResponse = new CollBackResponse(version,ts,merCode,nonceStr, format,encryptType,data,signType,notifyType); verifyObject = Utils.getParamStr(Utils.objectToMap(backResponse)); verifyResult = MerVerify.verifySHA256withRSA(AccEntryConfig.SHA256_PUBLIC_KEY, verifyObject, sign); if (verifyResult) { decryptData = MerVerify.decryptAES(AccEntryConfig.AES_KEY, data); System.out.println("----:"+decryptData); JSONObject jo = JSONObject.parseObject(new String(decryptData)); String trxId = jo.getString("trxId");//商戶訂單號 String trxStatus = jo.getString("trxStatus");//訂單狀態 String trxResultMsg = jo.getString("trxResultMsg");//訂單消息 String ipsTrxId = jo.getString("ipsTrxId");//IPS訂單流水號 if (trxStatus.equals("SUCCESS")) { //交易成功 System.out.println("交易成功"); } if (trxStatus.equals("FAIL")) { //交易失敗 System.out.println("交易失敗"); } if (trxStatus.equals("PROCESSING")) { //交易處理中 System.out.println("交易處理中"); } } } } catch (Throwable ex) { ex.printStackTrace(); } } /** * 異步回調 * * @param request * @param response */ @RequestMapping("synchroHuanxin") public String synchroHuanxin(HttpServletRequest request, HttpServletResponse response) { JSONObject map = Utils.getJsonParam(request); System.out.println("異步回調:" + map.toString()); String data = map.getString("data"); String version = map.getString("version"); String merCode = map.getString("merCode"); String ts = map.getString("ts"); String nonceStr = map.getString("nonceStr"); String format = map.getString("format"); String encryptType = map.getString("encryptType"); String signType = map.getString("signType"); String notifyType = map.getString("notifyType"); String sign = map.getString("sign"); String verifyObject = null; String decryptData = null; boolean verifyResult = false; try { if (!Utils.isBlank(sign)) { CollBackResponse backResponse = new CollBackResponse(version,ts,merCode,nonceStr, format,encryptType,data,signType,notifyType); verifyObject = Utils.getParamStr(Utils.objectToMap(backResponse)); verifyResult = MerVerify.verifySHA256withRSA(AccEntryConfig.SHA256_PUBLIC_KEY, verifyObject, sign); if (verifyResult) { decryptData = MerVerify.decryptAES(AccEntryConfig.AES_KEY, data); System.out.println("----:"+decryptData); JSONObject jo = JSONObject.parseObject(new String(decryptData)); String trxId = jo.getString("trxId");//商戶訂單號 String trxStatus = jo.getString("trxStatus");//訂單狀態 String trxResultMsg = jo.getString("trxResultMsg");//訂單消息 String ipsTrxId = jo.getString("ipsTrxId");//IPS訂單流水號 if (trxStatus.equals("SUCCESS")) { //交易成功 System.out.println("交易成功"); return "ipscheckok"; } if (trxStatus.equals("FAIL")) { //交易失敗 System.out.println("交易失敗"); } if (trxStatus.equals("PROCESSING")) { //交易處理中 System.out.println("交易處理中"); } } } } catch (Throwable ex) { ex.printStackTrace(); } return null; } }
所需的jar包在demo全部複製過來即可,但是不支持maven從網上倉庫引入依賴,會導致無法加載私鑰。目前我是直接把jar包添加到項目中,大家可以嘗試本地安裝jar再引入依賴
這裏是支付成功後環迅服務器回調接口傳過來的參數值,訂單狀態以及訂單信息都在data中。需要解密
驗籤需要把所有參數通過文檔方式加密一遍與sign相對比
同步回調:
{"merCode":"178767",
"notifyType":"1",
"data":"83b8203f20b6a9aabb79a9c3735ae75f8a3bd1c1b99e8972b4146297ccd2ed17a0948434480fd47f270a41093800b02e6bf426bacd846bc88b4671a89a51af3e8c0bf611d7e744d0b8b293e47aeae983597d02a98f8b90a0c5f9c738906d625c27fd52b39d7bcf05171c960ad535a26569d2579bd1ee6f921395e6258064067298694b8d80269276459cf4703c4c21a22d5fe9803fa7ce38d650cba8131504c0e3e965de980ba8525d452507b735db5f91ec6ac9ffbcd5555d22ddcdfbed1a2e486493de9bac03367857923f6effa9fbffe6d26dde4cdb7e818ab4dd3020c1acab938d42811e81c606ef2f462015f03b89e4f805eb0df182407bc68ed12e5f510785e1c5d324e756ef4c85b4ba63635773782a75d3559ec519e59a4a7814c942b09757c8f6d07d9c21c6bf67c92f13387a481410cc28a6208f3d5e5996fa0195a822abe5c6303e2439a56a4e443a19c72a9f652eb97112c3551a87e38741e5edf36f3fdbdf94222b52be63ad81e075da898700e380e5d44509a7ba59305ec995c8e61c3746fa1b7cd8ba1b3ba72969a9",
"sign":"3ff5680aed7a5667a19015567efd86d9b6734191746be3e31c095c629ee9763e63e58acccbe01c706f2e6d431f20d9fcca6b9bb3e58afb621a0bc53300ba0523b35b745d1fb414eeab7202f7de786ad6a8d8ad3a229f00db78eefe5de43d15a68df5291a681264384733c6bd71dfee53fafdec9f5eace2104d5aa7c34de0eba412fe5b6ad803442a85009f510494f6430645f1ec42a31500979f0fa610f042c4c5af3020472c970cc2a362dcc55afb2bf5fd2418f86a8288714d238df6a166d8a339f2f8e17ea2bbaf8a2e171efc71001256858e48bce7140101e3ebfde0485dd2f3eb22df94528af9fa0be390bb0b7e5c5987088324b3e6ac6f6a93b806e268",
"signType":"RSA2",
"version":"2.0.0",
"encryptType":"AES",
"nonceStr":"5668d6f03c6b4e0c8f3e28acc86473ef",
"ts":"20191024115045"}
----:{"account":"1787670106","trxId":"88988998989444","trxDtTm":"20191024","trxCcyCd":"156","trxAmt":"0.01","payAmt":"0.01","payFee":"0","feeWho":"2","payType":"1501","cardType":"1","trxStatus":"SUCCESS","trxResultMsg":"支付成功","attach":"蘋果","ipsTrxId":"BO20191024115016004120","bkTrxId":"2019102442104710419284230202905","ipsTrxDtTm":"20191024115045","thdTrxId":"","extInfo":"thrPayInfo="}
----:蘋果
異步回調:{
"merCode":"178767",
"notifyType":"1",
"data":"83b8203f20b6a9aabb79a9c3735ae75f8a3bd1c1b99e8972b4146297ccd2ed17a0948434480fd47f270a41093800b02e6bf426bacd846bc88b4671a89a51af3e8c0bf611d7e744d0b8b293e47aeae983597d02a98f8b90a0c5f9c738906d625c27fd52b39d7bcf05171c960ad535a26569d2579bd1ee6f921395e6258064067298694b8d80269276459cf4703c4c21a22d5fe9803fa7ce38d650cba8131504c0e3e965de980ba8525d452507b735db5f91ec6ac9ffbcd5555d22ddcdfbed1a2e486493de9bac03367857923f6effa9fbffe6d26dde4cdb7e818ab4dd3020c1acab938d42811e81c606ef2f462015f03b89e4f805eb0df182407bc68ed12e5f510785e1c5d324e756ef4c85b4ba63635773782a75d3559ec519e59a4a7814c942b09757c8f6d07d9c21c6bf67c92f13387a481410cc28a6208f3d5e5996fa0195a822abe5c6303e2439a56a4e443a19c72a9f652eb97112c3551a87e38741e5edf36f3fdbdf94222b52be63ad81e075da898700e380e5d44509a7ba59305ec995c8e61c3746fa1b7cd8ba1b3ba72969a9",
"sign":"29f4050533302b29cd5d13e85e89390cdb8bab95d84c77cfe2ee1fcb4c0d5d0948431460afa98e36875a406af212328c79f1c054f5d000ea579b7959e7933cf5e01fca7245bfa1d1ecefe6f30350e25870329a435c4e9828fca71e6f66949e831bee93c1cd6bbc3583a206996dfbb0e1e8bc0f3a78ff94589cc8e8db009e3a597a457b17a74e63a387bbfdb93c3e7624f0cfc313c0a307a7fdba1af7ec199c49fe9c54e811d50ce111de22fa9de86f1ea4c36e8de152cb8d2351fbd97fdb1494e7295b92a285adb1c9716fc8d3d66d45cdcaa98174801ce6bb3267e16a00a05181145e7c58d933e5be0ab880977acba63a7e1ee61cc5b836da92f0e5e363e177",
"signType":"RSA2",
"version":"2.0.0",
"nonceStr":"889df20d9ccd4b3385d7cc139da36f52",
"encryptType":"AES",
"ts":"20191024115045"}
支付回調必須要做域名穿透,大家可是用花生殼或者https://natapp.cn/member/dashborad這兩者,花生殼前期可以免費試用,但是得實名認證,域名不會發生改變,natapp可以一直使用但是每次使用的時候分配的域名都不一致
說實話,環迅支付文檔是我遇到最難搞的了。像個gui一樣~~~~~