2025年Java接入银联支付ChinaPay

Java接入银联支付ChinaPay一 介绍 中国银联是中国银行卡联合组织 通过银联跨行交易清算系统 实现商业银行系统间的互联互通和资源共享 保证银行卡跨行 跨地区和跨境的使用 效果如下 二 接入准备 当了解了银联支付后 首先需要和银联方联系 会有相关的工作人员提供相关支持

大家好,我是讯享网,很高兴认识大家。

一、介绍

        中国银联是中国银行卡联合组织,通过银联跨行交易清算系统,实现商业银行系统间的互联互通和资源共享,保证银行卡跨行、跨地区和跨境的使用。

        效果如下:


讯享网

二、接入准备

        当了解了银联支付后,首先需要和银联方联系,会有相关的工作人员提供相关支持,工作人员会发送邮件提供开发文档和相关文件。

  • 开发文档:

        邮件会提供中英两种文档,ChinaPay_新一代_商户接入手册_.pdf 和ChinaPay Merchants Integration Guidence .pdf,详细的对接相关介绍、流程图、入参介绍、返回值介绍等都会在文档中给出。

  • 网关公钥:

        .cer,邮件中会给出网关公钥

  • 交易证书和相关密码:

        .pfx和密码,交易证书需要在银联商务商户服务管理系统申请,银联的对接相关工作人员会给出具体的申请文档,文档中步骤很详细,这里就不赘述了

  • 私钥:

        *.sm2,私钥在申请交易证书后导出时一同导出

  • 插件包:

        邮件中会给出多种语言对接所需的相关插件,这里仅描述Java的NetPayClient for JAVA.zip,在解压后里面会有相关jar包和securitySM.properties的一个文件

  • 测试账号

       可以在 FAQ列表- 中国银联开放平台里获取

  • IP白名单

        需要和银联工作人员联系,发送邮件给出相关商户号和出口IP进行配置

  • 开发工具

        这里我使用的是idea进行的开发,后续示例也仅以idea的操作示例

三、B2C、B2B、无卡支付交易流程 

 支付流程说明

1、 用户在商户的页面下单,选择支付时选择银联 ChinaPay 的支付方式进行支付

2、 商户支付系统按照银联支付接口提供入参组织报文,拼接请求地址返回到前端跳转到银联支付页面

3、 银联ChinaPay 收到商户提交的报文之后,首先进行验签;在验签通过后,跳到银联页面选择网银支付

5、 用户输入信息进行交易

6、 交易成功后,银联ChinaPay 通知商户交易结果

7、 商户收到交易成功之后,显示购买结果给用户

 查询流程说明

1、 商户调用查询订单状态接口查询

2、 银联同步返回订单支付状态给商户

退款流程说明

1、 用户在商户页面选择订单发起退款

2、 商户调用退款接口发起退款

3、 银联ChinaPay 收到商户的退款请求之后,返回商户“1003”状态,代表退款接收成功

4、 银联ChinaPay 在 T+1 日进行轧差,如果为正,则向银行或者银联发起退款,如果为负,则在下一日继续轧差

5、 银行或银联收到 ChinaPay 的退款指令之后进行退款操作

6、 银行或银联通知 ChinaPay 退款结果

7、 银联ChinaPay 通知商户退款结果

8、 商户更新退款结果

9、 用户收到退款

此处相关流程图和支付流程说明在开发文档中会有给出,在这仅简单描述

四、接入示例

1、首先本地开发需要导入银联ChinaPay提供的jar包,jar包会在银联ChinaPay邮件中的插件包里的NetPayClient for JAVA.zip中给出,这里给出两种本地开发导入jar包操作方式,后续上线的话则需要将jar包导入到公司对应的maven仓库,这个就另说了。

第一种是选择File------>Project Structure------>Project Settings下的Modules------>Dependencies,点击右侧或是下侧的加号,也可以用快捷键Alt+Insert,选择JARs or directories,选择相应的jar包导入,点击Apply后就是一直ok就可以了,下面是截图示例。

选择File------>Project Structure

 Project Settings下的Modules------>Dependencies

点击加号后选择JARs or directories

选择下载好的jar包 

之后就是点击Apply后一直ok就可以了

第二种导入方式是通过maven的命令导入

命令如下:

install:install-file -Dfile=D:\chinapay\chinapaysecure1_5.jar -DgroupId=com.chinapay.secure -DartifactId=chinapay-sdk -Dversion=1.5.0 -Dpackaging=jar

讯享网

pom 文件引入:

讯享网<dependency> <groupId>com.chinapay.sdk</groupId> <artifactId>chinapay-sdk</artifactId> <version>1.5.0</version> </dependency>

两种方法选择一种即可

2、将提前准备好的网关公钥,私钥和交易证书添加到项目中,一般来说是在resources下创建一个文件夹,文件夹中添加这些材料,然后创建一个*.properties文件,后续创建支付、退款和查询状态的操作签名时都需要读取这个文件,通过这个文件读取相关的证书等材料。

这里的路径可以自己随意定义, *.properties就是为了读取证书和一些配置,所以其他证书的路径写在了*.properties文件中

3、示例代码

创建支付

public String getPayUrl(PayArguments payArgument) throws Exception { NumberFormat format = NumberFormat.getInstance(); format.setMaximumFractionDigits(0); String amount = format.format(payArgument.Amount * 100); amount = amount.replace(",", ""); Map<String, String> map = new HashMap<>(); map.put("Version", ""); map.put("MerId", config.merId); map.put("MerOrderNo", payArgument.PayInfoId + ""); map.put("TranDate", TimeUtil.DateToString(new Date(), "yyyyMMdd")); map.put("TranTime", TimeUtil.DateToString(new Date(), "HHmmss")); map.put("OrderAmt", amount); map.put("BusiType", "0001"); map.put("MerBgUrl", config.notifyUrl); map.put("Signature", this.sign(map)); return config.payUrl + "?" + getPara(map); } 

 查询状态

讯享网public Map<String, String> getPayStatus(String MerOrderNo) throws Exception { Map<String, String> map = new HashMap<>(); map.put("Version", ""); map.put("MerId", config.merId); map.put("MerOrderNo", MerOrderNo); map.put("TranDate", TimeUtil.DateToString(new Date(), "yyyyMMdd")); map.put("TranType", "0502"); map.put("BusiType ", "0001"); map.put("Signature", this.sign(map)); FormBody.Builder builder = new FormBody.Builder(); for (Map.Entry<String, String> temp : map.entrySet()) { builder.add(temp.getKey(), temp.getValue()); } OkHttpClient client = new OkHttpClient().newBuilder().readTimeout(60, TimeUnit.SECONDS).build(); FormBody formBody = builder.build(); Request request = new Request.Builder() .url(config.payQueryUrl) .post(formBody) .build(); Response response = client.newCall(request).execute(); byte[] responseBytes = response.body().bytes(); String resStr = new String(responseBytes, "UTF-8"); Map<String, String> resMap = new HashMap<>(); String[] resArr = resStr.split("&"); for (int i = 0; i < resArr.length; i++) { String[] kv = resArr[i].split("="); resMap.put(kv[0], kv[1]); } return resMap; }

 退款

public Map<String, String> refund(double refundAmount, String merOrderNo,                 String oriOrderNo, Date oriTranDate) throws Exception { NumberFormat format = NumberFormat.getInstance(); format.setMaximumFractionDigits(0); String amount = format.format(refundAmount * 100); amount = amount.replace(",", ""); Map<String, String> map = new HashMap<>(); map.put("Version", ""); map.put("MerId", config.merId); map.put("MerOrderNo", merOrderNo); map.put("TranDate", TimeUtil.DateToString(new Date(), "yyyyMMdd")); map.put("TranTime", TimeUtil.DateToString(new Date(), "HHmmss")); map.put("OriOrderNo", oriOrderNo); map.put("OriTranDate", TimeUtil.DateToString(oriTranDate, "yyyyMMdd")); map.put("TranType", "0401"); map.put("RefundAmt", amount); map.put("BusiType ", "0001"); map.put("Signature", this.sign(map)); OkHttpClient client = new OkHttpClient().newBuilder().readTimeout(60, TimeUnit.SECONDS).build(); FormBody.Builder formBodyBuilder = new FormBody.Builder(); for (Map.Entry<String, String> temp : map.entrySet()) { formBodyBuilder.add(temp.getKey(), temp.getValue()); } FormBody formBody = formBodyBuilder.build(); Request request = new Request.Builder() .url(config.refundUrl) .post(formBody) .build(); Response response = client.newCall(request).execute(); byte[] responseBytes = response.body().bytes(); String resStr = new String(responseBytes, "UTF-8"); Map<String, String> resMap = new HashMap<>(); String[] resArr = resStr.split("&"); for (int i = 0; i < resArr.length; i++) { String[] kv = resArr[i].split("="); resMap.put(kv[0], kv[1]); } return resMap; } 
讯享网private String getPara(Map<String, String> paraMap) { StringBuilder sb = new StringBuilder(); for (Map.Entry item : paraMap.entrySet()) { sb.append(item.getKey()); sb.append("="); sb.append(item.getValue()); sb.append("&"); } sb.deleteCharAt(sb.length() - 1); return sb.toString(); }

 此处propPath为*.properties的文件路径

private String sign(Map<String, String> map) { SecssUtil secssUtil = new SecssUtil(); secssUtil.init(config.propPath); secssUtil.sign(map); return secssUtil.getSign(); }

五、总结

        总结一下,在接入银联支付的话需要准备相应的各种材料和参数,否则在对接的时候会因为各种问题导致进度缓慢,例如缺少证书始终签名异常,文档一些字段在某些情况下是否是必填的,让银联先关工作人员配合排查原因,但是因为沟通理解错误导致效率低下等等原因,另外开发时也应先询问过是否需要绑定出口IP白名单,否则开发完调试发现,导致申请绑定IP白名单走流程又耽误好几天,总之接入银联支付我所想到或经历的大概就是这些了,希望看见这篇的人可以给出一些参考,进而避免踩一些坑,如果我哪里存在错误也欢迎评论指正。

小讯
上一篇 2025-02-19 20:24
下一篇 2025-04-05 09:26

相关推荐

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容,请联系我们,一经查实,本站将立刻删除。
如需转载请保留出处:https://51itzy.com/kjqy/37782.html