数字营销
标准API开放接口规范
1.文档说明
2.接口规则
3.标准接口定义
会员营销
3.1.1会员账户信息查询
3.1.2会员充值
3.1.13第三方优惠试算
3.1.5会员注册
3.1.6统一登录注册
3.1.7会员商户优惠试算
3.1.10会员基本信息查询
3.1.16会员绑储值卡
3.1.17会员实名
3.1.18会员充值查询
3.1.21会员绑定实体卡(开卡)
3.1.22会员卡密码修改
3.1.23会员调账
3.1.24会员调账查询
3.1.25会员状态变更
3.1.26发送短信验证码
3.1.28会员消费查询
3.1.29会员消费撤销
3.1.30会员消费撤销查询
3.1.31会员无密码消费
3.1.32会员消费
3.1.34会员信息更新
3.1.35会员账户流水
3.1.37 获取用户基本信息(OAuth2)
3.1.38 获取基础营销授权链接
3.3.1 H5页面调用绑定微信和手机号(GET)
3.3.2 H5页面调用绑定微信和外部顾客编号(GET)
卡券营销
3.2.1卡/券查询
3.2.2发卡/券
3.2.4卡/券作废
3.2.5卡/券核券撤销
3.2.7发卡/券(套券)
3.2.8发卡/券查询
3.2.9作废优惠券发券
3.2.10储值卡充值
3.2.11储值卡充值查询
3.2.12实体储值卡激活(单张)
3.2.13可发卡/券列表查询(套)
3.2.14卡/券批量查询
3.2.15卡/券鉴权
3.2.16卡/券动态二维码生成
3.2.17卡/券转赠
3.2.18卡/券发放查询
3.2.19卡/券核销查询
3.2.20卡/券核销撤销查询
3.2.21卡/券模板详情查询
3.2.22 卡/券交易流水查询
3.2.23储值卡调账
3.2.24储值卡调账查询
3.2.25 卡/券交易流水分页查询
3.2.26 作废卡/券发放
3.2.27卡/券核销
3.2.33卡/券动账流水
3.2.35券号查询该顾客同券模板下的券号列表
3.2.36卡/券冻结
3.2.45卡券延期
外部平台券
3.2.28第三方核券准备接口
3.2.29第三方核券接口
3.2.30第三方核券流水查询接口
3.2.31第三方撤销核券接口
3.2.32第三方券码查询接口
3.2.34第三方门店验券历史接口
3.2.35券号查询该顾客同券模板下的券号列表
3.2.38美团到店综合预约数据
3.2.39美团到店综合消费数据
3.2.40美团到店综合团单消费详情数据
3.2.41美团到店综合门店流量数据
3.2.42美团餐饮门店套餐映射
3.2.43美团到店综合门店商品信息
3.2.44抖音商品线上数据
3.2.46抖音综合提现记录查询
3.2.47抖音综合账单详情查询
3.2.48抖音订单查询
3.2.49抖音账单详细查询
3.2.50抖音券状态批量查询
订单同步
3.5.1外部订单同步
3.5.2外部订单查询
3.5.3外部订单同步撤销
3.5.17外部订单核销
3.5.18外部订单查询
3.5.19外部订单撤销
订单支付
3.5.9订单查询
3.5.10订单退款
3.5.11退款订单查询
3.5.12统一扫码
3.5.13终端信息采集
3.5.14统一支付
3.5.15 ISV收银台
3.5.16生成订单
3.5.20终端信息查询
3.5.23分账确认
3.5.24分账确认结果查询
7.1终端信息字段说明
7.2 splitList字段说明
账户类
3.9.1 资金管理余额查询
3.9.3 资金管理提现
3.9.4 资金管理提现结果查询
通知相关
3.10通知相关
5.1券核销通知
5.2会员注册/修改通知
5.3订单支付通知
5.4券核销撤销通知
5.5跨机构订单支付通知
5.6微信核销通知转发
5.7发券通知
5.8卡券充值通知
5.9支付宝核销通知转发
5.10 卡券变动通知
5.11 实体卡入库通知
5.13顾客注册/修改通知
5.15提现结果通知
5.16资金管理小b开通通知
对账文件
6.1对账文件
6.2 分账对账流水文件下载
6.3 对账流水文件下载
机构管理类
3.8.1外部门店信息同步
3.8.2 创建/修改机构用户
3.8.3 查询机构用户信息
3.8.4 机构统计信息查询
附录
7.1终端信息字段说明
7.2 splitList字段说明
7.3 店铺经营类型枚举
7.6 支付方式说明
7.7 paytypeinfo-支付方式参数说明
7.9渠道交易类型(trxCode)
7.10交易返回码(trxStatus)
7.11扩展参数说明
4.1 公共返回码(code)
4.开发参考
4.1 SDK参考概述
4.2 Java SDK
4.3 Python SDK
4.4 PHP SDK
一码付开放接口规范
1.接口说明
2.接口规则
3.标准API
3.1统一扫码
3.2统一撤销
3.3统一退款
3.4统一查询
3.5交易结果通知
3.6订单生成
3.7终端信息采集报备
3.8统一支付
3.9资金管理余额查询
3.10资金管理提现
3.11资金管理提现结果查询
3.12资金管理转账
3.13资金管理转账结果查询
3.14终端信息查询
3.15订单关闭
3.16ISV收银台
3.17ISV前端跳转
3.18对账流水文件下载
3.19根据授权码(付款码)获取用户ID
3.20资金管理提现通知
3.21分账/收款确认
3.22分账/收款确认结果查询
3.23资金管理转账通知
3.24资金管理批量转账
3.25资金管理批量转账结果查询
3.26B2B订单支付收款
3.27B2B订单支付付款
3.31代付
3.32代付订单查询
3.33资金管理交易文件查询
3.34收支明细查询
3.35交易列表查询
4.附录
4.1 接口返回码retcode说明
4.2 交易返回码trxstatus说明
4.3 交易类型
4.4 支付渠道优惠信息填写规范(benefitdetail字段)
4.5 拓展参数说明(extendparams)
4.6 终端信息字段说明
4.7 asinfo字段说明
4.8 交易方式
4.9 splitlist字段说明
4.10支付方式
4.11paytypeinfo-支付方式参数说明
4.12民生/平安银行转账子账簿信息
5.13微信订单支付(小程序资金管控)接入说明
5.开发参考
5.1 SDK参考概述
5.2 Java SDK
5.3 C SDK
5.4 PHP SDK
通惠API开放接口规范
1. 文档说明
2. 接口规则
3.标准接口定义
3.1会员信息类
3.3页面调用类
3.4 银行活动类
3.7 互动营销类
3.8 机构管理类
4.公共返回码
5.通知相关
6.对账文件类
7.开发参考
8.接口权限申请表下载
9.常见问题合集
通联核心产品文档中心
-
+
首页
4.2 Java SDK
<div hidden="">AI抽取开始标记</div> # Java SDK 使用指南 通联支付数字营销平台Java SDK旨在降低对接门槛与成本,提供了一套功能完整的开发工具包,以帮助开发者快速完成平台集成。 ### 前提条件 1. 已开通数字营销机构,详情请咨询业务对接人员。 2. 已获得接口对接参数,详情请咨询业务对接人员。 ### 安装 SDK #### 在 Maven 项目中加入依赖项(推荐方式) 在 Maven 工程中使用 Java SDK,只需在 pom.xml 中加入相应依赖即可。以 1.0.0 版本为例,步骤如下: 在 pom.xml 的 <dependencies> 中加入如下内容: ```xml <dependency> <groupId>com.allinpay.cus.sdk</groupId> <artifactId>allinpay-shopoint-sdk-java</artifactId> <version>1.0.0</version> </dependency> ``` 特殊情况处理:在无法访问 Maven 中央仓库的情况下,先下载 JAR 包(下载地址:[点击下载](https://repo1.maven.org/maven2/com/allinpay/cus/sdk/allinpay-shopoint-sdk-java/1.0.0/allinpay-shopoint-sdk-java-1.0.0.jar)),再将 JAR 安装到 Maven 仓库,使用如下命令: ```bash mvn install:install-file -Dfile=.\allinpay-shopoint-sdk-java-1.0.0.jar -DgroupId=com.allinpay.cus.sdk -DartifactId=allinpay-shopoint-sdk-java -Dversion=1.0.0 -Dpackaging=jar ``` ### 示例代码 #### 调用业务接口(RSA2) 本示例中,采用单元测试的方式演示如何使用RSA2签名的方式完成对业务接口allinpay.shopoint.memberService.memberQuery 的调用。示例如下: ```java /** * 单元测试:使用RSA2签名方式调用接口示例 */ @Test public void testRSA2Simple() { // 数字营销接口地址:测试环境=https://dms-api-test.shopoint.cn/shopoint-openapis-web/apis/v3,生产环境=https://dms-api.shopoint.cn/shopoint-openapis-web/apis/v3 String endpoint = "https://dms-api-test.shopoint.cn/shopoint-openapis-web/apis/v3"; // 数字营销分配的appid String appId = "7qq4RU5*****"; // 数字营销的RSA2公钥 String providerPublicKey = "MIGfMA0GCSqGS******"; // 对接方的RSA私钥 String callerPrivateKey = "MIIEvQIBADAN******"; // 接口调用凭证(RSA2) CredentialsProvider credsProvider = new RSA2CredentialsProvider(appId, providerPublicKey, callerPrivateKey); // 接口调用client OpenApisClient client = new OpenApisClient(endpoint, credsProvider); Map<String, String> paramMap = new HashMap<String, String>(); paramMap.put("idType", "1"); paramMap.put("memberId", "136****4175"); try { String method = "allinpay.shopoint.memberService.memberQuery"; String bizContent = new Gson().toJson(paramMap); String version = "1.0"; GenericResult result = client.requestApis(method, bizContent, version); System.out.println(String.format("testRSA2Simple:%s", new Gson().toJson(result))); assertEquals("0", result.getCode()); } catch (Exception e) { LogUtils.logException("testRSA2Simple:", e); fail(); } } ``` #### 调用业务接口(SM2) 本示例中,采用单元测试的方式演示如何使用SM2签名的方式完成对业务接口allinpay.shopoint.memberService.memberQuery 的调用。示例如下: ```java /** * 单元测试:使用SM2签名方式调用接口示例 */ @Test public void testSM2Simple() { // 数字营销接口地址:测试环境=https://dms-api-test.shopoint.cn/shopoint-openapis-web/apis/v3,生产环境=https://dms-api.shopoint.cn/shopoint-openapis-web/apis/v3 String endpoint = "https://dms-api-test.shopoint.cn/shopoint-openapis-web/apis/v3"; // 数字营销分配的appid String appId = "VRcuSC******"; // 数字营销的SM2公钥 String provider2PublicKey = "BA75235505******"; // 对接方的SM2私钥 String callerPrivateKey = "20494FD3045FF******"; // 接口调用凭证(SM2) CredentialsProvider credsProvider = new SM2CredentialsProvider(appId, provider2PublicKey, callerPrivateKey); // 接口调用client OpenApisClient client = new OpenApisClient(endpoint, credsProvider); Map<String, String> paramMap = new HashMap<String, String>(); paramMap.put("idType", "1"); paramMap.put("memberId", "136****4175"); try { String method = "allinpay.shopoint.memberService.memberQuery"; String bizContent = new Gson().toJson(paramMap); String version = "1.0"; GenericResult result = client.requestApis(method, bizContent, version); System.out.println(String.format("testSM2Simple:%s", new Gson().toJson(result))); assertEquals("0", result.getCode()); } catch (Exception e) { LogUtils.logException("testSM2Simple:", e); fail(); } } ``` #### 获取3.3.1页面跳转URL 本示例中,采用单元测试的方式演示如何获取3.3.1页面跳转URL(与3.3.2类似)。示例如下: ```java /** * 单元测试:获取3.3.1页面跳转URL,实现微信公众号绑定、手机号绑定、会员标签、等级绑定等 */ @Test public void testGetBindWxUrl() { // 生产环境:https://dms.shopoint.cn/wxmall/index.html#/BindWx String bindWxEndpoint = "https://dms-test.shopoint.cn/wxmall/index.html#/BindWx"; // 数字营销分配的appid String appId = "7qq4RU******"; // 数字营销的RSA2公钥 String providerPublicKey = "MIGfMA0G******"; // 对接方的RSA私钥 String callerPrivateKey = "MIIEvQIBAD******"; // 接口调用凭证(RSA2) CredentialsProvider credsProvider = new RSA2CredentialsProvider(appId, providerPublicKey, callerPrivateKey); OpenApisClient bindWxClient = new OpenApisClient(bindWxEndpoint, credsProvider, new ClientConfiguration()); Map<String, String> bizContent = new HashMap<String, String>(); bizContent.put("mobile", "136****5678"); bizContent.put("redirectUrl", "https://www.baidu.com"); bizContent.put("tags", "tag1,tag2,tag3"); bizContent.put("outCustomerLevel", "v1"); try { String method = "allinpay.shopoint.memberService.bindWx"; String version = "1.0"; String result = bindWxClient.getRequestUrl(method, bizContent, version); System.out.printf("testGetBindWxUrl:%s%n", result); assertNotNull(result); } catch (Exception e) { LogUtils.logException("testGetBindWxUrl:", e); fail(e.getMessage()); } } ``` #### 调用业务接口(使用代理) 本示例中,采用单元测试的方式演示如何使用RSA2签名的方式完成对业务接口allinpay.shopoint.memberService.memberQuery 的调用,同时演示了使用正向代理的相关代码。示例如下: ```java /** * 单元测试:使用正向代理示例 */ @Test public void testUseProxy() { // 数字营销接口地址:测试环境=https://dms-api-test.shopoint.cn/shopoint-openapis-web/apis/v3,生产环境=https://dms-api.shopoint.cn/shopoint-openapis-web/apis/v3 String endpoint = "https://dms-api-test.shopoint.cn/shopoint-openapis-web/apis/v3"; // 数字营销分配的appid String appId = "7qq4RU******"; // 数字营销的RSA2公钥 String providerPublicKey = "MIGfMA0GCSq******"; // 对接方的RSA私钥 String callerPrivateKey = "MIIEvQIBADANB******"; // 接口调用凭证(RSA2) CredentialsProvider credsProvider = new RSA2CredentialsProvider(appId, providerPublicKey, callerPrivateKey); ClientConfiguration config = new ClientConfiguration(); config.setProxyHost("127.0.0.1"); // 代理地址(域名或IP) config.setProxyPort(1080); // 代理端口 config.setProxyUsername("***"); // 代理账号 config.setProxyPassword("***"); // 代理密码 // 其他配置,如:连接超时、读取超时等请参考ClientConfiguration中的setter OpenApisClient proxyClient = new OpenApisClient(endpoint, credsProvider, config); Map<String, String> paramMap = new HashMap<String, String>(); paramMap.put("idType", "1"); paramMap.put("memberId", "136****4175"); try { String method = "allinpay.shopoint.memberService.memberQuery"; String bizContent = new Gson().toJson(paramMap); String version = "1.0"; GenericResult result = proxyClient.requestApis(method, bizContent, version); System.out.println(String.format("testUseProxy:%s", new Gson().toJson(result))); assertEquals("0", result.getCode()); } catch (Exception e) { LogUtils.logException("testUseProxy:", e); fail(e.getMessage()); } } ``` #### 接收并处理通知 本示例中,创建一个OpenApisNotifyControllerDemo.java文件,演示了**接收并处理数字营销的通知消息**。示例如下: ```java import java.io.InputStream; import java.nio.charset.StandardCharsets; import java.security.KeyFactory; import java.security.PublicKey; import java.security.Signature; import java.security.spec.X509EncodedKeySpec; import java.util.HashMap; import java.util.Map; import java.util.SortedMap; import java.util.TreeMap; import javax.servlet.http.HttpServletRequest; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.cloud.context.config.annotation.RefreshScope; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; import com.google.gson.Gson; import cn.hutool.core.codec.Base64; import cn.hutool.core.net.URLDecoder; import cn.hutool.core.util.StrUtil; import cn.hutool.crypto.BCUtil; import cn.hutool.crypto.Mode; import cn.hutool.crypto.Padding; import cn.hutool.crypto.asymmetric.AsymmetricAlgorithm; import cn.hutool.crypto.asymmetric.KeyType; import cn.hutool.crypto.asymmetric.RSA; import cn.hutool.crypto.asymmetric.SM2; import cn.hutool.crypto.asymmetric.Sign; import cn.hutool.crypto.asymmetric.SignAlgorithm; import cn.hutool.crypto.symmetric.AES; import cn.hutool.crypto.symmetric.SM4; /** * 接收数字化营销平台的通知消息 (示例代码,仅供参考) * 部分第三方依赖包如下(可使用Maven或其他方式加入相关依赖): * hutool-core-5.8.18.jar * gson-2.8.5.jar * commons-io-2.4.jar * hutool-crypto-5.8.18.jar */ @RefreshScope @RestController public class OpenApisNotifyControllerDemo { private static Logger log = LoggerFactory.getLogger(OpenApisNotifyControllerDemo.class); private static final String RSA_SIGNTYPE = "SHA256WithRSA"; private static final String RSA2_SIGNTYPE = "RSA2"; private static final String SM2_SIGNTYPE = "SM2"; private static final String F_BIZCONTENT = "bizContent"; private static final String F_TOKEN = "token"; // 服务提供方的(RSA/RSA2/SM2)公钥 @Value("${demo.notify.providerPublicKey:服务提供方的公钥}") private String providerPublicKey; // 对接方自己的私钥 @Value("${demo.notify.callerPrivateKey:对接方的私钥}") private String callerPrivateKey; // 对接方正确处理通知消息的情况下给数字营销返回的code值,如果code不等于该值,则数字营销系统会按照重试规则(详见《数字营销开放接口》文档:5.通知相关-通知重发机制)进行通知消息的重发 private static final String SUCCESS_CODE = "10000"; @PostMapping("/shopoint/openapis/notify") @ResponseBody public Map<String, String> notify(HttpServletRequest request) { Map<String, String> result = new HashMap<String, String>(); result.put("code", SUCCESS_CODE); try { // 获取请求中的所有参数 TreeMap<String, String> params = ServletUtils.getServletParamterMap(request); if (log.isDebugEnabled()) { log.debug("接收到的通知报文:{}", new Gson().toJson(params)); } // 验证签名+报文解密 boolean verify = false; String signType = params.get("signType"); if (RSA_SIGNTYPE.equalsIgnoreCase(signType)) { verify = RSAUtils.verify(params, providerPublicKey); } else if (SM2_SIGNTYPE.equalsIgnoreCase(signType)) { /** 报文全加密模式 */ verify = SM2Utils.verify(params, providerPublicKey); // 令牌解密 byte[] token = SM2Utils.decrypt(Base64.decode(params.get(F_TOKEN)), callerPrivateKey); // 报文解密 params.put(F_BIZCONTENT, SM4Utils.decryptStr(params.get(F_BIZCONTENT), token)); } else if (RSA2_SIGNTYPE.equalsIgnoreCase(signType)) { /** 报文全加密模式 */ verify = RSA2Utils.verify(params, providerPublicKey); // 令牌解密 byte[] token = RSA2Utils.decrypt(Base64.decode(params.get(F_TOKEN)), callerPrivateKey); // 报文解密 params.put(F_BIZCONTENT, AESUtils.decryptStr(params.get(F_BIZCONTENT), token)); } else { log.error("无法识别的signType:{}", signType); } if (!verify) { result.put("code", "301"); result.put("msg", "验签失败"); return result; } // 对通知消息进行业务处理 if (!handleNotify(params)) { result.put("code", "503"); result.put("msg", "通知消息处理失败"); return result; } } catch (Exception e) { log.error(e.getMessage(), e); result.put("code", "500"); result.put("msg", "通知消息处理失败,error:" + e.getMessage()); } finally { if (log.isDebugEnabled()) { log.debug("接收到通知后的响应报文:{}", new Gson().toJson(result)); } } return result; } /** * 处理通知消息 * * @param params * @return */ private boolean handleNotify(TreeMap<String, String> params) { if (log.isDebugEnabled()) { log.debug("待处理通知报文:{}", new Gson().toJson(params)); } // 通知消息method String method = params.get("method"); // TODO 需要处理的目标通知method,详见《数字营销开放接口》文档 5.通知相关 String targetMethod = ""; if (targetMethod.equals(method)) { // TODO 具体业务处理逻辑,处理成功返回true,处理失败返回false(为了分析处理失败的原因,建议由对接方自行输出调试日志) // 注意:对接方需自行保证接口幂等特性,以便正确处理通知消息重复发送的情况 return true; } return true; } } class RSAUtils { static Logger log = LoggerFactory.getLogger(RSAUtils.class); /** * RSA公钥验签方法 * * @param params * @param publicKeyString * @return */ public static boolean verify(TreeMap<String, String> params, String publicKeyString) { String sign = ""; try { sign = params.get("sign"); params.remove("sign"); params.remove("signType"); Signature signature = Signature.getInstance("SHA256withRSA"); PublicKey publicKey = KeyFactory.getInstance("RSA") .generatePublic(new X509EncodedKeySpec(Base64.decode(publicKeyString))); signature.initVerify(publicKey); signature.update(Tools.treeMapToString(params).getBytes(StandardCharsets.UTF_8)); return signature.verify(Base64.decode(sign)); } catch (Exception e) { log.error(e.getMessage(), e); return false; } } } final class SM2Utils { private static Logger log = LoggerFactory.getLogger(SM2Utils.class); public static final String SIGN = "sign"; public static final String SIGN_TYPE = "signType"; /** * 验签 * * @param params * @param publicKeyHex * @return */ public static boolean verify(SortedMap<String, String> params, String publicKeyHex) { String signBase64 = params.get(SIGN); // 剔除不参与签名的参数 params.remove(SIGN); params.remove(SIGN_TYPE); StringBuilder sb = new StringBuilder(); for (SortedMap.Entry<String, String> entry : params.entrySet()) { if (entry.getValue() != null && entry.getValue().length() > 0) { sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); } } if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } return verify(sb.toString(), signBase64, publicKeyHex); } /** * 接口响应报文验签,报文须删除signType和sign后直接验签 * * @param content * @param signBase64 * @param publicKeyString * @return */ public static boolean verify(String content, String signBase64, String publicKeyHex) { log.debug("待验签字符串:" + content); try { if (publicKeyHex.length() == 130) { publicKeyHex = publicKeyHex.substring(2); } String xHex = publicKeyHex.substring(0, 64); String yHex = publicKeyHex.substring(64, 128); SM2 sm2 = new SM2(null, BCUtil.toSm2Params(xHex, yHex)); return sm2.verify(content.getBytes(), Base64.decode(signBase64)); } catch (Exception e) { log.error("验签发生异常,请检查公钥配置是否正确!" + e.getMessage(), e); return false; } } /** * 解密 * * @param content * @param privateKey * @return */ public static byte[] decrypt(byte[] content, String privateKeyHex) { SM2 sm2 = new SM2(privateKeyHex, null); return sm2.decrypt(content); } private SM2Utils() { throw new IllegalStateException("Utility class"); } } final class SM4Utils { /** * 解密Hex(16进制)或Base64表示的字符串 * * @param data 被解密的String,必须为16进制字符串或Base64表示形式 * @param key 密钥,支持密钥长度:128位 * @return */ public static String decryptStr(String data, byte[] key) { return new SM4(Mode.ECB, Padding.ZeroPadding, key).decryptStr(data); } private SM4Utils() { throw new IllegalStateException("Utility class"); } } final class RSA2Utils { private static Logger log = LoggerFactory.getLogger(RSA2Utils.class); /** * 校验签名 * * @param params * @param publicKey * @return */ public static boolean verify(SortedMap<String, String> params, String publicKey) { String signBase64 = params.get(OpenApisConstants.SIGN); params.remove(OpenApisConstants.SIGN); params.remove(OpenApisConstants.SIGN_TYPE); StringBuilder sb = new StringBuilder(); for (SortedMap.Entry<String, String> entry : params.entrySet()) { if (entry.getValue() != null && entry.getValue().length() > 0) { sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); } } if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } String content = sb.toString(); return verify(content, signBase64, publicKey); } /** * 校验签名 * * @param content * @param signBase64 * @param publicKey * @return */ public static boolean verify(String content, String signBase64, String publicKey) { if (log.isDebugEnabled()) { log.debug("待验签字符串:" + content); } Sign sign = new Sign(SignAlgorithm.SHA256withRSA, null, publicKey); return sign.verify(StrUtil.bytes(content, OpenApisConstants.DEFAULT_CHARSET), Base64.decode(signBase64)); } /** * 解密 * * @param content * @param privateKey * @return */ public static byte[] decrypt(byte[] content, String privateKey) { RSA rsa = new RSA(AsymmetricAlgorithm.RSA_ECB_PKCS1.getValue(), privateKey, null); return rsa.decrypt(content, KeyType.PrivateKey); } private RSA2Utils() { throw new IllegalStateException("Utility class"); } } final class AESUtils { /** * 解密Hex(16进制)或Base64表示的字符串 * * @param data 被解密的String,必须为16进制字符串或Base64表示形式 * @param key 密钥,支持密钥长度:128位 * @return */ public static String decryptStr(String data, byte[] key) { return new AES(Mode.ECB, Padding.ZeroPadding, key).decryptStr(data); } private AESUtils() { throw new IllegalStateException("Utility class"); } } final class ServletUtils { static Logger log = LoggerFactory.getLogger(ServletUtils.class); public static TreeMap<String, String> getServletParamterMap(HttpServletRequest request) { try { InputStream in = request.getInputStream(); String body = IOUtils.toString(in); TreeMap<String, String> requestMap = string2Map(body); return requestMap; } catch (Exception e) { log.error("getServletParamterMap error:", e); return new TreeMap<String, String>(); } } public static TreeMap<String, String> string2Map(String queryString) { if (null == queryString || "".equals(queryString)) { return null; } TreeMap<String, String> m = new TreeMap<String, String>(); String[] strArray = queryString.split("&"); for (int index = 0; index < strArray.length; index++) { String pair = strArray[index]; m.put(URLDecoder.decode(pair.substring(0, pair.indexOf("=")), StandardCharsets.UTF_8), URLDecoder.decode(pair.substring(pair.indexOf("=") + 1), StandardCharsets.UTF_8)); } return m; } } final class Tools{ public static String treeMapToString(TreeMap<String, String> params) { StringBuilder sb = new StringBuilder(); for (Map.Entry<String, String> entry : params.entrySet()) { if (entry.getValue() != null && entry.getValue().length() > 0) { sb.append(entry.getKey()).append("=").append(entry.getValue()).append("&"); } } if (sb.length() > 0) { sb.deleteCharAt(sb.length() - 1); } return sb.toString(); } } final class OpenApisConstants { public static final String RSA = "RSA"; public static final String SIGN = "sign"; public static final String SIGN_TYPE = "signType"; public static final String DEFAULT_FORMAT = "JSON"; public static final String DEFAULT_SIGNTYPE = "SHA256WithRSA"; public static final String SM2_SIGNTYPE = "SM2"; public static final String RSA2_SIGNTYPE = "RSA2"; public static final String DEFAULT_VERSION = "1.0"; public static final String DEFAULT_CHARSET = "UTF-8"; } ``` ### 注意事项 • 请确保使用的 SDK 版本与接口版本匹配 • 如遇依赖冲突问题,请检查项目中是否存在相同组件的不同版本 • 生产环境建议使用最新的稳定版本 <div hidden="">AI抽取结束标记</div>
卜子超buzc
2025年11月20日 17:14
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Word文件
PDF文档
分享
链接
类型
密码
更新密码
有效期