SpringBoot从0到1整合银联无跳转支付功能附源码

前言

提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。

提示:以下是本篇文章正文内容,下面案例可供参考

一、官网

https://open.unionpay.com/tjweb/user/mchTest/param 个人登录后的相关参数查看

其实在接入支付之前建议大家了解下 对称加密,分对称加密RSA之类,摘要算法,https,证书等这些知识点,因为此代码后面的验签,判断都是基于此的。初始化加载证书,判断证书是否正确,过期,是否被串改都需要上面知识的支撑

二、使用步骤

1.下载官方提供的代码

https://open.unionpay.com/tjweb/acproduct/list?apiSvcId=449&index=5
支持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 菜单"服务单管理"->"创建服务单"请求排查问题。

3. 配置项目启动相关参数

由于项目启动需要验证各种类型的证书,在本地他会找磁盘路径的证书位置,需要大家将 assert目录的证书拷贝到磁盘中 并对应到配置文件中的路径上 如下

4. 依赖jar

因为当前的项目不是一个maven项目只是一个普通的web项目,会涉及各种的jar依赖,大家自行配置项目中的web路径下lib目录,其中如果有问题请看当下目录的说明文件

5.tomcat启动项目

其实银联的这个文档写的还是相当不错的。就是在接的过程中他涉及的各种问题就是需要自己去体会理解。毕竟是老的版本,所以有些技术细节还是跟当下不一样的,比如传参居然不是json。

6.支付 项目已经正常启动,那么带大家了解下他的支付流程,这里先简单介绍下,后面直接上代码。其实所有的支付无非就是生产订单,请求第三方支付定义的接口,传递规范的参数,然后支付方会返回一个同步通知,还有一个异步通知,在这两个通知中实现你的业务处理。

== 同步,异步通知 都是需要外网能够访问的连接地址,所以你需要一个内网穿透工具,之前我一直在用natapp,https://blog.csdn.net/hu15081398237/article/details/94721290 这里面有我介绍使用natapp接入微信的步骤, 今天在这里使用的是uTools https://u.tools/docs/guide/about-uTools.html 它提供穿透功能==


当你项目启动成功,然后使用当前工具映射地址即可成功,然后你就可以使用链接访问自己项目。此项目能被外网访问

7.银联自己的项目代码简单介绍

建议大家先看下他们的代码,跑通后在自己整合,我们是大致瞅了一眼,我们架构就开始封装了,毕竟是大佬,但是后面我们也踩坑,我又调用它原始代码,分析入参,出参,结合我们的代码分析问题,一般都是出在了参数传递问题

在你都接入好后,需要用他们提供的信息进行测试,有问题就到这个地址找,搜索。银联这方面还是做得不错的
https://open.unionpay.com/tjweb/support/faq/mchlist?id=4

8.整合后的SpringBoot项目


自定义starter初始化项目

注意 相当于jdk spi,doubo spi 的特性:

9.springboot项目启动

10.支付讲解,先看图,后面有Demo代码,注释很清楚


上面填写的参数就是如下需要的信息




11.示例代码讲解

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();
 }

}

总结

项目封装基础,业务处理我们架构都用了设计模式【sdk模块】,主要就是模板设计。建议大家好好理解下模板设计模式,真的各种项目框架的源代码不可能没有模板。需要项目源码请双击点赞留言+邮箱,毕竟整理不易。后面我抽出时间会整理个开源的sdk出来,这个的封装是我们架构封装,我只是嵌入业务功能代码实现。所以有些封装只是适合我们的业务处理,对于开源是我们没有考虑的。如果大家关注度高我就来整理下。主要最近沉迷于netty中,从入门到迷茫(>ω<)喵

到此这篇关于SpringBoot从0到1整合银联无跳转功能支付附源码的文章就介绍到这了,更多相关SpringBoot银联支付内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • springboot调用支付宝第三方接口(沙箱环境)

    springboot+调用支付宝第三方接口(沙箱环境) 大神勿喷!! 网址:https://developers.alipay.com/platform/developerIndex.htm 沙箱环境中给测试提供了一些必要和非必要的参数,需要自己动手修改的只有RSA密钥,需要通过支付宝提供的开发工具生成RSA密钥,下载链接:https://docs.open.alipay.com/291/105971#LDsXr 下载完成安装之后,可以生成RSA密钥.生成的密钥分为应用公钥,应用私钥.应用私钥自

  • SpringBoot集成支付宝沙箱支付(支付、退款)

    前言 支付宝推出一个沙箱环境,能够很好的模拟支付宝支付,并且还提供了demo,但demo是一个普通web项目,怎么整合到Spring Boot项目呢,其实很简单 简单配置请参照支付宝沙箱支付开发文档 一.支付部分 AlipayConfig配置:我使用的是页面跳转同步通知,返回路径return_url为公网访问地址,也可以使用localhost,且不能携带参数,APPID.商户私钥.支付宝公钥和支付宝网关换为自己的. public class AlipayConfig{ // 应用ID,您的APP

  • SpringBoot下如何实现支付宝接口的使用

    这篇文章主要介绍了SpringBoot下如何实现支付宝接口的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 前期准备: 参考之前写过的 支付宝接口引入servlet版本 Jar包引入: <!-- 支付宝 --> <dependency> <groupId>net.guerlab</groupId> <artifactId>sdk-alipay-starter</artifactId&g

  • SpringBoot整合支付宝APP支付

    前言 现在是移动端产品疯狂的年代,随之,移动端支付也是热门小技能,最近本公司在做一个移动端,要接入微信支付和支付宝支付,老习惯,功能做完之后做个复盘记录,这边主要讲解支付宝APP支付 所需条件 1.创建蚂蚁金服开放平台公司账号 2.选择开发中心---->移动应用 3.选择支付接入 4.创建应用 5.查看应用appId 6.添加功能 两个功能:支付宝授权功能和APP支付功能 7.功能签约 开发流程 引入支付宝支付SDK <!-- 支付宝支付 SDK --> <dependency&g

  • IDEA基于支付宝小程序搭建springboot项目的详细步骤

    服务端 在平台上创建springboot小程序应用 创建小程序 登录蚂蚁金服开放平台,扫码登录填写信息后,点击支付宝小程序,选择立即接入 > 同意个人公测 > 开始创建 . 填写好小程序基本信息后,点击创建按钮,创建名为xxx小程序. PS:一个账号下最多可以创建10个小程序:未提交过审核的小程序可以删除,删除的小程序不在计数范围. 创建云应用后端服务 在小程序页面选择刚创建的小程序,点击查看,进入开发者页面. 在左侧导航栏选择云服务(免费),点击创建云服务,选择创建云应用,技术栈选Sprin

  • springboot对接支付宝支付接口(详细开发步骤总结)

    最近需要对接支付宝的支付接口,官方文档写得内容有点分散,整理了一下发布出来,用作记录,同时也希望对不了解情况的人有所帮助,这里以电脑端的网页支付为例. 开发主要分为三个步骤:一.生成私钥公钥.二.建立应用.三.沙箱环境.四.接口开发 一.生成私钥公钥 生成密钥的官网文档:官网文档 官方文档讲得已经很详细,按照步骤来即可,记得保存好公钥与私钥,下面需要用到 二.建立应用 1.首先进入蚂蚁金服开放平台的首页,通过支付宝账户登录,登录的时候要选择一个身份,这个选自研开发者吧,反正后面可以拓展 2.在蚂

  • springboot 集成支付宝支付的示例代码

    最简单的springboot集成支付宝 1 注册沙箱 沙箱是一个模拟环境登录,百度蚂蚁金服开放平台,支付宝扫码登录如下 然后沙箱需要注册一下,非常之简单,注册好以后进入到如下页面,选沙箱工具,然后下载一个生成密钥的工具.然后解压按照里面的readme生成公私密钥, 选择沙箱应用 上传公钥即可..沙箱到这里就基本完成了,里面还有沙箱版本的的android app可以下载下来. java 程序 1 新建一个springboot项目 因为我们创建的是一个web工程,所以,仅仅演示支付宝的demo,只需

  • springboot整合微信支付sdk过程解析

    前言 之前做的几个微信小程序项目,大部分客户都有要在微信小程序前端提现的需求.提现功能的实现,自然使用企业付款接口,不过这个功能开通比较麻烦,要满足3个条件; 之前实现过几个微信支付的接口,不过都是自己码的代码,从网上找找拼凑,觉得看起来不舒服~_~ 于是乎找到了微信官方提供的支付sdk.这里用的是java版本,springboot整合java 下载sdk,引入项目 这里可以直接下载官方提供的sdk,然后将几个java类拷贝到你的项目,也可以直接引入maven依赖,这里是直接将Java类拷贝到我

  • SpringBoot从0到1整合银联无跳转支付功能附源码

    前言 提示:这里可以添加本文要记录的大概内容: 例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容. 提示:以下是本篇文章正文内容,下面案例可供参考 一.官网 https://open.unionpay.com/tjweb/user/mchTest/param 个人登录后的相关参数查看 其实在接入支付之前建议大家了解下 对称加密,分对称加密RSA之类,摘要算法,https,证书等这些知识点,因为此代码后面的验签,判断都是基于此的

  • python gui开发——制作抖音无水印视频下载工具(附源码)

    hello,大家好啊,失踪人口回归了 [捂脸]!本次使用tkinter撰写一篇 抖音无水印视频下载,目的很纯粹,就是为了设置 微信状态视频.本篇博文中,我会写下我的代码撰写思路以及想写设计流程,代码放在了第四节,工具打包好放在了 蓝奏云,慢慢看,后面有链接. 一.准备工作 本次要用到以下依赖库:re json os random tkinter threading requests pillow 其中后两个需要安装后使用 二.预览 0.复制抖音分享短链接 1.启动 2.运行 3.结果 (小姐姐挺

  • Springboot+Thymeleaf+Jpa实现登录功能(附源码)

    前言 最近有学习到关于Springboot+Thymeleaf+Jpa的综合运用知识,因此想写一个简单的登录界面来尝试一下,以下将展示具体流程 具体实现 首先要创建一个springboot项目 添加以下依赖项 pom.xml代码 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=&

  • Vue 2.0+Vue-router构建一个简单的单页应用(附源码)

    一.介绍 vue.js 是 目前 最火的前端框架,vue.js 兼具 angular.js 和 react.js 的优点,并剔除它们的缺点,并且提供了很多的周边配套工具 如vue-router .vue-resource .vuex等等 ,通过他们我们可以很轻松的构建一个大型单页应用. 目前Vue版本为:Vue2.0 官网地址:http://vuejs.org.cn/ 查看API文档:https://vuefe.cn/v2/api/ 对比其他框架:http://vuejs.org.cn/guid

  • SpringBoot2.0+阿里巴巴Sentinel动态限流实战(附源码)

    Sentinel 是什么? 随着微服务的流行,服务和服务之间的稳定性变得越来越重要.Sentinel 以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度保护服务的稳定性. Sentinel 具有以下特征: 丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围).消息削峰填谷.集群流量控制.实时熔断下游不可用应用等. 完备的实时监控:Sentinel 同时提供实时的监控功能.您可以在控制台中看到接入应用的

  • 一篇文章带你入门Springboot沙箱环境支付宝支付(附源码)

    目录 0.前言 1.效果展示 2.技术栈介绍 3.前期准备 第一步:申请一个沙箱测试账号 第二步:电脑下载一个支付宝提供的客户端用于生成RSA2 第三步:手机下载 [沙箱版支付宝] 4.后端搭建 项目目录结构 pom.xml application.yml application-alipay.proerties Order订单实体类 Service层 Controller层 配置类 跨域拦截器配置以及注册 启动spirngboot项目 支付操作的页面: 支付完成后支付宝回调的页面: 启动前端项

  • SpringBoot自定义加载yml实现方式,附源码解读

    目录 自定义加载yml,附源码解读 解决方法 源码解读 如何引入多个yml方法 方案一:无前缀,使用@Value注解 方案二:有前缀,无需@Value注解 自定义加载yml,附源码解读 昨天在对公司的微服务配置文件标准化的过程中,发现将原来的properties文件转为yml文件之后,微服务module中标记有@Configuration的配置类都不能正常工作了,究其原因,是由于@PropertySource属性默认只用于标记并告诉spring boot加载properties类型的文件 spr

  • 详解SpringBoot集成jsp(附源码)+遇到的坑

    本文介绍了SpringBoot集成jsp(附源码)+遇到的坑 ,分享给大家 1.大体步骤 (1)创建Maven web project: (2)在pom.xml文件添加依赖: (3)配置application.properties支持jsp (4)编写测试Controller (5)编写JSP页面 (6)编写启动类App.java 2.新建SpringInitialzr 3.pom文件 <dependencies> <dependency> <groupId>org.s

  • Android10.0实现本地音乐播放(附源码下载)

    1.概述 本篇文章仅是Android小白在写一个小程序,内容仅供参考,有很多不足之处希望各位大神指出,文章末尾有整个项目的下载,不需要币,只求帮你们解决到问题的同时收获到一颗小小的赞.这个项目中还有很多不足的地方,如:在按键中设置图片文字,这些正常的应该交给Handler处理,我只是粗略地完成这个项目.测试环境:Android10.0.实现:自动播放下一首,正常音乐的功能,全屏显示. Android10.0是内外分存了的,应用是没有权限读取内存的,需要在配置文件中application中加上属性

  • ssm+vue前后端分离框架整合实现(附源码)

    前言 本文针对Spring+SpringMVC+Mybatis后台开发框架(基于maven构建)与vue前端框架(基于webpack构建)的项目整合进行介绍,对于ssm和vue单独项目的搭建不作为本文的重点,而着重介绍两者之间交互的要点. SSM 项目结构 说明 项目有service和web两个子项目组成,web依赖于service,其中web主要是control层内容,service则对应service层,而MyBatis内容放在了service项目中,spring配置文件放在了web项目中.

随机推荐