银联对接接口
项目中用到了对接银联接口(支付宝微信同理)特记录笔记如下:
1.
首先我们要在我们的银联开放平台上找到我们的项目demo 和各种证书
![在这里插入图片描述](https://img-blog.csdnimg.cn/06860e05610945338982476108b7c264.png
注意啦这些文件下载下来你要修改你的properties里面文件的类型
这这里我们能看到在线网关支付,点击进去就能找到我们的demo
2.
下载我们的demo
3.
下载后的文件
在java版本的sdk里面可以看到我们的例子啦 但是里面是一个普通的web项目 我这边自己将他改造成自己的springboot项目
请仔细查看文件路径不要搞混了
改造后的boot项目如下图:
自己写的类有下面这个payImpl 和控制层类 因为是自己的测试接口就不写接口了,有需要自己添加
```java
package unitpay.lhl.impl;
import org.springframework.stereotype.Service;
import unitpay.lhl.sdk.AcpService;
import unitpay.lhl.sdk.DemoBase;
import unitpay.lhl.sdk.LogUtil;
import unitpay.lhl.sdk.SDKConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
/**
* @author
* @Date 2021/8/17 16:32
*/
@Service
public class payImpl {
/**
* 支付
*/
public String pay(HttpServletRequest req, HttpServletResponse resp)
throws IOException {
Map<String, String> requestData = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
//版本号,全渠道默认值
requestData.put("version", DemoBase.version);
//字符集编码,可以使用UTF-8,GBK两种方式
requestData.put("encoding", DemoBase.encoding);
//签名方法
requestData.put("signMethod", SDKConfig.getConfig().getSignMethod());
//交易类型 ,01:消费
requestData.put("txnType", "01");
//交易子类型, 01:自助消费
requestData.put("txnSubType", "01");
//业务类型,B2C网关支付,手机wap支付
requestData.put("bizType", "000201");
//渠道类型,这个字段区分B2C网关支付和手机wap支付;07:PC,平板 08:手机
requestData.put("channelType", "08");
/***商户接入参数***/
//商户号码,请改成自己申请的正式商户号或者open上注册得来的777测试商户号
requestData.put("merId", "777290058193047");
//接入类型,0:直连商户
requestData.put("accessType", "0");
//商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则
requestData.put("orderId", DemoBase.getOrderId());
//订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效
requestData.put("txnTime", DemoBase.getCurrentTime());
//交易币种(境内商户一般是156 人民币)
requestData.put("currencyCode", "156");
//交易金额,单位分,不要带小数点
requestData.put("txnAmt", "1234");
//前台通知地址 (需设置为外网能访问 http https均可),支付成功后的页面 点击“返回商户”按钮的时候将异步通知报文post到该地址
//如果想要实现过几秒中自动跳转回商户页面权限,需联系银联业务申请开通自动返回商户权限
//异步通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知
requestData.put("frontUrl", DemoBase.frontUrl);
//后台通知地址(需设置为【外网】能访问 http https均可),支付成功后银联会自动将异步通知报文post到商户上送的该地址,失败的交易银联不会发送后台通知
//后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知
//注意:1.需设置为外网能访问,否则收不到通知 2.http https均可 3.收单后台通知后需要10秒内返回http200或302状态码
// 4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200,那么银联会间隔一段时间再次发送。总共发送5次,每次的间隔时间为0,1,2,4分钟。
// 5.后台通知地址如果上送了带有?的参数,例如:http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签,否则将会验签失败
requestData.put("backUrl", DemoBase.backUrl);
/**请求参数设置完毕,以下对请求参数进行签名并生成html表单,将表单写入浏览器跳转打开银联页面**/
//报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
Map<String, String> submitFromData = AcpService.sign(requestData, DemoBase.encoding);
//获取请求银联的前台地址:对应属性文件acp_sdk.properties文件中的acpsdk.frontTransUrl
String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl();
//生成自动跳转的Html表单
String html = AcpService.createAutoFormHtml(requestFrontUrl, submitFromData, DemoBase.encoding);
LogUtil.writeLog("打印请求HTML,此为请求报文,为联调排查问题的依据:" + html);
//将生成的html写到浏览器中完成自动跳转打开银联支付页面;这里调用signData之后,将html写到浏览器跳转到银联页面之前均不能对html中的表单项的名称和值进行修改,如果修改会导致验签不通过
return html;
}
public String apppay(HttpServletRequest req, HttpServletResponse resp) {
String merId = req.getParameter("merId");
String txnAmt = req.getParameter("txnAmt");
String orderId = req.getParameter("orderId");
String txnTime = req.getParameter("txnTime");
Map<String, String> contentData = new HashMap<String, String>();
/***银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改***/
contentData.put("version", DemoBase.version); //版本号 全渠道默认值
contentData.put("encoding", DemoBase.encoding); //字符集编码 可以使用UTF-8,GBK两种方式
contentData.put("signMethod", SDKConfig.getConfig().getSignMethod()); //签名方法
contentData.put("txnType", "01"); //交易类型 01:消费
contentData.put("txnSubType", "01"); //交易子类 01:消费
contentData.put("bizType", "000201"); //填写000201
contentData.put("channelType", "08"); //渠道类型 08手机
/***商户接入参数***/
contentData.put("merId", "777290058193047"); //商户号码,请改成自己申请的商户号或者open上注册得来的777商户号测试
contentData.put("accessType", "0"); //接入类型,商户接入填0 ,不需修改(0:直连商户, 1: 收单机构 2:平台商户)
contentData.put("orderId", DemoBase.getOrderId()); //商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则
contentData.put("txnTime", DemoBase.getCurrentTime()); //订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效
contentData.put("accType", "01"); //账号类型 01:银行卡02:存折03:IC卡帐号类型(卡介质)
contentData.put("txnAmt", "1"); //交易金额 单位为分,不能带小数点
contentData.put("currencyCode", "156"); //境内商户固定 156 人民币
// 请求方保留域,
// 透传字段,查询、通知、对账文件中均会原样出现,如有需要请启用并修改自己希望透传的数据。
// 出现部分特殊字符时可能影响解析,请按下面建议的方式填写:
// 1. 如果能确定内容不会出现&={}[]"'等符号时,可以直接填写数据,建议的方法如下。
// contentData.put("reqReserved", "透传信息1|透传信息2|透传信息3");
// 2. 内容可能出现&={}[]"'符号时:
// 1) 如果需要对账文件里能显示,可将字符替换成全角&={}【】“‘字符(自己写代码,此处不演示);
// 2) 如果对账文件没有显示要求,可做一下base64(如下)。
// 注意控制数据长度,实际传输的数据长度不能超过1024位。
// 查询、通知等接口解析时使用new String(Base64.decodeBase64(reqReserved), DemoBase.encoding);解base64后再对数据做后续解析。
// contentData.put("reqReserved", Base64.encodeBase64String("任意格式的信息都可以".toString().getBytes(DemoBase.encoding)));
//后台通知地址(需设置为外网能访问 http https均可),支付成功后银联会自动将异步通知报文post到商户上送的该地址,【支付失败的交易银联不会发送后台通知】
//后台通知参数详见open.unionpay.com帮助中心 下载 产品接口规范 网关支付产品接口规范 消费交易 商户通知
//注意:1.需设置为外网能访问,否则收不到通知 2.http https均可 3.收单后台通知后需要10秒内返回http200或302状态码
// 4.如果银联通知服务器发送通知后10秒内未收到返回状态码或者应答码非http200或302,那么银联会间隔一段时间再次发送。总共发送5次,银联后续间隔1、2、4、5 分钟后会再次通知。
// 5.后台通知地址如果上送了带有?的参数,例如:http://abc/web?a=b&c=d 在后台通知处理程序验证签名之前需要编写逻辑将这些字段去掉再验签,否则将会验签失败
contentData.put("backUrl", DemoBase.backUrl);
/**对请求参数进行签名并发送http post请求,接收同步应答报文**/
Map<String, String> reqData = AcpService.sign(contentData, DemoBase.encoding); //报文中certId,signature的值是在signData方法中获取并自动赋值的,只要证书配置正确即可。
String requestAppUrl = SDKConfig.getConfig().getAppRequestUrl(); //交易请求url从配置文件读取对应属性文件acp_sdk.properties中的 acpsdk.backTransUrl
Map<String, String> rspData = AcpService.post(reqData, requestAppUrl, DemoBase.encoding); //发送请求报文并接受同步应答(默认连接超时时间30秒,读取返回结果超时时间30秒);这里调用signData之后,调用submitUrl之前不能对submitFromData中的键值对做任何修改,如果修改会导致验签不通过
/**对应答码的处理,请根据您的业务逻辑来编写程序,以下应答码处理逻辑仅供参考------------->**/
//应答码规范参考open.unionpay.com帮助中心 下载 产品接口规范 《平台接入接口规范-第5部分-附录》
if (!rspData.isEmpty()) {
if (AcpService.validate(rspData, DemoBase.encoding)) {
LogUtil.writeLog("验证签名成功");
String respCode = rspData.get("respCode");
if (("00").equals(respCode)) {
String tn = rspData.get("tn");
System.out.println("传输成功"+tn);
} else {
}
} else {
LogUtil.writeErrorLog("验证签名失败");
//TODO 检查验证签名失败的原因
}
} else {
//未返回正确的http状态
LogUtil.writeErrorLog("未获取到返回报文或返回http状态码非200");
}
String reqMessage = DemoBase.genHtmlResult(reqData);
String rspMessage = DemoBase.genHtmlResult(rspData);
System.out.println("请求"+reqMessage);
System.out.println("相应"+rspMessage);
return rspData.get("tn");
}
}
这里是自己写的控制层:
@RestController
@RequestMapping("/hello")
public class testcontroller {
@Autowired
payImpl payImpll;
//web端支付
@GetMapping("/hello")
public void hello(HttpServletRequest req, HttpServletResponse resp) throws IOException {
String pay = payImpll.pay(req, resp);
resp.getWriter().write(pay);
System.out.println("5656" + pay);
return;
}
//回调
@RequestMapping("/callBack")
public String callBack() {
System.out.println("后台已经接受到了");
return "您已经支付成功了";
}
//异步查询
@RequestMapping("asyncallBack")
public String asyncallBack() {
return "您已经异步支付成功了";
}
///app端支付///
@GetMapping("/appPay")
public String appPay(HttpServletRequest req, HttpServletResponse resp) {
String apppay = payImpll.apppay(req, resp);
return apppay;
}
4.测试
把这些页面信息丢给我们的浏览器 可以发生转跳 你也可以用浏览器访问你的接口
请求结果:
走到这里,哦嚯成功一大半啦!接着就是我们的通知和异步回调了
呐!这里的通知地址一定是外网可以请求的,不要问为什么,问就是官网要求的。为了方便测试我们可以用花生壳 或者natapp做穿透,将我们的本地ip暴漏在公网环境。
5.可能出现的问题
有的同学项目跑起来可能会报错证书加载不到 报null,请配置如下加载器
嗯 大概差不多 应该就是这么些接口咯!如果您有问题请留言!欢迎大家一起讨论。如果