利用SpringBoot實現一個跳轉支付功能?相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
支持3個語言的接入,這里只說java版本的。而且后面我也對代碼進行了重構,畢竟demo的項目還是無法直接嵌入到生產項目中
項目展示(示例):因為我接入的是token版本的,所以都是查看token的代碼,銀聯的支付涉及web 和token兩種,大同小異,只是涉及到傳入的參數類型不一樣而已
2.目錄說明【一定要對著項目結構認真看這段內容】
ACPSample-WuTiaoZhuan
│
├src┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈類文件夾
│ │
│ ├assets ┈┈┈┈┈┈┈┈┈相關資源目錄
│ │ │
│ ├assets ┈┈┈┈┈┈┈┈┈相關資源目錄
│ │ │
│ │ ├apache_httpclient┈┈┈┈┈┈┈┈┈apache中的http post方法
│ │ │
│ │ ├測試環境證書
│ │ │ │
│ │ │ ├acp_test_enc.cer ┈┈┈┈┈┈┈┈┈ 【重要】測試環境敏感信息加密證書(所有商戶固定使用同一個)
│ │ │ │
│ │ │ ├acp_test_sign.pfx ┈┈┈┈┈┈┈┈┈ 【重要】 測試環境簽名私鑰證書(所有商戶固定使用同一個)
│ │ │ │
│ │ │ ├acp_test_root.cer ┈┈┈┈┈┈┈┈┈ 【重要】 測試環境驗簽公鑰證書根證書 (所有商戶固定使用同一個)
│ │ │ │
│ │ │ └acp_test_middle.cer ┈┈┈┈┈┈┈┈┈【重要】 測試環境驗簽公鑰證書中級證書 (所有商戶固定使用同一個)
│ │ │
│ │ ├對賬文件樣例
│ │ │ │
│ │ │ └802310048993424_20150905.zip ┈┈┈┈┈┈┈┈┈提供的對賬文件樣例(如果需要可以參考)
│ │ │
│ │ ├收單機構接入需做改動
│ │ │ │
│ │ │ ├acp_test_sign_inst.pfx ┈┈┈┈┈┈┈┈┈【重要】 收單機構接入的測試環境簽名私鑰證書(所有機構固定使用同一個)
│ │ │ │
│ │ │ └機構接入需做改動.txt
│ │ │
│ │ ├測試環境配置文件
│ │ │ │
│ │ │ └acp_sdk.properties ┈┈┈┈┈┈┈┈┈【重要】 測試環境配置文件樣例(證書方式簽名)(使用方式請看文件里的說明)
│ │ │
│ │ ├生產環境配置文件
│ │ │ │
│ │ │ └acp_sdk.properties ┈┈┈┈┈┈┈┈┈【重要】 生產環境配置文件樣例(證書方式簽名)(使用方式請看文件里的說明)
│ │ │
│ │ └生產環境證書
│ │ │
│ │ ├acp_prod_enc.cer┈┈┈┈┈┈┈┈┈【重要】 生產環境敏感信息加密證書(所有商戶固定使用同一個)
│ │ │
│ │ ├acp_prod_root.cer ┈┈┈┈┈┈┈┈┈【重要】 生產環境驗簽公鑰根證書 (所有商戶固定使用同一個)
│ │ │
│ │ └acp_prod_middle.cer ┈┈┈┈┈┈┈┈┈【重要】 生產環境驗簽公鑰中級證書 (所有商戶固定使用同一個)
│ │
│ ├com.unionpay.acp.demo
│ │ │
│ │ ├token ┈┈┈┈┈┈┈┈┈無跳轉token交易相關
│ │ │ │
│ │ │ ├Form03_6_2_Token_OpenCard_Back.java┈┈┈┈┈商戶側開通示例類(后臺)
│ │ │ │
│ │ │ ├Form03_6_2_Token_OpenCard_Front.java┈┈┈┈┈銀聯側開通示例類(前臺)
│ │ │ │
│ │ │ ├Form03_6_3_Token_OpenQuery.java┈┈┈┈┈開通查詢示例類(后臺)
│ │ │ │
│ │ │ ├Form03_6_4_Token_DeleteToken.java┈┈┈┈┈刪除token示例類(后臺)
│ │ │ │
│ │ │ ├Form03_6_5_Token_UpdateToken.java┈┈┈┈┈更新token示例類(后臺)
│ │ │ │
│ │ │ ├Form03_6_6_Token_ConsumeSMS.java┈┈┈┈┈消費短信示例類(后臺)
│ │ │ │
│ │ │ ├Form03_6_6_Token_OpenSMS.java┈┈┈┈┈開通短信示例類 (后臺)
│ │ │ │
│ │ │ ├Form03_6_7_Token_Consume.java┈┈┈┈┈消費示例類 (后臺)
│ │ │ │
│ │ │ ├Form03_6_7_Token_OpenAndConsume.java┈┈┈┈┈開通并付款示例類 (前臺)
│ │ │ │
│ │ │ └Form03_6_Token_ApplyToken.java┈┈┈┈申請toke號示例類 (前臺)
│ │ │
│ │ ├BackRcvResponse.java┈┈┈┈┈后臺通知處理示例類
│ │ │
│ │ ├DemoBase.java┈┈┈┈┈基礎類
│ │ │
│ │ ├EncryptCerUpdateQuery.java┈┈┈┈┈加密證書更新示例類(后臺)
│ │ │
│ │ ├Form03_6_3_ConsumeUndo.java┈┈┈┈┈消費撤銷示例類 (后臺)
│ │ │
│ │ ├Form03_6_4_Refund.java┈┈┈┈┈退貨示例類 (后臺)
│ │ │
│ │ ├Form03_6_5_Query.java┈┈┈┈┈交易狀態查詢示例類 (后臺)
│ │ │
│ │ ├Form03_7_FileTransfer.java┈┈┈┈┈對賬文件下載示例(后臺)
│ │ │
│ │ ├FrontRcvResponse.java┈┈┈┈┈前臺通知處理示例類
│ │ │
│ │ └多個商戶號各自使用自己的私鑰證書(多證書)使用方法.txt
│ │
│ ├com.unionpay.acp.sdk
│ │ │
│ │ ├AcpService.java┈┈┈┈┈┈全渠道SDK API接口類
│ │ │
│ │ ├CertUtil.java┈┈┈┈┈┈證書處理工具類
│ │ │
│ │ ├HttpClient.java┈┈┈┈┈后臺交易http post通訊類,如果要使用代理訪問或者產生了問題那么可以自行解決或者使用apache httpClient
│ │ │
│ │ ├LogUtil.java┈┈┈┈┈日志工具類
│ │ │
│ │ ├SDKConfig.java┈┈┈┈┈┈┈讀取acp_sdk.properties屬性文件并填裝配置的屬性的配置類
│ │ │
│ │ ├SDKConstants.java┈┈┈┈┈┈┈常量類
│ │ │
│ │ ├SDKUtil.java┈┈┈┈┈┈┈SDK工具類,包含了對報文的簽名,驗簽等方法
│ │ │
│ │ ├SecureUtil.java┈┈┈┈┈┈┈安全相關工具類
│ │ │
│ │ └SM3Digest.java┈┈┈┈┈┈┈sm3算法工具類
│ │
│ └web ┈┈┈┈┈┈┈┈┈ web相關類
│ │
│ ├AutoLoadServlet.java ┈┈┈┈┈┈初始化讀取acp_sdk.properties初始化請求銀聯地址,證書等相關資源的servlet
│ │
│ └CharsetEncodingFilter.java ┈┈┈┈web請求編碼過濾器
│
├acp_sdk.properties ┈┈┈┈【重要】測試環境配置文件,請求銀聯地址,私鑰簽名證書,驗簽公鑰路徑,多證書的配置文件(這個文件切換生產的時候要替換成生產環境的配置文件)
│
├log4j.properties ┈┈┈┈LogUtil.java日志工具類的配置文件
│
├WebContent ┈┈┈┈┈┈┈┈┈┈┈┈┈┈頁面文件夾
│ │
│ ├index.jsp ┈┈┈┈┈┈┈┈┈調試入口頁面
│ │
│ └WEB-INF
│ │
│ └lib(如果JAVA項目中包含這些架包,則不需要導入)
│ │
│ ├bcprov-jdk15on-1.54.jar---------注意包名后綴版本,低版本的bc包不支持sdk使用的部分方法
│ │
│ ├commons-codec-1.6.jar
│ │
│ ├commons-io-2.2.jar
│ │
│ ├commons-lang-2.5.jar
│ │
│ ├log4j-1.2.17.jar
│ │
│ ├slf4j-api-1.5.11.jar
│ │
│ └slf4j-log4j12-1.5.11.jar
│
└readme.txt ┈┈┈┈┈┈┈┈┈使用說明文本
───────────
注意
1.【接口規范】該接口參考文檔位置:
接口產品規范:open.unionpay.com幫助中心 下載 產品接口規范 《無跳轉產品接口規范》
應答碼規范:《平臺接入接口規范-第5部分-附錄》
商戶對賬文件格式說明:《全渠道平臺接入接口規范 第3部分 文件接口》
2.【測試商戶號】開發包中使用的商戶號777290058110097是open.unionpay.com注冊的測試商戶號,只能在入網測試環境使用;
可以先使用這個商戶調通交易(當然您也可以自己在這個網站注冊一個777開頭的測試商戶號,自己注冊后要開通權限:https://open.unionpay.com 登陸后 右上角我的測試-我的產品-將未測試的產品點擊成測試狀態,過10分鐘后就有權限了)
正式線上環境請替換成申請的正式商戶號,并確保商戶號有對應的權限,如果報了無此交易權限等錯誤,請聯系您申請接入銀聯的業務人員確認您做的交易是否開通了對應的權限。
3.【關于配置文件】
配置文件在src/assets文件夾下可以找到,src下面默認使用的是測試環境使用證書方式簽名的配置文件。請按配置文件中的說明進行修改。
使用時需要配置證書路徑,證書文件除了生產環境的簽名證書需要業務郵件發送下載方式下載,其余證書均在src/assets文件夾下面有提供,需要復制到配置文件配置的路徑。
4.【測試過程遇到問題】
1)優先在open平臺中查找答案:
調試過程中的問題或其他問題請在 https://open.unionpay.com/ajweb/help/faq/list 幫助中心 FAQ 搜索解決方案
測試過程中產生的7位應答碼問題疑問請在https://open.unionpay.com/ajweb/help/respCode/respCodeList 輸入應答碼搜索解決方案
2)測試環境測試支付請使用測試卡號測試, FAQ搜索“測試卡”。
3)切換生產環境要點請FAQ搜索“切換”。
5.【生產環境遇到問題】連接銀聯生產環境測試遇到的問題 如果通過open平臺無法解決 請登陸merchant.unionpay.com 菜單"服務單管理"->"創建服務單"請求排查問題。
由于項目啟動需要驗證各種類型的證書,在本地他會找磁盤路徑的證書位置,需要大家將 assert目錄的證書拷貝到磁盤中 并對應到配置文件中的路徑上 如下
因為當前的項目不是一個maven項目只是一個普通的web項目,會涉及各種的jar依賴,大家自行配置項目中的web路徑下lib目錄,其中如果有問題請看當下目錄的說明文件
其實銀聯的這個文檔寫的還是相當不錯的。就是在接的過程中他涉及的各種問題就是需要自己去體會理解。畢竟是老的版本,所以有些技術細節還是跟當下不一樣的,比如傳參居然不是json。
6.支付 項目已經正常啟動,那么帶大家了解下他的支付流程,這里先簡單介紹下,后面直接上代碼。其實所有的支付無非就是生產訂單,請求第三方支付定義的接口,傳遞規范的參數,然后支付方會返回一個同步通知,還有一個異步通知,在這兩個通知中實現你的業務處理。
== 同步,異步通知 都是需要外網能夠訪問的連接地址,所以你需要一個內網穿透工具,之前我一直在用natapp,https://blog.csdn.net/hu15081398237/article/details/94721290 這里面有我介紹使用natapp接入微信的步驟, 今天在這里使用的是uTools https://u.tools/docs/guide/about-uTools.html 它提供穿透功能==
當你項目啟動成功,然后使用當前工具映射地址即可成功,然后你就可以使用鏈接訪問自己項目。此項目能被外網訪問
建議大家先看下他們的代碼,跑通后在自己整合,我們是大致瞅了一眼,我們架構就開始封裝了,畢竟是大佬,但是后面我們也踩坑,我又調用它原始代碼,分析入參,出參,結合我們的代碼分析問題,一般都是出在了參數傳遞問題
在你都接入好后,需要用他們提供的信息進行測試,有問題就到這個地址找,搜索。銀聯這方面還是做得不錯的
https://open.unionpay.com/tjweb/support/faq/mchlist?id=4
自定義starter初始化項目
注意 相當于jdk spi,doubo spi 的特性:
上面填寫的參數就是如下需要的信息
package com.ehs.union.pay.spring.boot.demo.controller; import com.esh.union.pay.sdk.bean.config.UnionPayConfig; import com.esh.union.pay.sdk.bean.consts.*; import com.esh.union.pay.sdk.bean.request.UnionOpenAllChannelAndPayRequest; import com.esh.union.pay.sdk.service.UnionPayService; import com.esh.union.pay.sdk.utils.UnionStrUtil; import com.esh.union.pay.sdk.utils.sign.CertDescriptor; import com.esh.union.pay.sdk.utils.sign.UnionSecureUtil; import com.esh.union.pay.sdk.utils.sign.UnionSignUtil; import com.esh.union.pay.sdk.utils.sign.encrypt.RSA2; import com.esh.union.pay.sdk.utils.sign.encrypt.X509; import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.digest.DigestUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.DateFormatUtils; import org.apache.commons.lang3.time.DateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLDecoder; import java.security.PrivateKey; import java.util.*; /** * 無跳轉支付 * * @author huyufan * @date 2020/11/8 16:49 */ @Controller @RequestMapping(value = "/pay") public class PayController { protected static final Logger log = LoggerFactory.getLogger(PayController.class); @Autowired private UnionPayService unionPayService; @Autowired private CertDescriptor certDescriptor; /** * 支付請求 TO 銀聯 * @param resp * @param openAllChannelAndPayRequest */ @RequestMapping(value = "/yinlian", method = RequestMethod.POST) public void toYinlianPay(HttpServletResponse resp, @ModelAttribute(value = "openAllChannelAndPayRequest") UnionOpenAllChannelAndPayRequest openAllChannelAndPayRequest) { UnionPayConfig config = unionPayService.getConfig(); CertDescriptor certDescriptor = unionPayService.getCertDescriptor(); openAllChannelAndPayRequest.setBizType(UnionBizTypeConsts.BIZ_TYPE_000902); openAllChannelAndPayRequest.setTxnTime(DateFormatUtils.format(new Date(), "yyyyMMddHHmmss")); //TODO 后臺異步回調,暫未實現 backUrl 默認填官方要求的默認值,否則會校驗 openAllChannelAndPayRequest.setBackUrl(config.getBackUrl()); openAllChannelAndPayRequest.setCurrencyCode("156"); openAllChannelAndPayRequest.setTxnType(UnionTxnTypeConsts.TXN_TYPE_01); openAllChannelAndPayRequest.setTxnSubType(UnionTxnSubTypeConsts.TXN_SUB_TYPE_01); openAllChannelAndPayRequest.setAccessType(UnionAccessTypeConsts.ACCESS_TYPE_0); openAllChannelAndPayRequest.setSignature(UnionSignMethodConsts.SIGNMETHOD_01); openAllChannelAndPayRequest.setChannelType(UnionChannelTypeConsts.CHANNEL_TYPE_07); openAllChannelAndPayRequest.setAccType(UnionAccTypeConsts.ACC_TYPE_01); openAllChannelAndPayRequest.setTokenPayData("{trId=99988877766&tokenType=01}"); openAllChannelAndPayRequest.setAccNo(null); //前臺通知地址 openAllChannelAndPayRequest.setFrontUrl(config.getFrontUrl()); openAllChannelAndPayRequest.setPayTimeout(DateFormatUtils.format(DateUtils.addDays(new Date(), 1), "yyyyMMddHHmmss")); //request openAllChannelAndPayRequest.setCertId(certDescriptor.getSignCertId()); openAllChannelAndPayRequest.setEncryptCertId(certDescriptor.getEncryptCertId()); Map<String, String> paramMap = openAllChannelAndPayRequest.signAndGetMap(config, certDescriptor.getSignCertPrivateKey(config.getPrivateCertPwd()), config.getPrivateKeyString()); String html = createAutoFormHtml(String.format(UnionUrlConsts.FRONT_TRANS_URL, "test.95516.com"), paramMap, "UTF-8"); try { resp.getWriter().write(html); } catch (IOException e) { e.printStackTrace(); } } /** * 銀聯同步通知 * @param req * @return */ @RequestMapping("/frontRcvResponse") public ModelAndView frontRcvResponse(HttpServletRequest req) { String encoding = req.getParameter("encoding"); log.info("返回報文中encoding=[" + encoding + "]"); String pageResult = ""; if ("UTF-8".equalsIgnoreCase(encoding)) { pageResult = "success"; } else { pageResult = "error"; ModelAndView modelAndView = new ModelAndView(pageResult); modelAndView.addObject("result", "交易出錯,請聯系管理員"); return modelAndView; } //獲取響應數據轉換為map Map<String, String> respParam = getAllRequestParam(req); // 驗簽集合 Map<String, String> valideData = null; StringBuffer page = new StringBuffer(); //組裝集合數據 if (null != respParam && !respParam.isEmpty()) { Iterator<Map.Entry<String, String>> it = respParam.entrySet().iterator(); valideData = new HashMap<String, String>(respParam.size()); while (it.hasNext()) { Map.Entry<String, String> e = it.next(); String key = (String) e.getKey(); String value = (String) e.getValue(); valideData.put(key, value); } } //獲取公鑰證書 /*X509Certificate x509Cert = SDK.genCertificateByStr(valideData.get("signPubKeyCert")); if (x509Cert == null) { log.info("convert signPubKeyCert failed"); throw new RuntimeException(); }*/ // 2.驗證證書鏈 以及是否過期 /*if (!SDK.verifyCertificate(x509Cert, certDescriptor)) { log.info("驗證公鑰證書失敗,證書信息:[" + valideData.get("signPubKeyCert") + "]"); throw new RuntimeException(); }*/ //驗 簽 處理 String sign256 = DigestUtils.sha256Hex(UnionSignUtil.createQueryString(valideData, UnionParamConsts.getDefaultIgnoreSignParams())); boolean validate = RSA2.verify(sign256, valideData.get(UnionParamConsts.PARAM_SIGNATURE),X509.getPublicKey(valideData.get(UnionParamConsts.PARAM_SIGN_PUB_KEY_CERT)), "UTF-8"); if (!validate) { log.info("驗證簽名結果[失敗]."); return null; } else { log.info("驗證簽名結果[成功]."); //前臺回調接口不涉及用戶相關信息封裝 /*String customerInfo = valideData.get("customerInfo"); if (null != customerInfo) { Map<String, String> customerInfoMap = this.parseCustomerInfo(customerInfo, "UTF-8"); page.append("customerInfo明文: " + customerInfoMap); } String accNo = valideData.get("accNo"); //如果返回的卡號是密文那么,可以用下邊方法解密 if (null != accNo) { accNo = AcpService.decryptData(accNo, "UTF-8"); page.append("<br>accNo明文: " + accNo); }*/ //判斷respCode=00、A6后,對涉及資金類的交易,請再發起查詢接口查詢,確定交易成功后更新數據庫。 String respCode = valideData.get("respCode"); } if (StringUtils.equals(valideData.get("respCode"), "00")) { //解密卡號 String accNo = UnionSecureUtil.decryptData(certDescriptor.getSignCertPrivateKey("000000"), valideData.get("accNo"), "UTF-8"); respParam.put("accNo明文",accNo); ModelAndView modelAndView = new ModelAndView(pageResult); modelAndView.addObject("result", respParam); return modelAndView; } else { //TODO 交易碼不正確 邏輯未完善 ModelAndView modelAndView = new ModelAndView(pageResult); modelAndView.addObject("result", "交易出錯,請聯系管理員"); return modelAndView; } } /** * 解析返回報文(后臺通知)中的customerInfo域:<br> * 解base64,如果帶敏感信息加密 encryptedInfo 則將其解密并將 encryptedInfo中的域放到customerInfoMap返回<br> * * @param customerInfo<br> * @param encoding<br> * @return */ public Map<String, String> parseCustomerInfo(String customerInfo, String encoding) { Map<String, String> customerInfoMap = null; try { byte[] b = Base64.decodeBase64(customerInfo.getBytes(encoding)); String customerInfoNoBase64 = new String(b, encoding); log.info("解base64后===>" + customerInfoNoBase64); //去掉前后的{} customerInfoNoBase64 = customerInfoNoBase64.substring(1, customerInfoNoBase64.length() - 1); customerInfoMap = UnionStrUtil.parseQString(customerInfoNoBase64); if (customerInfoMap.containsKey("encryptedInfo")) { String encInfoStr = customerInfoMap.get("encryptedInfo"); customerInfoMap.remove("encryptedInfo"); PrivateKey privateKey = certDescriptor.getSignCertPrivateKey("000000"); String encryptedInfoStr = UnionSecureUtil.decryptData(privateKey, encInfoStr, encoding); Map<String, String> encryptedInfoMap = UnionStrUtil.parseQString(encryptedInfoStr); customerInfoMap.putAll(encryptedInfoMap); } } catch (UnsupportedEncodingException e) { log.info(e.getMessage(), e); } catch (IOException e) { log.info(e.getMessage(), e); } return customerInfoMap; } /** * 獲取請求參數中所有的信息 * 當商戶上送frontUrl或backUrl地址中帶有參數信息的時候, * 這種方式會將url地址中的參數讀到map中,會導多出來這些信息從而致驗簽失敗,這個時候可以自行修改過濾掉url中的參數或者使用getAllRequestParamStream方法。 * * @param request * @return */ public static Map<String, String> getAllRequestParam( final HttpServletRequest request) { Map<String, String> res = new HashMap<String, String>(); Enumeration<?> temp = request.getParameterNames(); if (null != temp) { while (temp.hasMoreElements()) { String en = (String) temp.nextElement(); String value = request.getParameter(en); res.put(en, value); // 在報文上送時,如果字段的值為空,則不上送<下面的處理為在獲取所有參數數據時,判斷若值為空,則刪除這個字段> if (res.get(en) == null || "".equals(res.get(en))) { // System.out.println("======為空的字段名===="+en); res.remove(en); } } } return res; } /** * 獲取請求參數中所有的信息。 * 非struts可以改用此方法獲取,好處是可以過濾掉request.getParameter方法過濾不掉的url中的參數。 * struts可能對某些content-type會提前讀取參數導致從inputstream讀不到信息,所以可能用不了這個方法。理論應該可以調整struts配置使不影響,但請自己去研究。 * 調用本方法之前不能調用req.getParameter("key");這種方法,否則會導致request取不到輸入流。 * * @param request * @return */ public static Map<String, String> getAllRequestParamStream( final HttpServletRequest request) { Map<String, String> res = new HashMap<String, String>(); try { String notifyStr = new String(IOUtils.toByteArray(request.getInputStream()), "UTF-8"); log.info("收到通知報文:" + notifyStr); String[] kvs = notifyStr.split("&"); for (String kv : kvs) { String[] tmp = kv.split("="); if (tmp.length >= 2) { String key = tmp[0]; String value = URLDecoder.decode(tmp[1], "UTF-8"); res.put(key, value); } } } catch (UnsupportedEncodingException e) { log.info("getAllRequestParamStream.UnsupportedEncodingException error: " + e.getClass() + ":" + e.getMessage()); } catch (IOException e) { log.info("getAllRequestParamStream.IOException error: " + e.getClass() + ":" + e.getMessage()); } return res; } public static String createAutoFormHtml(String reqUrl, Map<String, String> hiddens, String encoding) { StringBuffer sf = new StringBuffer(); sf.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + encoding + "\"/></head><body>"); sf.append("<form id = \"pay_form\" action=\"" + reqUrl + "\" method=\"post\">"); if (null != hiddens && 0 != hiddens.size()) { Set<Map.Entry<String, String>> set = hiddens.entrySet(); Iterator<Map.Entry<String, String>> it = set.iterator(); while (it.hasNext()) { Map.Entry<String, String> ey = it.next(); String key = ey.getKey(); String value = ey.getValue(); sf.append("<input type=\"hidden\" name=\"" + key + "\" id=\"" + key + "\" value=\"" + value + "\"/>"); } } sf.append("</form>"); sf.append("</body>"); sf.append("<script type=\"text/javascript\">"); sf.append("document.all.pay_form.submit();"); sf.append("</script>"); sf.append("</html>"); return sf.toString(); } }
看完上述內容,你們掌握利用SpringBoot實現一個跳轉支付功能的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。