文末可看支付效果。
做过支付的同学都知道,支付宝和微信支付只适合小额付款,如果遇到业务场景是大额支付的,还是得对接银联支付。
和其他支付平台一样,根据不同的移动端银联也提供了相应的支付渠道,比如在线网关支付、云闪付APP支付(原手机支付控件)、企业网银支付(商户版)、手机网页支付(WAP支付)、二维码支付等。
要对接平台接口第一步自然是要有账号,注册账号的过程就不多说了,放上官网自行注册。
(https://open.unionpay.com/)
选择需要对接的产品填写信息,提交入网申请,会有商务对接。
很友好的是,平台会提供测试参数用来调试接口,在个人中心-商户入网测试中心-测试参数,包括测试商户号、测试证书、测试地址。
有了这些东西,就可以开始写代码调试接口了,当然了,最让人舒心的是,平台也有提供SDK供你参考,实测了一下,确实只是仅供参考,因为跑不起来,哈哈😀。
代码虽然很简单,但要成功集成还是要花功夫的。
acp_sdk.properties配置文件主要放置一些证书文件,证书从商户入网测试中心下载。
########################## 入网测试环境交易发送地址 #############################
# 报文版本号,固定5.1.0
acpsdk.version=5.1.0
# 签名方式,证书方式固定01
acpsdk.signMethod=01
##测试交易请求地址
acpsdk.frontTransUrl=https://gateway.test.95516.com/gateway/api/frontTransReq.do
acpsdk.backTransUrl=https://gateway.test.95516.com/gateway/api/backTransReq.do
acpsdk.singleQueryUrl=https://gateway.test.95516.com/gateway/api/queryTrans.do
# 是否验证验签证书的CN,测试环境设置false,生产环境设置true。
acpsdk.ifValidateCNName=false
# 是否验证https证书,测试环境设置false,生产环境设置true。
acpsdk.ifValidateRemoteCert=false
######################### 入网测试环境签名证书配置 ################################
# 签名证书路径
acpsdk.signCert.path=/data/acp_test_sign.pfx
# 签名证书密码,测试环境固定000000
acpsdk.signCert.pwd=000000
# 签名证书类型,固定不需要修改
acpsdk.signCert.type=PKCS12
########################## 加密证书配置 ################################
# 敏感信息加密证书路径
acpsdk.encryptCert.path=/data/acp_test_enc.cer
########################## 验签证书配置 ################################
# 验签中级证书路径
acpsdk.middleCert.path=/data/acp_test_middle.cer
# 验签根证书路径
acpsdk.rootCert.path=/data/acp_test_root.cer
将acp_sdk.properties配置文件放到根目录下
再写个配置类UnionPayConfig,启动的时候加载证书内容
@Component
@Configuration
@EnableConfigurationProperties({UnionProperties.class})
public class UnionPayConfig {
private UnionProperties union;
public UnionPayConfig(UnionProperties union) {
this.union = union;
SDKConfig.getConfig().loadPropertiesFromSrc();
}
}
PC端唤起支付页面,注意交易金额,单位为分,不能带小数点,结果是以form表单的形式打开支付页面。
@GetMapping(value="webPay")
public void webPay(HttpServletResponse resp) throws IOException {
logger.info("银联电脑端支付");
UnionPayParam param = new UnionPayParam();
param.setOrderId(UnionConfig.getCurrentTime())
.setTxnTime(UnionConfig.getCurrentTime())
.setTxnAmt("330000")
.setReqReserved(param.getOrderId());
unionPayService.webPay000202(param, resp);
Map<String, String> data = new HashMap<>(29);
// 版本号,全渠道默认值
data.put("version", UnionConfig.version);
// 字符集编码,可以使用UTF-8,GBK两种方式
data.put("encoding", UnionConfig.encoding_UTF8);
// 签名方法
data.put("signMethod", SDKConfig.getConfig().getSignMethod());
// 交易类型 ,01:消费
data.put("txnType", "01");
// 交易子类型, 01:自助消费
data.put("txnSubType", "01");
// 业务类型 000202: B2B
data.put("bizType", "000202");
// 渠道类型 固定07
data.put("channelType", "07");
// 商户号码
data.put("merId", unionProperties.getMerId());
// 接入类型,0:直连商户
data.put("accessType", "0");
// 商户订单号,8-40位数字字母,不能含“-”或“_”,可以自行定制规则
data.put("orderId", param.getOrderId());
// 订单发送时间,取系统时间,格式为YYYYMMDDhhmmss,必须取当前时间,否则会报txnTime无效
data.put("txnTime", param.getTxnTime());
// 交易币种(境内商户一般是156 人民币)
data.put("currencyCode", "156");
// 交易金额,单位分,不要带小数点
data.put("txnAmt", CommonUtils.subZeroAndDot(param.getTxnAmt()));
// 业务场景,取值参考接口规范
data.put("bizScene", "110001");
// 收款方账户名称
data.put("payeeAcctNm", URLEncoder.encode(unionProperties.getPayeeAcctNm(),UnionConfig.encoding_UTF8));
// 收款方账号
data.put("payeeAcctNo", unionProperties.getPayeeAcctNo());
// 收款银行名称
data.put("payeeBankName", URLEncoder.encode(unionProperties.getPayeeBankName(),UnionConfig.encoding_UTF8));
// 前台通知地址
data.put("frontUrl", unionProperties.getFrontUrl());
// 后台通知地址
data.put("backUrl", unionProperties.getBackUrl());
// 订单超时时间
data.put("payTimeout", new SimpleDateFormat("yyyyMMddHHmmss").format(System.currentTimeMillis() + 15 * 60 * 1000));
// 请求方保留域, 内容不会出现&={}[]"'等符号时,可以直接填写数据
data.put("reqReserved", Base64.encodeBase64String(param.getReqReserved().getBytes(UnionConfig.encoding_UTF8)));
Map<String, String> reqData = AcpUtils.sign(data,UnionConfig.encoding_UTF8);
String requestFrontUrl = SDKConfig.getConfig().getFrontRequestUrl();
String html = AcpUtils.createAutoFormHtml(requestFrontUrl,reqData,UnionConfig.encoding_UTF8);
logger.info("打印请求HTML,此为请求报文:{}", html);
resp.getWriter().write(html);
和其他支付方式一样,支付结果以异步通知告知开发者,在这里处理业务逻辑,比如更改订单状态为已支付。
@PostMapping(value="unionPayNotify")
public void unionPayNotify(HttpServletRequest request, HttpServletResponse response) throws Exception {
logger.info("银联接收后台通知开始");
String encoding = request.getParameter(SDKConstants.param_encoding);
// 获取银联通知服务器发送的后台通知参数
Map<String, String> reqParam = AcpUtils.getAllRequestParam(request);
Map<String, String> valideData = new HashMap<>();
if (!reqParam.isEmpty()) {
Iterator<Map.Entry<String, String>> it = reqParam.entrySet().iterator();
valideData = new HashMap<>(reqParam.size());
while (it.hasNext()) {
Map.Entry<String, String> e = it.next();
String key = e.getKey();
String value = e.getValue();
value = new String(value.getBytes(encoding), encoding);
valideData.put(key, value);
}
}
if (!AcpUtils.validate(valideData, encoding)) {
logger.info("银联验证签名结果[失败].");
} else {
logger.info("银联回调结果参数=====:{}", JSON.toJSONString(valideData));
logger.info("银联验证签名结果[成功].");
AsyncNotifyResult result = JSON.parseObject(JSON.toJSONString(valideData), AsyncNotifyResult.class);
// 应答码
String respCode = result.getRespCode();
// 应答信息
String respMsg = result.getRespMsg();
if (StringUtils.equals("00", respCode) && StringUtils.equals("成功[0000000]", respMsg)) {
// 商户订单号
String orderId = result.getOrderId();
// 银联查询流水号
String queryId = result.getQueryId();
// 银联订单发送时间
String txnTime = result.getTxnTime();
String txnAmt = result.getTxnAmt();
// 辅助信息
String reqReserved = result.getReqReserved();
logger.info("reqReserved=====:{}", new String(Base64.decodeBase64(reqReserved), UnionConfig.encoding_UTF8));
// TODO
// 银联没有返回付款时间,系统自己生成
logger.info("处理业务逻辑,orderId:{},更新queryId:{},txnTime:{}=====", orderId, queryId, txnTime);
}
response.getWriter().print("ok");
}
}
看一下效果
这是测试环境,选择卡号,输入验证码,点击提交
如果需要代码
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海
上一篇:支付宝小程序模板开发,一整套流程