银联支付,实现代码对接

1,095 阅读3分钟

文末可看支付效果。

做过支付的同学都知道,支付宝和微信支付只适合小额付款,如果遇到业务场景是大额支付的,还是得对接银联支付。

和其他支付平台一样,根据不同的移动端银联也提供了相应的支付渠道,比如在线网关支付、云闪付APP支付(原手机支付控件)、企业网银支付(商户版)、手机网页支付(WAP支付)、二维码支付等。

要对接平台接口第一步自然是要有账号,注册账号的过程就不多说了,放上官网自行注册。(open.unionpay.com/)

image.png 选择需要对接的产品填写信息,提交入网申请,会有商务对接。

image.png 很友好的是,平台会提供测试参数用来调试接口,在个人中心-商户入网测试中心-测试参数,包括测试商户号、测试证书、测试地址。

image.png 有了这些东西,就可以开始写代码调试接口了,当然了,最让人舒心的是,平台也有提供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配置文件放到根目录下

image.png

再写个配置类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");
        }
    }

看一下效果

image.png 这是测试环境,选择卡号,输入验证码,点击提交

image.png

image.png

image.png

如果需要代码,

银联支付,实现代码对接

山水有相逢,来日皆可期,谢谢阅读,我们再会

我手中的金箍棒,上能通天,下能探海

上一篇:支付宝小程序模板开发,一整套流程