mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-09-03 02:56:20 +00:00
feat 集成 egan/pay-java-parent 做后续二次开发
This commit is contained in:
@@ -13,4 +13,12 @@
|
||||
<artifactId>daxpay-single-union</artifactId>
|
||||
<description>云闪付支付通道实现</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.dromara.daxpay</groupId>
|
||||
<artifactId>daxpay-single-unisdk</artifactId>
|
||||
<version>${daxpay.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
@@ -0,0 +1,243 @@
|
||||
package org.dromara.daxpay.channel.union.sdk.api;
|
||||
|
||||
import org.dromara.daxpay.unisdk.common.api.BasePayConfigStorage;
|
||||
import org.dromara.daxpay.unisdk.common.bean.CertStoreType;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
|
||||
/**
|
||||
* @author Actinia
|
||||
* <pre>
|
||||
* email hayesfu@qq.com
|
||||
* create 2017 2017/11/4 0004
|
||||
* </pre>
|
||||
*/
|
||||
public class UnionPayConfigStorage extends BasePayConfigStorage {
|
||||
|
||||
|
||||
/**
|
||||
* 商户号
|
||||
*/
|
||||
private String merId;
|
||||
|
||||
/**
|
||||
* 商户收款账号
|
||||
*/
|
||||
private String seller;
|
||||
|
||||
private String version = "5.1.0";
|
||||
/**
|
||||
* 0:普通商户直连接入
|
||||
* 1: 收单机构
|
||||
* 2:平台类商户接入
|
||||
*/
|
||||
private String accessType = "0";
|
||||
|
||||
|
||||
/**
|
||||
* 应用私钥证书
|
||||
*/
|
||||
private Object keyPrivateCert;
|
||||
/**
|
||||
* 应用私钥证书,rsa_private pkcs8格式 生成签名时使用
|
||||
*/
|
||||
private String keyPrivateCertPwd;
|
||||
|
||||
/**
|
||||
* 中级证书
|
||||
*/
|
||||
private Object acpMiddleCert;
|
||||
/**
|
||||
* 根证书
|
||||
*/
|
||||
private Object acpRootCert;
|
||||
|
||||
/**
|
||||
* 证书存储类型
|
||||
*/
|
||||
private CertStoreType certStoreType;
|
||||
|
||||
/**
|
||||
* 设置私钥证书
|
||||
*
|
||||
* @param certificate 私钥证书地址 或者证书内容字符串
|
||||
* 私钥证书密码 {@link #setKeyPrivateCertPwd(String)}
|
||||
*/
|
||||
public void setKeyPrivateCert(String certificate) {
|
||||
super.setKeyPrivate(certificate);
|
||||
this.keyPrivateCert = certificate;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置私钥证书
|
||||
*
|
||||
* @param keyPrivateCert 私钥证书信息流
|
||||
* 私钥证书密码 {@link #setKeyPrivateCertPwd(String)}
|
||||
*/
|
||||
public void setKeyPrivateCert(InputStream keyPrivateCert) {
|
||||
this.keyPrivateCert = keyPrivateCert;
|
||||
}
|
||||
|
||||
public InputStream getKeyPrivateCertInputStream() throws IOException {
|
||||
return certStoreType.getInputStream(keyPrivateCert);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置中级证书
|
||||
*
|
||||
* @param acpMiddleCert 证书信息或者证书路径
|
||||
*/
|
||||
public void setAcpMiddleCert(String acpMiddleCert) {
|
||||
this.acpMiddleCert = acpMiddleCert;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置中级证书
|
||||
*
|
||||
* @param acpMiddleCert 证书文件
|
||||
*/
|
||||
public void setAcpMiddleCert(InputStream acpMiddleCert) {
|
||||
this.acpMiddleCert = acpMiddleCert;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置根证书
|
||||
*
|
||||
* @param acpRootCert 证书路径或者证书信息字符串
|
||||
*/
|
||||
public void setAcpRootCert(String acpRootCert) {
|
||||
this.acpRootCert = acpRootCert;
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置根证书
|
||||
*
|
||||
* @param acpRootCert 证书文件流
|
||||
*/
|
||||
public void setAcpRootCert(InputStream acpRootCert) {
|
||||
this.acpRootCert = acpRootCert;
|
||||
}
|
||||
|
||||
public String getAcpMiddleCert() {
|
||||
return (String) acpMiddleCert;
|
||||
}
|
||||
|
||||
public String getAcpRootCert() {
|
||||
return (String) acpRootCert;
|
||||
}
|
||||
|
||||
public InputStream getAcpMiddleCertInputStream() throws IOException {
|
||||
return certStoreType.getInputStream(acpMiddleCert);
|
||||
}
|
||||
|
||||
public InputStream getAcpRootCertInputStream() throws IOException {
|
||||
return certStoreType.getInputStream(acpRootCert);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取私钥证书密码
|
||||
*
|
||||
* @return 私钥证书密码
|
||||
*/
|
||||
public String getKeyPrivateCertPwd() {
|
||||
return keyPrivateCertPwd;
|
||||
}
|
||||
|
||||
public void setKeyPrivateCertPwd(String keyPrivateCertPwd) {
|
||||
this.keyPrivateCertPwd = keyPrivateCertPwd;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAppid() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用id
|
||||
* 纠正名称
|
||||
*
|
||||
* @return 应用id
|
||||
*/
|
||||
@Override
|
||||
public String getAppId() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 合作者id
|
||||
* @see #getPid()
|
||||
*/
|
||||
@Deprecated
|
||||
public String getPartner() {
|
||||
return merId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置合作者id
|
||||
*
|
||||
* @param partner 合作者id
|
||||
* @see #setPid(String)
|
||||
*/
|
||||
@Deprecated
|
||||
public void setPartner(String partner) {
|
||||
this.merId = partner;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPid() {
|
||||
return merId;
|
||||
}
|
||||
|
||||
public void setPid(String pid) {
|
||||
this.merId = pid;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getSeller() {
|
||||
return seller;
|
||||
}
|
||||
|
||||
public void setSeller(String seller) {
|
||||
this.seller = seller;
|
||||
}
|
||||
|
||||
public String getMerId() {
|
||||
return merId;
|
||||
}
|
||||
|
||||
public void setMerId(String merId) {
|
||||
this.merId = merId;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getAccessType() {
|
||||
return accessType;
|
||||
}
|
||||
|
||||
public void setAccessType(String accessType) {
|
||||
this.accessType = accessType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 证书存储类型
|
||||
*
|
||||
* @return 证书存储类型
|
||||
*/
|
||||
public CertStoreType getCertStoreType() {
|
||||
return certStoreType;
|
||||
}
|
||||
|
||||
public void setCertStoreType(CertStoreType certStoreType) {
|
||||
this.certStoreType = certStoreType;
|
||||
}
|
||||
}
|
@@ -0,0 +1,739 @@
|
||||
package org.dromara.daxpay.channel.union.sdk.api;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.dromara.daxpay.channel.union.sdk.bean.*;
|
||||
import org.dromara.daxpay.unisdk.common.bean.*;
|
||||
import org.dromara.daxpay.unisdk.common.bean.outbuilder.PayTextOutMessage;
|
||||
import org.dromara.daxpay.unisdk.common.bean.result.PayException;
|
||||
import org.dromara.daxpay.unisdk.common.exception.PayErrorException;
|
||||
import org.dromara.daxpay.unisdk.common.http.HttpConfigStorage;
|
||||
import org.dromara.daxpay.unisdk.common.http.UriVariables;
|
||||
import org.dromara.daxpay.unisdk.common.util.DateUtils;
|
||||
import org.dromara.daxpay.unisdk.common.util.Util;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.CertDescriptor;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.SignTextUtils;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.SignUtils;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.encrypt.RSA;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.encrypt.RSA2;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.cert.*;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* 云闪付支付服务类重命名, 避免与系统中类名冲突
|
||||
* @author xxm
|
||||
* @since 2024/3/8
|
||||
*/
|
||||
public class UnionPayKit extends UnionPayService {
|
||||
/**
|
||||
* 测试域名
|
||||
*/
|
||||
private static final String TEST_BASE_DOMAIN = "test.95516.com";
|
||||
/**
|
||||
* 正式域名
|
||||
*/
|
||||
private static final String RELEASE_BASE_DOMAIN = "95516.com";
|
||||
/**
|
||||
* 交易请求地址
|
||||
*/
|
||||
private static final String FRONT_TRANS_URL = "https://gateway.%s/gateway/api/frontTransReq.do";
|
||||
private static final String BACK_TRANS_URL = "https://gateway.%s/gateway/api/backTransReq.do";
|
||||
private static final String SINGLE_QUERY_URL = "https://gateway.%s/gateway/api/queryTrans.do";
|
||||
private static final String BATCH_TRANS_URL = "https://gateway.%s/gateway/api/batchTrans.do";
|
||||
private static final String FILE_TRANS_URL = "https://filedownload.%s/";
|
||||
private static final String APP_TRANS_URL = "https://gateway.%s/gateway/api/appTransReq.do";
|
||||
private static final String CARD_TRANS_URL = "https://gateway.%s/gateway/api/cardTransReq.do";
|
||||
/**
|
||||
* 证书解释器
|
||||
*/
|
||||
private volatile CertDescriptor certDescriptor;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param payConfigStorage 支付配置
|
||||
*/
|
||||
public UnionPayKit(UnionPayConfigStorage payConfigStorage) {
|
||||
super(payConfigStorage);
|
||||
}
|
||||
|
||||
public UnionPayKit(UnionPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) {
|
||||
super(payConfigStorage, configStorage);
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置支付配置
|
||||
*
|
||||
* @param payConfigStorage 支付配置
|
||||
*/
|
||||
@Override
|
||||
public UnionPayService setPayConfigStorage(UnionPayConfigStorage payConfigStorage) {
|
||||
this.payConfigStorage = payConfigStorage;
|
||||
if (null != certDescriptor) {
|
||||
return this;
|
||||
}
|
||||
//
|
||||
if (!payConfigStorage.isCertSign()){
|
||||
return this;
|
||||
}
|
||||
|
||||
try {
|
||||
certDescriptor = new CertDescriptor();
|
||||
certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12");
|
||||
certDescriptor.initPublicCert(payConfigStorage.getAcpMiddleCertInputStream());
|
||||
certDescriptor.initRootCert(payConfigStorage.getAcpRootCertInputStream());
|
||||
}
|
||||
catch (IOException e) {
|
||||
LOG.error("", e);
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付请求地址
|
||||
*
|
||||
* @param transactionType 交易类型
|
||||
* @return 请求地址
|
||||
*/
|
||||
@Override
|
||||
public String getReqUrl(TransactionType transactionType) {
|
||||
return (payConfigStorage.isTest() ? TEST_BASE_DOMAIN : RELEASE_BASE_DOMAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据是否为沙箱环境进行获取请求地址
|
||||
*
|
||||
* @return 请求地址
|
||||
*/
|
||||
public String getReqUrl() {
|
||||
return getReqUrl(null);
|
||||
}
|
||||
|
||||
public String getFrontTransUrl() {
|
||||
return String.format(FRONT_TRANS_URL, getReqUrl());
|
||||
}
|
||||
|
||||
public String getBackTransUrl() {
|
||||
return String.format(BACK_TRANS_URL, getReqUrl());
|
||||
}
|
||||
|
||||
public String getAppTransUrl() {
|
||||
return String.format(APP_TRANS_URL, getReqUrl());
|
||||
}
|
||||
|
||||
public String getSingleQueryUrl() {
|
||||
return String.format(SINGLE_QUERY_URL, getReqUrl());
|
||||
}
|
||||
|
||||
|
||||
public String getFileTransUrl() {
|
||||
return String.format(FILE_TRANS_URL, getReqUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台通知地址
|
||||
*
|
||||
* @param parameters 预订单信息
|
||||
* @param order 订单
|
||||
* @return 预订单信息
|
||||
*/
|
||||
private Map<String, Object> initNotifyUrl(Map<String, Object> parameters, AssistOrder order) {
|
||||
//后台通知地址
|
||||
OrderParaStructure.loadParameters(parameters, SDKConstants.param_backUrl, payConfigStorage.getNotifyUrl());
|
||||
OrderParaStructure.loadParameters(parameters, SDKConstants.param_backUrl, order.getNotifyUrl());
|
||||
OrderParaStructure.loadParameters(parameters, SDKConstants.param_backUrl, order);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改
|
||||
*
|
||||
* @return 返回参数集合
|
||||
*/
|
||||
private Map<String, Object> getCommonParam() {
|
||||
Map<String, Object> params = new TreeMap<>();
|
||||
UnionPayConfigStorage configStorage = payConfigStorage;
|
||||
//银联接口版本
|
||||
params.put(SDKConstants.param_version, configStorage.getVersion());
|
||||
//编码方式
|
||||
params.put(SDKConstants.param_encoding, payConfigStorage.getInputCharset().toUpperCase());
|
||||
//商户代码
|
||||
params.put(SDKConstants.param_merId, payConfigStorage.getPid());
|
||||
|
||||
//订单发送时间
|
||||
params.put(SDKConstants.param_txnTime, DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS));
|
||||
//后台通知地址
|
||||
params.put(SDKConstants.param_backUrl, payConfigStorage.getNotifyUrl());
|
||||
|
||||
//交易币种
|
||||
params.put(SDKConstants.param_currencyCode, "156");
|
||||
//接入类型,商户接入填0 ,不需修改(0:直连商户, 1: 收单机构 2:平台商户)
|
||||
params.put(SDKConstants.param_accessType, configStorage.getAccessType());
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 回调校验
|
||||
*
|
||||
* @param result 回调回来的参数集
|
||||
* @return 签名校验 true通过
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean verify(Map<String, Object> result) {
|
||||
return verify(new NoticeParams(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调校验
|
||||
*
|
||||
* @param noticeParams 回调回来的参数集
|
||||
* @return 签名校验 true通过
|
||||
*/
|
||||
@Override
|
||||
public boolean verify(NoticeParams noticeParams) {
|
||||
final Map<String, Object> result = noticeParams.getBody();
|
||||
if (null == result || result.get(SDKConstants.param_signature) == null) {
|
||||
LOG.debug("银联支付验签异常:params:" + result);
|
||||
return false;
|
||||
}
|
||||
return this.signVerify(result, (String) result.get(SDKConstants.param_signature));
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名校验
|
||||
*
|
||||
* @param params 参数集
|
||||
* @param sign 签名原文
|
||||
* @return 签名校验 true通过
|
||||
*/
|
||||
public boolean signVerify(Map<String, Object> params, String sign) {
|
||||
SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType());
|
||||
|
||||
String data = SignTextUtils.parameterText(params, "&", "signature");
|
||||
switch (signUtils) {
|
||||
case RSA:
|
||||
data = SignUtils.SHA1.createSign(data, "", payConfigStorage.getInputCharset());
|
||||
return RSA.verify(data, sign, verifyCertificate(genCertificateByStr((String) params.get(SDKConstants.param_signPubKeyCert))).getPublicKey(), payConfigStorage.getInputCharset());
|
||||
case RSA2:
|
||||
data = SignUtils.SHA256.createSign(data, "", payConfigStorage.getInputCharset());
|
||||
return RSA2.verify(data, sign, verifyCertificate(genCertificateByStr((String) params.get(SDKConstants.param_signPubKeyCert))).getPublicKey(), payConfigStorage.getInputCharset());
|
||||
case SHA1:
|
||||
case SHA256:
|
||||
case SM3:
|
||||
String before = signUtils.createSign(payConfigStorage.getKeyPublic(), "", payConfigStorage.getInputCharset());
|
||||
return signUtils.verify(data, sign, "&" + before, payConfigStorage.getInputCharset());
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 订单超时时间。
|
||||
* 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。
|
||||
* 此时间建议取支付时的北京时间加15分钟。
|
||||
* 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。
|
||||
*
|
||||
* @param expirationTime 超时时间
|
||||
* @return 具体的时间字符串
|
||||
*/
|
||||
private String getPayTimeout(Date expirationTime) {
|
||||
//
|
||||
if (null != expirationTime) {
|
||||
return DateUtils.formatDate(expirationTime, DateUtils.YYYYMMDDHHMMSS);
|
||||
}
|
||||
return DateUtils.formatDate(new Timestamp(System.currentTimeMillis() + 30 * 60 * 1000), DateUtils.YYYYMMDDHHMMSS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回创建的订单信息
|
||||
*
|
||||
* @param order 支付订单
|
||||
* @return 订单信息
|
||||
* @see PayOrder 支付订单信息
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> orderInfo(PayOrder order) {
|
||||
Map<String, Object> params = this.getCommonParam();
|
||||
|
||||
UnionTransactionType type = (UnionTransactionType) order.getTransactionType();
|
||||
initNotifyUrl(params, order);
|
||||
|
||||
//设置交易类型相关的参数
|
||||
type.convertMap(params);
|
||||
|
||||
params.put(SDKConstants.param_orderId, order.getOutTradeNo());
|
||||
|
||||
if (StringUtils.isNotEmpty(order.getAddition())) {
|
||||
params.put(SDKConstants.param_reqReserved, order.getAddition());
|
||||
}
|
||||
switch (type) {
|
||||
case WAP:
|
||||
case WEB:
|
||||
case B2B:
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice()));
|
||||
params.put("orderDesc", order.getSubject());
|
||||
params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime()));
|
||||
params.put(SDKConstants.param_frontUrl, payConfigStorage.getReturnUrl());
|
||||
break;
|
||||
case CONSUME:
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice()));
|
||||
params.put(SDKConstants.param_qrNo, order.getAuthCode());
|
||||
break;
|
||||
case APPLY_QR_CODE:
|
||||
if (null != order.getPrice()) {
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice()));
|
||||
}
|
||||
params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime()));
|
||||
break;
|
||||
default:
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice()));
|
||||
params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime()));
|
||||
params.put("orderDesc", order.getSubject());
|
||||
}
|
||||
params.putAll(order.getAttrs());
|
||||
params = preOrderHandler(params, order);
|
||||
return setSign(params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成并设置签名
|
||||
*
|
||||
* @param parameters 请求参数
|
||||
* @return 请求参数
|
||||
*/
|
||||
private Map<String, Object> setSign(Map<String, Object> parameters) {
|
||||
|
||||
SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType());
|
||||
|
||||
String signStr;
|
||||
switch (signUtils) {
|
||||
case RSA:
|
||||
parameters.put(SDKConstants.param_signMethod, SDKConstants.SIGNMETHOD_RSA);
|
||||
parameters.put(SDKConstants.param_certId, certDescriptor.getSignCertId());
|
||||
signStr = SignUtils.SHA1.createSign(SignTextUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset());
|
||||
parameters.put(SDKConstants.param_signature, RSA.sign(signStr, certDescriptor.getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset()));
|
||||
break;
|
||||
case RSA2:
|
||||
parameters.put(SDKConstants.param_signMethod, SDKConstants.SIGNMETHOD_RSA);
|
||||
parameters.put(SDKConstants.param_certId, certDescriptor.getSignCertId());
|
||||
signStr = SignUtils.SHA256.createSign(SignTextUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset());
|
||||
parameters.put(SDKConstants.param_signature, RSA2.sign(signStr, certDescriptor.getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset()));
|
||||
break;
|
||||
case SHA1:
|
||||
case SHA256:
|
||||
case SM3:
|
||||
String key = payConfigStorage.getKeyPrivate();
|
||||
signStr = SignTextUtils.parameterText(parameters, "&", "signature");
|
||||
key = signUtils.createSign(key, "", payConfigStorage.getInputCharset()) + "&";
|
||||
parameters.put(SDKConstants.param_signature, signUtils.createSign(signStr, key, payConfigStorage.getInputCharset()));
|
||||
break;
|
||||
default:
|
||||
throw new PayErrorException(new PayException("sign fail", "未找到的签名类型"));
|
||||
}
|
||||
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证证书链
|
||||
*
|
||||
* @param cert 需要验证的证书
|
||||
*/
|
||||
private X509Certificate verifyCertificate(X509Certificate cert) {
|
||||
try {
|
||||
cert.checkValidity();//验证有效期
|
||||
X509Certificate middleCert = certDescriptor.getPublicCert();
|
||||
X509Certificate rootCert = certDescriptor.getRootCert();
|
||||
|
||||
X509CertSelector selector = new X509CertSelector();
|
||||
selector.setCertificate(cert);
|
||||
|
||||
Set<TrustAnchor> trustAnchors = new HashSet<>();
|
||||
trustAnchors.add(new TrustAnchor(rootCert, null));
|
||||
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustAnchors, selector);
|
||||
|
||||
Set<X509Certificate> intermediateCerts = new HashSet<>();
|
||||
intermediateCerts.add(rootCert);
|
||||
intermediateCerts.add(middleCert);
|
||||
intermediateCerts.add(cert);
|
||||
|
||||
pkixParams.setRevocationEnabled(false);
|
||||
|
||||
CertStore intermediateCertStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(intermediateCerts));
|
||||
pkixParams.addCertStore(intermediateCertStore);
|
||||
|
||||
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
|
||||
|
||||
/*PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)*/
|
||||
builder.build(pkixParams);
|
||||
return cert;
|
||||
}
|
||||
catch (CertPathBuilderException e) {
|
||||
LOG.error("verify certificate chain fail.", e);
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
LOG.error("", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送订单
|
||||
*
|
||||
* @param order 发起支付的订单信息
|
||||
* @return 返回支付结果
|
||||
*/
|
||||
|
||||
public JSONObject postOrder(PayOrder order, String url) {
|
||||
Map<String, Object> params = orderInfo(order);
|
||||
String responseStr = getHttpRequestTemplate().postForObject(url, params, String.class);
|
||||
JSONObject response = UriVariables.getParametersToMap(responseStr);
|
||||
if (response.isEmpty()) {
|
||||
throw new PayErrorException(new PayException("failure", "响应内容有误!", responseStr));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toPay(PayOrder order) {
|
||||
|
||||
if (null == order.getTransactionType()) {
|
||||
order.setTransactionType(UnionTransactionType.WEB);
|
||||
}
|
||||
else if (UnionTransactionType.WEB != order.getTransactionType() && UnionTransactionType.WAP != order.getTransactionType() && UnionTransactionType.B2B != order.getTransactionType()) {
|
||||
throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType()));
|
||||
}
|
||||
|
||||
return super.toPay(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输出二维码,用户返回给支付端,
|
||||
*
|
||||
* @param order 发起支付的订单信息
|
||||
* @return 返回图片信息,支付时需要的
|
||||
*/
|
||||
@Override
|
||||
public String getQrPay(PayOrder order) {
|
||||
order.setTransactionType(UnionTransactionType.APPLY_QR_CODE);
|
||||
JSONObject response = postOrder(order, getBackTransUrl());
|
||||
if (this.verify(response)) {
|
||||
if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) {
|
||||
//成功
|
||||
return (String) response.get(SDKConstants.param_qrCode);
|
||||
}
|
||||
throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), response.toJSONString()));
|
||||
}
|
||||
throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷卡付,pos主动扫码付款(条码付)
|
||||
*
|
||||
* @param order 发起支付的订单信息
|
||||
* @return 返回支付结果
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> microPay(PayOrder order) {
|
||||
order.setTransactionType(UnionTransactionType.CONSUME);
|
||||
JSONObject response = postOrder(order, getBackTransUrl());
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将字符串转换为X509Certificate对象.
|
||||
*
|
||||
* @param x509CertString 证书串
|
||||
* @return X509Certificate
|
||||
*/
|
||||
public static X509Certificate genCertificateByStr(String x509CertString) {
|
||||
X509Certificate x509Cert = null;
|
||||
try {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
InputStream tIn = new ByteArrayInputStream(x509CertString.getBytes("ISO-8859-1"));
|
||||
x509Cert = (X509Certificate) cf.generateCertificate(tIn);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new PayErrorException(new PayException("证书加载失败", "gen certificate error:" + e.getLocalizedMessage()));
|
||||
}
|
||||
return x509Cert;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输出消息,用户返回给支付端
|
||||
*
|
||||
* @param code 状态
|
||||
* @param message 消息
|
||||
* @return 返回输出消息
|
||||
*/
|
||||
@Override
|
||||
public PayOutMessage getPayOutMessage(String code, String message) {
|
||||
return PayTextOutMessage.TEXT().content(code.toLowerCase()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成功输出消息,用户返回给支付端
|
||||
* 主要用于拦截器中返回
|
||||
*
|
||||
* @param payMessage 支付回调消息
|
||||
* @return 返回输出消息
|
||||
*/
|
||||
@Override
|
||||
public PayOutMessage successPayOutMessage(PayMessage payMessage) {
|
||||
return getPayOutMessage("ok", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能:生成自动跳转的Html表单
|
||||
*
|
||||
* @param orderInfo 发起支付的订单信息
|
||||
* @param method 请求方式 "post" "get",
|
||||
* @return 生成自动跳转的Html表单返回给支付端, 针对于PC端
|
||||
* @see MethodType 请求类型
|
||||
*/
|
||||
@Override
|
||||
public String buildRequest(Map<String, Object> orderInfo, MethodType method) {
|
||||
StringBuffer sf = new StringBuffer();
|
||||
sf.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=")
|
||||
.append(payConfigStorage.getInputCharset())
|
||||
.append("\"/></head><body>");
|
||||
sf.append("<form id = \"pay_form\" action=\"")
|
||||
.append(getFrontTransUrl())
|
||||
.append("\" method=\"post\">");
|
||||
if (null != orderInfo && !orderInfo.isEmpty()) {
|
||||
for (Map.Entry<String, Object> entry : orderInfo.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
sf.append("<input type=\"hidden\" name=\"" + key + "\" id=\"" + key + "\" value=\"" + value + "\"/>");
|
||||
}
|
||||
}
|
||||
sf.append("</form>");
|
||||
sf.append("</body>");
|
||||
sf.append("<script type=\"text/javascript\">");
|
||||
sf.append("document.all.pay_form.submit();");
|
||||
sf.append("</script>");
|
||||
sf.append("</html>");
|
||||
return sf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能:将订单信息进行签名并提交请求
|
||||
* 业务范围:手机支付控件(含安卓Pay)
|
||||
*
|
||||
* @param order 订单信息
|
||||
* @return 成功:返回支付结果 失败:返回
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> app(PayOrder order) {
|
||||
if (null == order.getTransactionType()) {
|
||||
order.setTransactionType(UnionTransactionType.APP);
|
||||
}
|
||||
JSONObject response = postOrder(order, getAppTransUrl());
|
||||
if (this.verify(response)) {
|
||||
if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) {
|
||||
// //成功,获取tn号
|
||||
// String tn = (String)response.get(SDKConstants.param_tn);
|
||||
return response;
|
||||
}
|
||||
throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), response.toJSONString()));
|
||||
}
|
||||
throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易查询接口
|
||||
*
|
||||
* @param tradeNo 支付平台订单号
|
||||
* @param outTradeNo 商户单号
|
||||
* @return 返回查询回来的结果集,支付方原值返回
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> query(String tradeNo, String outTradeNo) {
|
||||
return query(new AssistOrder(tradeNo, outTradeNo));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易查询接口
|
||||
*
|
||||
* @param assistOrder 查询条件
|
||||
* @return 返回查询回来的结果集,支付方原值返回
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> query(AssistOrder assistOrder) {
|
||||
Map<String, Object> params = this.getCommonParam();
|
||||
UnionTransactionType.QUERY.convertMap(params);
|
||||
params.put(SDKConstants.param_orderId, assistOrder.getOutTradeNo());
|
||||
this.setSign(params);
|
||||
String responseStr = getHttpRequestTemplate().postForObject(this.getSingleQueryUrl(), params, String.class);
|
||||
return UriVariables.getParametersToMap(responseStr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 消费撤销/退货接口
|
||||
*
|
||||
* @param origQryId 原交易查询流水号.
|
||||
* @param orderId 退款单号
|
||||
* @param refundAmount 退款金额
|
||||
* @param type UnionTransactionType.REFUND 或者UnionTransactionType.CONSUME_UNDO
|
||||
* @return 返回支付方申请退款后的结果
|
||||
*/
|
||||
public UnionRefundResult unionRefundOrConsumeUndo(String origQryId, String orderId, BigDecimal refundAmount, UnionTransactionType type) {
|
||||
return unionRefundOrConsumeUndo(new RefundOrder(orderId, origQryId, refundAmount), type);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 消费撤销/退货接口
|
||||
*
|
||||
* @param refundOrder 退款订单信息
|
||||
* @param type UnionTransactionType.REFUND 或者UnionTransactionType.CONSUME_UNDO
|
||||
* @return 返回支付方申请退款后的结果
|
||||
*/
|
||||
public UnionRefundResult unionRefundOrConsumeUndo(RefundOrder refundOrder, UnionTransactionType type) {
|
||||
Map<String, Object> params = this.getCommonParam();
|
||||
type.convertMap(params);
|
||||
params.put(SDKConstants.param_orderId, refundOrder.getRefundNo());
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(refundOrder.getRefundAmount()));
|
||||
params.put(SDKConstants.param_origQryId, refundOrder.getTradeNo());
|
||||
params.putAll(refundOrder.getAttrs());
|
||||
this.setSign(params);
|
||||
String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class);
|
||||
JSONObject response = UriVariables.getParametersToMap(responseStr);
|
||||
|
||||
if (this.verify(new NoticeParams(response))) {
|
||||
final UnionRefundResult refundResult = UnionRefundResult.create(response);
|
||||
if (SDKConstants.OK_RESP_CODE.equals(refundResult.getRespCode())) {
|
||||
return refundResult;
|
||||
|
||||
}
|
||||
throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString()));
|
||||
}
|
||||
throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易关闭接口
|
||||
* 使用冲正接口
|
||||
*
|
||||
* @param tradeNo 支付平台订单号
|
||||
* @param outTradeNo 商户单号
|
||||
* @return 返回支付方交易关闭后的结果
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> close(String tradeNo, String outTradeNo) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易关闭接口
|
||||
* TODO 这个是冲正接口, 后续进行迁移
|
||||
*
|
||||
* @param assistOrder 关闭订单
|
||||
* @return 返回支付方交易关闭后的结果
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> close(AssistOrder assistOrder) {
|
||||
Map<String, Object> params = this.getCommonParam();
|
||||
//交易类型
|
||||
params.put(SDKConstants.param_txnType, "99");
|
||||
//交易子类
|
||||
params.put(SDKConstants.param_txnSubType, "01");
|
||||
//业务类型
|
||||
params.put(SDKConstants.param_bizType,"000000");
|
||||
//渠道类型
|
||||
params.put(SDKConstants.param_channelType,"08");
|
||||
// 订单号
|
||||
params.put(SDKConstants.param_orderId, assistOrder.getOutTradeNo());
|
||||
this.setSign(params);
|
||||
String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class);
|
||||
return UriVariables.getParametersToMap(responseStr);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnionRefundResult refund(RefundOrder refundOrder) {
|
||||
return unionRefundOrConsumeUndo(refundOrder, UnionTransactionType.REFUND);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询退款
|
||||
*
|
||||
* @param refundOrder 退款订单单号信息
|
||||
* @return 返回支付方查询退款后的结果
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> refundquery(RefundOrder refundOrder) {
|
||||
return this.query(refundOrder);
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单
|
||||
*
|
||||
* @param billDate 账单时间
|
||||
* @param fileType 文件类型 文件类型,一般商户填写00即可
|
||||
* @return 返回fileContent 请自行将数据落地
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> downloadBill(Date billDate, String fileType) {
|
||||
return downloadBill(billDate, new UnionPayBillType(fileType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单
|
||||
*
|
||||
* @param billDate 账单时间
|
||||
* @param billType 账单类型
|
||||
* @return 返回fileContent 请自行将数据落地
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> downloadBill(Date billDate, BillType billType) {
|
||||
|
||||
Map<String, Object> params = this.getCommonParam();
|
||||
UnionTransactionType.FILE_TRANSFER.convertMap(params);
|
||||
|
||||
params.put(SDKConstants.param_settleDate, DateUtils.formatDate(billDate, DateUtils.MMDD));
|
||||
params.put(SDKConstants.param_fileType, billType.getFileType());
|
||||
params.remove(SDKConstants.param_backUrl);
|
||||
params.remove(SDKConstants.param_currencyCode);
|
||||
this.setSign(params);
|
||||
String responseStr = getHttpRequestTemplate().postForObject(this.getFileTransUrl(), params, String.class);
|
||||
JSONObject response = UriVariables.getParametersToMap(responseStr);
|
||||
// if (this.verify(response)) {
|
||||
// if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) {
|
||||
return response;
|
||||
// }
|
||||
// throw new PayErrorException(new PayException(response.get(SDKConstants.param_respCode).toString(), response.get(SDKConstants.param_respMsg).toString(), response.toString()));
|
||||
// }
|
||||
// throw new PayErrorException(new PayException("failure", "验证签名失败", response.toString()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建消息
|
||||
*
|
||||
* @param message 支付平台返回的消息
|
||||
* @return 支付消息对象
|
||||
*/
|
||||
@Override
|
||||
public PayMessage createMessage(Map<String, Object> message) {
|
||||
return UnionPayMessage.create(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,743 @@
|
||||
package org.dromara.daxpay.channel.union.sdk.api;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.daxpay.channel.union.sdk.bean.*;
|
||||
import org.dromara.daxpay.unisdk.common.api.BasePayService;
|
||||
import org.dromara.daxpay.unisdk.common.bean.*;
|
||||
import org.dromara.daxpay.unisdk.common.bean.outbuilder.PayTextOutMessage;
|
||||
import org.dromara.daxpay.unisdk.common.bean.result.PayException;
|
||||
import org.dromara.daxpay.unisdk.common.exception.PayErrorException;
|
||||
import org.dromara.daxpay.unisdk.common.http.HttpConfigStorage;
|
||||
import org.dromara.daxpay.unisdk.common.http.UriVariables;
|
||||
import org.dromara.daxpay.unisdk.common.util.DateUtils;
|
||||
import org.dromara.daxpay.unisdk.common.util.Util;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.CertDescriptor;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.SignTextUtils;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.SignUtils;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.encrypt.RSA;
|
||||
import org.dromara.daxpay.unisdk.common.util.sign.encrypt.RSA2;
|
||||
import org.dromara.daxpay.unisdk.common.util.str.StringUtils;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.cert.*;
|
||||
import java.sql.Timestamp;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* @author Actinia
|
||||
* <pre>
|
||||
* email hayesfu@qq.com
|
||||
* create 2017 2017/11/5
|
||||
* </pre>
|
||||
*/
|
||||
@Slf4j
|
||||
public class UnionPayService extends BasePayService<UnionPayConfigStorage> {
|
||||
/**
|
||||
* 测试域名
|
||||
*/
|
||||
private static final String TEST_BASE_DOMAIN = "test.95516.com";
|
||||
/**
|
||||
* 正式域名
|
||||
*/
|
||||
private static final String RELEASE_BASE_DOMAIN = "95516.com";
|
||||
/**
|
||||
* 交易请求地址
|
||||
*/
|
||||
private static final String FRONT_TRANS_URL = "https://gateway.%s/gateway/api/frontTransReq.do";
|
||||
private static final String BACK_TRANS_URL = "https://gateway.%s/gateway/api/backTransReq.do";
|
||||
private static final String SINGLE_QUERY_URL = "https://gateway.%s/gateway/api/queryTrans.do";
|
||||
private static final String BATCH_TRANS_URL = "https://gateway.%s/gateway/api/batchTrans.do";
|
||||
private static final String FILE_TRANS_URL = "https://filedownload.%s/";
|
||||
private static final String APP_TRANS_URL = "https://gateway.%s/gateway/api/appTransReq.do";
|
||||
private static final String CARD_TRANS_URL = "https://gateway.%s/gateway/api/cardTransReq.do";
|
||||
/**
|
||||
* 证书解释器
|
||||
*/
|
||||
private volatile CertDescriptor certDescriptor;
|
||||
|
||||
/**
|
||||
* 构造函数
|
||||
*
|
||||
* @param payConfigStorage 支付配置
|
||||
*/
|
||||
public UnionPayService(UnionPayConfigStorage payConfigStorage) {
|
||||
this(payConfigStorage, null);
|
||||
}
|
||||
|
||||
public UnionPayService(UnionPayConfigStorage payConfigStorage, HttpConfigStorage configStorage) {
|
||||
super(payConfigStorage, configStorage);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置支付配置
|
||||
*
|
||||
* @param payConfigStorage 支付配置
|
||||
*/
|
||||
@Override
|
||||
public UnionPayService setPayConfigStorage(UnionPayConfigStorage payConfigStorage) {
|
||||
this.payConfigStorage = payConfigStorage;
|
||||
if (null != certDescriptor) {
|
||||
return this;
|
||||
}
|
||||
try {
|
||||
certDescriptor = new CertDescriptor();
|
||||
certDescriptor.initPrivateSignCert(payConfigStorage.getKeyPrivateCertInputStream(), payConfigStorage.getKeyPrivateCertPwd(), "PKCS12");
|
||||
certDescriptor.initPublicCert(payConfigStorage.getAcpMiddleCertInputStream());
|
||||
certDescriptor.initRootCert(payConfigStorage.getAcpRootCertInputStream());
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取支付请求地址
|
||||
*
|
||||
* @param transactionType 交易类型
|
||||
* @return 请求地址
|
||||
*/
|
||||
@Override
|
||||
public String getReqUrl(TransactionType transactionType) {
|
||||
return (payConfigStorage.isTest() ? TEST_BASE_DOMAIN : RELEASE_BASE_DOMAIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据是否为沙箱环境进行获取请求地址
|
||||
*
|
||||
* @return 请求地址
|
||||
*/
|
||||
public String getReqUrl() {
|
||||
return getReqUrl(null);
|
||||
}
|
||||
|
||||
public String getFrontTransUrl() {
|
||||
return String.format(FRONT_TRANS_URL, getReqUrl());
|
||||
}
|
||||
|
||||
public String getBackTransUrl() {
|
||||
return String.format(BACK_TRANS_URL, getReqUrl());
|
||||
}
|
||||
|
||||
public String getAppTransUrl() {
|
||||
return String.format(APP_TRANS_URL, getReqUrl());
|
||||
}
|
||||
|
||||
public String getSingleQueryUrl() {
|
||||
return String.format(SINGLE_QUERY_URL, getReqUrl());
|
||||
}
|
||||
|
||||
|
||||
public String getFileTransUrl() {
|
||||
return String.format(FILE_TRANS_URL, getReqUrl());
|
||||
}
|
||||
|
||||
/**
|
||||
* 后台通知地址
|
||||
*
|
||||
* @param parameters 预订单信息
|
||||
* @param order 订单
|
||||
* @return 预订单信息
|
||||
*/
|
||||
private Map<String, Object> initNotifyUrl(Map<String, Object> parameters, AssistOrder order) {
|
||||
//后台通知地址
|
||||
OrderParaStructure.loadParameters(parameters, SDKConstants.param_backUrl, payConfigStorage.getNotifyUrl());
|
||||
OrderParaStructure.loadParameters(parameters, SDKConstants.param_backUrl, order.getNotifyUrl());
|
||||
OrderParaStructure.loadParameters(parameters, SDKConstants.param_backUrl, order);
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 银联全渠道系统,产品参数,除了encoding自行选择外其他不需修改
|
||||
*
|
||||
* @return 返回参数集合
|
||||
*/
|
||||
private Map<String, Object> getCommonParam() {
|
||||
Map<String, Object> params = new TreeMap<>();
|
||||
UnionPayConfigStorage configStorage = payConfigStorage;
|
||||
//银联接口版本
|
||||
params.put(SDKConstants.param_version, configStorage.getVersion());
|
||||
//编码方式
|
||||
params.put(SDKConstants.param_encoding, payConfigStorage.getInputCharset().toUpperCase());
|
||||
//商户代码
|
||||
params.put(SDKConstants.param_merId, payConfigStorage.getPid());
|
||||
|
||||
//订单发送时间
|
||||
params.put(SDKConstants.param_txnTime, DateUtils.formatDate(new Date(), DateUtils.YYYYMMDDHHMMSS));
|
||||
//后台通知地址
|
||||
params.put(SDKConstants.param_backUrl, payConfigStorage.getNotifyUrl());
|
||||
|
||||
//交易币种
|
||||
params.put(SDKConstants.param_currencyCode, "156");
|
||||
//接入类型,商户接入填0 ,不需修改(0:直连商户, 1: 收单机构 2:平台商户)
|
||||
params.put(SDKConstants.param_accessType, configStorage.getAccessType());
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 回调校验
|
||||
*
|
||||
* @param result 回调回来的参数集
|
||||
* @return 签名校验 true通过
|
||||
*/
|
||||
@Deprecated
|
||||
@Override
|
||||
public boolean verify(Map<String, Object> result) {
|
||||
|
||||
|
||||
return verify(new NoticeParams(result));
|
||||
}
|
||||
|
||||
/**
|
||||
* 回调校验
|
||||
*
|
||||
* @param noticeParams 回调回来的参数集
|
||||
* @return 签名校验 true通过
|
||||
*/
|
||||
@Override
|
||||
public boolean verify(NoticeParams noticeParams) {
|
||||
final Map<String, Object> result = noticeParams.getBody();
|
||||
if (null == result || result.get(SDKConstants.param_signature) == null) {
|
||||
log.debug("银联支付验签异常:params:" + result);
|
||||
return false;
|
||||
}
|
||||
return this.signVerify(result, (String) result.get(SDKConstants.param_signature));
|
||||
}
|
||||
|
||||
/**
|
||||
* 签名校验
|
||||
*
|
||||
* @param params 参数集
|
||||
* @param sign 签名原文
|
||||
* @return 签名校验 true通过
|
||||
*/
|
||||
public boolean signVerify(Map<String, Object> params, String sign) {
|
||||
SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType());
|
||||
|
||||
String data = SignTextUtils.parameterText(params, "&", "signature");
|
||||
switch (signUtils) {
|
||||
case RSA:
|
||||
data = SignUtils.SHA1.createSign(data, "", payConfigStorage.getInputCharset());
|
||||
return RSA.verify(data, sign, verifyCertificate(genCertificateByStr((String) params.get(SDKConstants.param_signPubKeyCert))).getPublicKey(), payConfigStorage.getInputCharset());
|
||||
case RSA2:
|
||||
data = SignUtils.SHA256.createSign(data, "", payConfigStorage.getInputCharset());
|
||||
return RSA2.verify(data, sign, verifyCertificate(genCertificateByStr((String) params.get(SDKConstants.param_signPubKeyCert))).getPublicKey(), payConfigStorage.getInputCharset());
|
||||
case SHA1:
|
||||
case SHA256:
|
||||
case SM3:
|
||||
String before = signUtils.createSign(payConfigStorage.getKeyPublic(), "", payConfigStorage.getInputCharset());
|
||||
return signUtils.verify(data, sign, "&" + before, payConfigStorage.getInputCharset());
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 订单超时时间。
|
||||
* 超过此时间后,除网银交易外,其他交易银联系统会拒绝受理,提示超时。 跳转银行网银交易如果超时后交易成功,会自动退款,大约5个工作日金额返还到持卡人账户。
|
||||
* 此时间建议取支付时的北京时间加15分钟。
|
||||
* 超过超时时间调查询接口应答origRespCode不是A6或者00的就可以判断为失败。
|
||||
*
|
||||
* @param expirationTime 超时时间
|
||||
* @return 具体的时间字符串
|
||||
*/
|
||||
private String getPayTimeout(Date expirationTime) {
|
||||
//
|
||||
if (null != expirationTime) {
|
||||
return DateUtils.formatDate(expirationTime, DateUtils.YYYYMMDDHHMMSS);
|
||||
}
|
||||
return DateUtils.formatDate(new Timestamp(System.currentTimeMillis() + 30 * 60 * 1000), DateUtils.YYYYMMDDHHMMSS);
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回创建的订单信息
|
||||
*
|
||||
* @param order 支付订单
|
||||
* @return 订单信息
|
||||
* @see PayOrder 支付订单信息
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> orderInfo(PayOrder order) {
|
||||
Map<String, Object> params = this.getCommonParam();
|
||||
|
||||
UnionTransactionType type = (UnionTransactionType) order.getTransactionType();
|
||||
initNotifyUrl(params, order);
|
||||
|
||||
//设置交易类型相关的参数
|
||||
type.convertMap(params);
|
||||
|
||||
params.put(SDKConstants.param_orderId, order.getOutTradeNo());
|
||||
|
||||
if (StringUtils.isNotEmpty(order.getAddition())) {
|
||||
params.put(SDKConstants.param_reqReserved, order.getAddition());
|
||||
}
|
||||
switch (type) {
|
||||
case WAP:
|
||||
case WEB:
|
||||
//todo PCwap网关跳转支付特殊用法.txt
|
||||
case B2B:
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice()));
|
||||
params.put("orderDesc", order.getSubject());
|
||||
params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime()));
|
||||
|
||||
params.put(SDKConstants.param_frontUrl, payConfigStorage.getReturnUrl());
|
||||
break;
|
||||
case CONSUME:
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice()));
|
||||
params.put(SDKConstants.param_qrNo, order.getAuthCode());
|
||||
break;
|
||||
case APPLY_QR_CODE:
|
||||
if (null != order.getPrice()) {
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice()));
|
||||
}
|
||||
params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime()));
|
||||
break;
|
||||
default:
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(order.getPrice()));
|
||||
params.put(SDKConstants.param_payTimeout, getPayTimeout(order.getExpirationTime()));
|
||||
params.put("orderDesc", order.getSubject());
|
||||
}
|
||||
params.putAll(order.getAttrs());
|
||||
params = preOrderHandler(params, order);
|
||||
return setSign(params);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 生成并设置签名
|
||||
*
|
||||
* @param parameters 请求参数
|
||||
* @return 请求参数
|
||||
*/
|
||||
private Map<String, Object> setSign(Map<String, Object> parameters) {
|
||||
|
||||
SignUtils signUtils = SignUtils.valueOf(payConfigStorage.getSignType());
|
||||
|
||||
String signStr;
|
||||
switch (signUtils) {
|
||||
case RSA:
|
||||
parameters.put(SDKConstants.param_signMethod, SDKConstants.SIGNMETHOD_RSA);
|
||||
parameters.put(SDKConstants.param_certId, certDescriptor.getSignCertId());
|
||||
signStr = SignUtils.SHA1.createSign(SignTextUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset());
|
||||
parameters.put(SDKConstants.param_signature, RSA.sign(signStr, certDescriptor.getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset()));
|
||||
break;
|
||||
case RSA2:
|
||||
parameters.put(SDKConstants.param_signMethod, SDKConstants.SIGNMETHOD_RSA);
|
||||
parameters.put(SDKConstants.param_certId, certDescriptor.getSignCertId());
|
||||
signStr = SignUtils.SHA256.createSign(SignTextUtils.parameterText(parameters, "&", "signature"), "", payConfigStorage.getInputCharset());
|
||||
parameters.put(SDKConstants.param_signature, RSA2.sign(signStr, certDescriptor.getSignCertPrivateKey(payConfigStorage.getKeyPrivateCertPwd()), payConfigStorage.getInputCharset()));
|
||||
break;
|
||||
case SHA1:
|
||||
case SHA256:
|
||||
case SM3:
|
||||
String key = payConfigStorage.getKeyPrivate();
|
||||
signStr = SignTextUtils.parameterText(parameters, "&", "signature");
|
||||
key = signUtils.createSign(key, "", payConfigStorage.getInputCharset()) + "&";
|
||||
parameters.put(SDKConstants.param_signature, signUtils.createSign(signStr, key, payConfigStorage.getInputCharset()));
|
||||
break;
|
||||
default:
|
||||
throw new PayErrorException(new PayException("sign fail", "未找到的签名类型"));
|
||||
}
|
||||
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 验证证书链
|
||||
*
|
||||
* @param cert 需要验证的证书
|
||||
*/
|
||||
private X509Certificate verifyCertificate(X509Certificate cert) {
|
||||
try {
|
||||
cert.checkValidity();//验证有效期
|
||||
X509Certificate middleCert = certDescriptor.getPublicCert();
|
||||
X509Certificate rootCert = certDescriptor.getRootCert();
|
||||
|
||||
X509CertSelector selector = new X509CertSelector();
|
||||
selector.setCertificate(cert);
|
||||
|
||||
Set<TrustAnchor> trustAnchors = new HashSet<TrustAnchor>();
|
||||
trustAnchors.add(new TrustAnchor(rootCert, null));
|
||||
PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustAnchors, selector);
|
||||
|
||||
Set<X509Certificate> intermediateCerts = new HashSet<X509Certificate>();
|
||||
intermediateCerts.add(rootCert);
|
||||
intermediateCerts.add(middleCert);
|
||||
intermediateCerts.add(cert);
|
||||
|
||||
pkixParams.setRevocationEnabled(false);
|
||||
|
||||
CertStore intermediateCertStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(intermediateCerts));
|
||||
pkixParams.addCertStore(intermediateCertStore);
|
||||
|
||||
CertPathBuilder builder = CertPathBuilder.getInstance("PKIX");
|
||||
|
||||
/*PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult)*/
|
||||
builder.build(pkixParams);
|
||||
return cert;
|
||||
}
|
||||
catch (java.security.cert.CertPathBuilderException e) {
|
||||
log.error("verify certificate chain fail.", e);
|
||||
}
|
||||
catch (CertificateExpiredException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
catch (GeneralSecurityException e) {
|
||||
log.error("", e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送订单
|
||||
*
|
||||
* @param order 发起支付的订单信息
|
||||
* @return 返回支付结果
|
||||
*/
|
||||
|
||||
public JSONObject postOrder(PayOrder order, String url) {
|
||||
Map<String, Object> params = orderInfo(order);
|
||||
String responseStr = getHttpRequestTemplate().postForObject(url, params, String.class);
|
||||
JSONObject response = UriVariables.getParametersToMap(responseStr);
|
||||
if (response.isEmpty()) {
|
||||
throw new PayErrorException(new PayException("failure", "响应内容有误!", responseStr));
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toPay(PayOrder order) {
|
||||
|
||||
if (null == order.getTransactionType()) {
|
||||
order.setTransactionType(UnionTransactionType.WEB);
|
||||
}
|
||||
else if (UnionTransactionType.WEB != order.getTransactionType() && UnionTransactionType.WAP != order.getTransactionType() && UnionTransactionType.B2B != order.getTransactionType()) {
|
||||
throw new PayErrorException(new PayException("-1", "错误的交易类型:" + order.getTransactionType()));
|
||||
}
|
||||
|
||||
return super.toPay(order);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输出二维码,用户返回给支付端,
|
||||
*
|
||||
* @param order 发起支付的订单信息
|
||||
* @return 返回图片信息,支付时需要的
|
||||
*/
|
||||
@Override
|
||||
public String getQrPay(PayOrder order) {
|
||||
order.setTransactionType(UnionTransactionType.APPLY_QR_CODE);
|
||||
JSONObject response = postOrder(order, getBackTransUrl());
|
||||
if (this.verify(response)) {
|
||||
if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) {
|
||||
//成功
|
||||
return (String) response.get(SDKConstants.param_qrCode);
|
||||
}
|
||||
throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), response.toJSONString()));
|
||||
}
|
||||
throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷卡付,pos主动扫码付款(条码付)
|
||||
*
|
||||
* @param order 发起支付的订单信息
|
||||
* @return 返回支付结果
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> microPay(PayOrder order) {
|
||||
order.setTransactionType(UnionTransactionType.CONSUME);
|
||||
JSONObject response = postOrder(order, getBackTransUrl());
|
||||
return response;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 将字符串转换为X509Certificate对象.
|
||||
*
|
||||
* @param x509CertString 证书串
|
||||
* @return X509Certificate
|
||||
*/
|
||||
public static X509Certificate genCertificateByStr(String x509CertString) {
|
||||
X509Certificate x509Cert = null;
|
||||
try {
|
||||
CertificateFactory cf = CertificateFactory.getInstance("X.509");
|
||||
InputStream tIn = new ByteArrayInputStream(x509CertString.getBytes("ISO-8859-1"));
|
||||
x509Cert = (X509Certificate) cf.generateCertificate(tIn);
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new PayErrorException(new PayException("证书加载失败", "gen certificate error:" + e.getLocalizedMessage()));
|
||||
}
|
||||
return x509Cert;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取输出消息,用户返回给支付端
|
||||
*
|
||||
* @param code 状态
|
||||
* @param message 消息
|
||||
* @return 返回输出消息
|
||||
*/
|
||||
@Override
|
||||
public PayOutMessage getPayOutMessage(String code, String message) {
|
||||
return PayTextOutMessage.TEXT().content(code.toLowerCase()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取成功输出消息,用户返回给支付端
|
||||
* 主要用于拦截器中返回
|
||||
*
|
||||
* @param payMessage 支付回调消息
|
||||
* @return 返回输出消息
|
||||
*/
|
||||
@Override
|
||||
public PayOutMessage successPayOutMessage(PayMessage payMessage) {
|
||||
return getPayOutMessage("ok", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能:生成自动跳转的Html表单
|
||||
*
|
||||
* @param orderInfo 发起支付的订单信息
|
||||
* @param method 请求方式 "post" "get",
|
||||
* @return 生成自动跳转的Html表单返回给支付端, 针对于PC端
|
||||
* @see MethodType 请求类型
|
||||
*/
|
||||
@Override
|
||||
public String buildRequest(Map<String, Object> orderInfo, MethodType method) {
|
||||
StringBuffer sf = new StringBuffer();
|
||||
sf.append("<html><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + payConfigStorage.getInputCharset() + "\"/></head><body>");
|
||||
sf.append("<form id = \"pay_form\" action=\"" + getFrontTransUrl() + "\" method=\"post\">");
|
||||
if (null != orderInfo && 0 != orderInfo.size()) {
|
||||
for (Map.Entry<String, Object> entry : orderInfo.entrySet()) {
|
||||
String key = entry.getKey();
|
||||
Object value = entry.getValue();
|
||||
sf.append("<input type=\"hidden\" name=\"" + key + "\" id=\"" + key + "\" value=\"" + value + "\"/>");
|
||||
}
|
||||
}
|
||||
sf.append("</form>");
|
||||
sf.append("</body>");
|
||||
sf.append("<script type=\"text/javascript\">");
|
||||
sf.append("document.all.pay_form.submit();");
|
||||
sf.append("</script>");
|
||||
sf.append("</html>");
|
||||
return sf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 功能:将订单信息进行签名并提交请求
|
||||
* 业务范围:手机支付控件(含安卓Pay)
|
||||
*
|
||||
* @param order 订单信息
|
||||
* @return 成功:返回支付结果 失败:返回
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> app(PayOrder order) {
|
||||
if (null == order.getTransactionType()) {
|
||||
order.setTransactionType(UnionTransactionType.APP);
|
||||
}
|
||||
JSONObject response = postOrder(order, getAppTransUrl());
|
||||
if (this.verify(response)) {
|
||||
if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) {
|
||||
// //成功,获取tn号
|
||||
// String tn = (String)response.get(SDKConstants.param_tn);
|
||||
// //TODO
|
||||
return response;
|
||||
}
|
||||
throw new PayErrorException(new PayException((String) response.get(SDKConstants.param_respCode), (String) response.get(SDKConstants.param_respMsg), response.toJSONString()));
|
||||
}
|
||||
throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易查询接口
|
||||
*
|
||||
* @param tradeNo 支付平台订单号
|
||||
* @param outTradeNo 商户单号
|
||||
* @return 返回查询回来的结果集,支付方原值返回
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> query(String tradeNo, String outTradeNo) {
|
||||
return query(new AssistOrder(tradeNo, outTradeNo));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易查询接口
|
||||
*
|
||||
* @param assistOrder 查询条件
|
||||
* @return 返回查询回来的结果集,支付方原值返回
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> query(AssistOrder assistOrder) {
|
||||
Map<String, Object> params = this.getCommonParam();
|
||||
UnionTransactionType.QUERY.convertMap(params);
|
||||
params.put(SDKConstants.param_orderId, assistOrder.getOutTradeNo());
|
||||
this.setSign(params);
|
||||
String responseStr = getHttpRequestTemplate().postForObject(this.getSingleQueryUrl(), params, String.class);
|
||||
JSONObject response = UriVariables.getParametersToMap(responseStr);
|
||||
if (this.verify(new NoticeParams(response))) {
|
||||
if (SDKConstants.OK_RESP_CODE.equals(response.getString(SDKConstants.param_respCode))) {
|
||||
String origRespCode = response.getString(SDKConstants.param_origRespCode);
|
||||
if ((SDKConstants.OK_RESP_CODE).equals(origRespCode)) {
|
||||
//交易成功,更新商户订单状态
|
||||
return response;
|
||||
}
|
||||
}
|
||||
throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString()));
|
||||
}
|
||||
throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 消费撤销/退货接口
|
||||
*
|
||||
* @param origQryId 原交易查询流水号.
|
||||
* @param orderId 退款单号
|
||||
* @param refundAmount 退款金额
|
||||
* @param type UnionTransactionType.REFUND 或者UnionTransactionType.CONSUME_UNDO
|
||||
* @return 返回支付方申请退款后的结果
|
||||
*/
|
||||
public UnionRefundResult unionRefundOrConsumeUndo(String origQryId, String orderId, BigDecimal refundAmount, UnionTransactionType type) {
|
||||
return unionRefundOrConsumeUndo(new RefundOrder(orderId, origQryId, refundAmount), type);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 消费撤销/退货接口
|
||||
*
|
||||
* @param refundOrder 退款订单信息
|
||||
* @param type UnionTransactionType.REFUND 或者UnionTransactionType.CONSUME_UNDO
|
||||
* @return 返回支付方申请退款后的结果
|
||||
*/
|
||||
public UnionRefundResult unionRefundOrConsumeUndo(RefundOrder refundOrder, UnionTransactionType type) {
|
||||
Map<String, Object> params = this.getCommonParam();
|
||||
type.convertMap(params);
|
||||
params.put(SDKConstants.param_orderId, refundOrder.getRefundNo());
|
||||
params.put(SDKConstants.param_txnAmt, Util.conversionCentAmount(refundOrder.getRefundAmount()));
|
||||
params.put(SDKConstants.param_origQryId, refundOrder.getTradeNo());
|
||||
params.putAll(refundOrder.getAttrs());
|
||||
this.setSign(params);
|
||||
String responseStr = getHttpRequestTemplate().postForObject(this.getBackTransUrl(), params, String.class);
|
||||
JSONObject response = UriVariables.getParametersToMap(responseStr);
|
||||
|
||||
if (this.verify(new NoticeParams(response))) {
|
||||
final UnionRefundResult refundResult = UnionRefundResult.create(response);
|
||||
if (SDKConstants.OK_RESP_CODE.equals(refundResult.getRespCode())) {
|
||||
return refundResult;
|
||||
|
||||
}
|
||||
throw new PayErrorException(new PayException(response.getString(SDKConstants.param_respCode), response.getString(SDKConstants.param_respMsg), response.toJSONString()));
|
||||
}
|
||||
throw new PayErrorException(new PayException("failure", "验证签名失败", response.toJSONString()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易关闭接口
|
||||
*
|
||||
* @param tradeNo 支付平台订单号
|
||||
* @param outTradeNo 商户单号
|
||||
* @return 返回支付方交易关闭后的结果
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> close(String tradeNo, String outTradeNo) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 交易关闭接口
|
||||
*
|
||||
* @param assistOrder 关闭订单
|
||||
* @return 返回支付方交易关闭后的结果
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> close(AssistOrder assistOrder) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnionRefundResult refund(RefundOrder refundOrder) {
|
||||
return unionRefundOrConsumeUndo(refundOrder, UnionTransactionType.REFUND);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询退款
|
||||
*
|
||||
* @param refundOrder 退款订单单号信息
|
||||
* @return 返回支付方查询退款后的结果
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> refundquery(RefundOrder refundOrder) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单
|
||||
*
|
||||
* @param billDate 账单时间
|
||||
* @param fileType 文件类型 文件类型,一般商户填写00即可
|
||||
* @return 返回fileContent 请自行将数据落地
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> downloadBill(Date billDate, String fileType) {
|
||||
return downloadBill(billDate, new UnionPayBillType(fileType));
|
||||
}
|
||||
|
||||
/**
|
||||
* 下载对账单
|
||||
*
|
||||
* @param billDate 账单时间
|
||||
* @param billType 账单类型
|
||||
* @return 返回fileContent 请自行将数据落地
|
||||
*/
|
||||
@Override
|
||||
public Map<String, Object> downloadBill(Date billDate, BillType billType) {
|
||||
|
||||
Map<String, Object> params = this.getCommonParam();
|
||||
UnionTransactionType.FILE_TRANSFER.convertMap(params);
|
||||
|
||||
params.put(SDKConstants.param_settleDate, DateUtils.formatDate(billDate, DateUtils.MMDD));
|
||||
params.put(SDKConstants.param_fileType, billType.getFileType());
|
||||
params.remove(SDKConstants.param_backUrl);
|
||||
params.remove(SDKConstants.param_currencyCode);
|
||||
this.setSign(params);
|
||||
String responseStr = getHttpRequestTemplate().postForObject(this.getFileTransUrl(), params, String.class);
|
||||
JSONObject response = UriVariables.getParametersToMap(responseStr);
|
||||
if (this.verify(response)) {
|
||||
if (SDKConstants.OK_RESP_CODE.equals(response.get(SDKConstants.param_respCode))) {
|
||||
return response;
|
||||
|
||||
}
|
||||
throw new PayErrorException(new PayException(response.get(SDKConstants.param_respCode).toString(), response.get(SDKConstants.param_respMsg).toString(), response.toString()));
|
||||
|
||||
}
|
||||
throw new PayErrorException(new PayException("failure", "验证签名失败", response.toString()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建消息
|
||||
*
|
||||
* @param message 支付平台返回的消息
|
||||
* @return 支付消息对象
|
||||
*/
|
||||
@Override
|
||||
public PayMessage createMessage(Map<String, Object> message) {
|
||||
return UnionPayMessage.create(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,280 @@
|
||||
/**
|
||||
* Licensed Property to China UnionPay Co., Ltd.
|
||||
* <p>
|
||||
* (C) Copyright of China UnionPay Co., Ltd. 2010
|
||||
* All Rights Reserved.
|
||||
* <p>
|
||||
* <p>
|
||||
* Modification History:
|
||||
* =============================================================================
|
||||
* Author Date Description
|
||||
* ------------ ---------- ---------------------------------------------------
|
||||
* xshu 2014-05-28 MPI插件包常量定义
|
||||
* =============================================================================
|
||||
*/
|
||||
package org.dromara.daxpay.channel.union.sdk.bean;
|
||||
|
||||
/**
|
||||
*
|
||||
* acpsdk常量类
|
||||
*
|
||||
* date 2016-7-22 下午4:05:54
|
||||
*
|
||||
*/
|
||||
public class SDKConstants {
|
||||
|
||||
public static final String SIGNMETHOD_RSA = "01";
|
||||
public static final String OK_RESP_CODE = "00";
|
||||
public static final String UNIONPAY_CNNAME = "中国银联股份有限公司";
|
||||
|
||||
/******************************************** 5.0报文接口定义 ********************************************/
|
||||
/** 版本号. */
|
||||
public static final String param_version = "version";
|
||||
/** 证书ID. */
|
||||
public static final String param_certId = "certId";
|
||||
/** 签名. */
|
||||
public static final String param_signature = "signature";
|
||||
/** 签名方法. */
|
||||
public static final String param_signMethod = "signMethod";
|
||||
/** 编码方式. */
|
||||
public static final String param_encoding = "encoding";
|
||||
/** 交易类型. */
|
||||
public static final String param_txnType = "txnType";
|
||||
/** 交易子类. */
|
||||
public static final String param_txnSubType = "txnSubType";
|
||||
/** 业务类型. */
|
||||
public static final String param_bizType = "bizType";
|
||||
/** 前台通知地址 . */
|
||||
public static final String param_frontUrl = "frontUrl";
|
||||
/** 后台通知地址. */
|
||||
public static final String param_backUrl = "backUrl";
|
||||
/** 接入类型. */
|
||||
public static final String param_accessType = "accessType";
|
||||
/** 收单机构代码. */
|
||||
public static final String param_acqInsCode = "acqInsCode";
|
||||
/** 商户类别. */
|
||||
public static final String param_merCatCode = "merCatCode";
|
||||
/** 商户类型. */
|
||||
public static final String param_merType = "merType";
|
||||
/** 商户代码. */
|
||||
public static final String param_merId = "merId";
|
||||
/** 商户名称. */
|
||||
public static final String param_merName = "merName";
|
||||
/** 商户简称. */
|
||||
public static final String param_merAbbr = "merAbbr";
|
||||
/** 二级商户代码. */
|
||||
public static final String param_subMerId = "subMerId";
|
||||
/** 二级商户名称. */
|
||||
public static final String param_subMerName = "subMerName";
|
||||
/** 二级商户简称. */
|
||||
public static final String param_subMerAbbr = "subMerAbbr";
|
||||
/** Cupsecure 商户代码. */
|
||||
public static final String param_csMerId = "csMerId";
|
||||
/** 商户订单号. */
|
||||
public static final String param_orderId = "orderId";
|
||||
/** 交易时间. */
|
||||
public static final String param_txnTime = "txnTime";
|
||||
/** 发送时间. */
|
||||
public static final String param_txnSendTime = "txnSendTime";
|
||||
/** 订单超时时间间隔. */
|
||||
public static final String param_orderTimeoutInterval = "orderTimeoutInterval";
|
||||
/** 支付超时时间. */
|
||||
public static final String param_payTimeout = "payTimeout";
|
||||
/** 默认支付方式. */
|
||||
public static final String param_defaultPayType = "defaultPayType";
|
||||
/** 支持支付方式. */
|
||||
public static final String param_supPayType = "supPayType";
|
||||
/** 支付方式. */
|
||||
public static final String param_payType = "payType";
|
||||
/** 自定义支付方式. */
|
||||
public static final String param_customPayType = "customPayType";
|
||||
/** 物流标识. */
|
||||
public static final String param_shippingFlag = "shippingFlag";
|
||||
/** 收货地址-国家. */
|
||||
public static final String param_shippingCountryCode = "shippingCountryCode";
|
||||
/** 收货地址-省. */
|
||||
public static final String param_shippingProvinceCode = "shippingProvinceCode";
|
||||
/** 收货地址-市. */
|
||||
public static final String param_shippingCityCode = "shippingCityCode";
|
||||
/** 收货地址-地区. */
|
||||
public static final String param_shippingDistrictCode = "shippingDistrictCode";
|
||||
/** 收货地址-详细. */
|
||||
public static final String param_shippingStreet = "shippingStreet";
|
||||
/** 商品总类. */
|
||||
public static final String param_commodityCategory = "commodityCategory";
|
||||
/** 商品名称. */
|
||||
public static final String param_commodityName = "commodityName";
|
||||
/** 商品URL. */
|
||||
public static final String param_commodityUrl = "commodityUrl";
|
||||
/** 商品单价. */
|
||||
public static final String param_commodityUnitPrice = "commodityUnitPrice";
|
||||
/** 商品数量. */
|
||||
public static final String param_commodityQty = "commodityQty";
|
||||
/** 是否预授权. */
|
||||
public static final String param_isPreAuth = "isPreAuth";
|
||||
/** 币种. */
|
||||
public static final String param_currencyCode = "currencyCode";
|
||||
/** 账户类型. */
|
||||
public static final String param_accType = "accType";
|
||||
/** 账号. */
|
||||
public static final String param_accNo = "accNo";
|
||||
/** 支付卡类型. */
|
||||
public static final String param_payCardType = "payCardType";
|
||||
/** 发卡机构代码. */
|
||||
public static final String param_issInsCode = "issInsCode";
|
||||
/** 持卡人信息. */
|
||||
public static final String param_customerInfo = "customerInfo";
|
||||
/** 交易金额. */
|
||||
public static final String param_txnAmt = "txnAmt";
|
||||
/** 余额. */
|
||||
public static final String param_balance = "balance";
|
||||
/** 地区代码. */
|
||||
public static final String param_districtCode = "districtCode";
|
||||
/** 附加地区代码. */
|
||||
public static final String param_additionalDistrictCode = "additionalDistrictCode";
|
||||
/** 账单类型. */
|
||||
public static final String param_billType = "billType";
|
||||
/** 账单号码. */
|
||||
public static final String param_billNo = "billNo";
|
||||
/** 账单月份. */
|
||||
public static final String param_billMonth = "billMonth";
|
||||
/** 账单查询要素. */
|
||||
public static final String param_billQueryInfo = "billQueryInfo";
|
||||
/** 账单详情. */
|
||||
public static final String param_billDetailInfo = "billDetailInfo";
|
||||
/** 账单金额. */
|
||||
public static final String param_billAmt = "billAmt";
|
||||
/** 账单金额符号. */
|
||||
public static final String param_billAmtSign = "billAmtSign";
|
||||
/** 绑定标识号. */
|
||||
public static final String param_bindId = "bindId";
|
||||
/** 风险级别. */
|
||||
public static final String param_riskLevel = "riskLevel";
|
||||
/** 绑定信息条数. */
|
||||
public static final String param_bindInfoQty = "bindInfoQty";
|
||||
/** 绑定信息集. */
|
||||
public static final String param_bindInfoList = "bindInfoList";
|
||||
/** 批次号. */
|
||||
public static final String param_batchNo = "batchNo";
|
||||
/** 总笔数. */
|
||||
public static final String param_totalQty = "totalQty";
|
||||
/** 总金额. */
|
||||
public static final String param_totalAmt = "totalAmt";
|
||||
/** 文件类型. */
|
||||
public static final String param_fileType = "fileType";
|
||||
/** 文件名称. */
|
||||
public static final String param_fileName = "fileName";
|
||||
/** 批量文件内容. */
|
||||
public static final String param_fileContent = "fileContent";
|
||||
/** 商户摘要. */
|
||||
public static final String param_merNote = "merNote";
|
||||
/** 商户自定义域. */
|
||||
// public static final String param_merReserved = "merReserved";//接口变更删除
|
||||
/** 请求方保留域. */
|
||||
public static final String param_reqReserved = "reqReserved";// 新增接口
|
||||
/** 保留域. */
|
||||
public static final String param_reserved = "reserved";
|
||||
/** 终端号. */
|
||||
public static final String param_termId = "termId";
|
||||
/** 终端类型. */
|
||||
public static final String param_termType = "termType";
|
||||
/** 交互模式. */
|
||||
public static final String param_interactMode = "interactMode";
|
||||
/** 发卡机构识别模式. */
|
||||
// public static final String param_recognitionMode = "recognitionMode";
|
||||
public static final String param_issuerIdentifyMode = "issuerIdentifyMode";// 接口名称变更
|
||||
/** 商户端用户号. */
|
||||
public static final String param_merUserId = "merUserId";
|
||||
/** 持卡人IP. */
|
||||
public static final String param_customerIp = "customerIp";
|
||||
/** 查询流水号. */
|
||||
public static final String param_queryId = "queryId";
|
||||
/** 原交易查询流水号. */
|
||||
public static final String param_origQryId = "origQryId";
|
||||
/** 系统跟踪号. */
|
||||
public static final String param_traceNo = "traceNo";
|
||||
/** 交易传输时间. */
|
||||
public static final String param_traceTime = "traceTime";
|
||||
/** 清算日期. */
|
||||
public static final String param_settleDate = "settleDate";
|
||||
/** 清算币种. */
|
||||
public static final String param_settleCurrencyCode = "settleCurrencyCode";
|
||||
/** 清算金额. */
|
||||
public static final String param_settleAmt = "settleAmt";
|
||||
/** 清算汇率. */
|
||||
public static final String param_exchangeRate = "exchangeRate";
|
||||
/** 兑换日期. */
|
||||
public static final String param_exchangeDate = "exchangeDate";
|
||||
/** 响应时间. */
|
||||
public static final String param_respTime = "respTime";
|
||||
/** 原交易应答码. */
|
||||
public static final String param_origRespCode = "origRespCode";
|
||||
/** 原交易应答信息. */
|
||||
public static final String param_origRespMsg = "origRespMsg";
|
||||
/** 应答码. */
|
||||
public static final String param_respCode = "respCode";
|
||||
/** 应答码信息. */
|
||||
public static final String param_respMsg = "respMsg";
|
||||
// 新增四个报文字段merUserRegDt merUserEmail checkFlag activateStatus
|
||||
/** 商户端用户注册时间. */
|
||||
public static final String param_merUserRegDt = "merUserRegDt";
|
||||
/** 商户端用户注册邮箱. */
|
||||
public static final String param_merUserEmail = "merUserEmail";
|
||||
/** 验证标识. */
|
||||
public static final String param_checkFlag = "checkFlag";
|
||||
/** 开通状态. */
|
||||
public static final String param_activateStatus = "activateStatus";
|
||||
/** 加密证书ID. */
|
||||
public static final String param_encryptCertId = "encryptCertId";
|
||||
/** 用户MAC、IMEI串号、SSID. */
|
||||
public static final String param_userMac = "userMac";
|
||||
/** 关联交易. */
|
||||
// public static final String param_relationTxnType = "relationTxnType";
|
||||
/** 短信类型 */
|
||||
public static final String param_smsType = "smsType";
|
||||
|
||||
/** 风控信息域 */
|
||||
public static final String param_riskCtrlInfo = "riskCtrlInfo";
|
||||
|
||||
/** IC卡交易信息域 */
|
||||
public static final String param_ICTransData = "ICTransData";
|
||||
|
||||
/** VPC交易信息域 */
|
||||
public static final String param_VPCTransData = "VPCTransData";
|
||||
|
||||
/** 安全类型 */
|
||||
public static final String param_securityType = "securityType";
|
||||
|
||||
/** 银联订单号 */
|
||||
public static final String param_tn = "tn";
|
||||
|
||||
/** 分期付款手续费率 */
|
||||
public static final String param_instalRate = "instalRate";
|
||||
|
||||
/** 分期付款手续费率 */
|
||||
public static final String param_mchntFeeSubsidy = "mchntFeeSubsidy";
|
||||
|
||||
/** 签名公钥证书 */
|
||||
public static final String param_signPubKeyCert = "signPubKeyCert";
|
||||
|
||||
/** 加密公钥证书 */
|
||||
public static final String param_encryptPubKeyCert = "encryptPubKeyCert";
|
||||
|
||||
/** 证书类型 */
|
||||
public static final String param_certType = "certType";
|
||||
|
||||
/** 渠道类型*/
|
||||
public static final String param_channelType = "channelType";
|
||||
|
||||
/** C2B码,1-20位数字*/
|
||||
public static final String param_qrNo = "qrNo";
|
||||
|
||||
/** 二维码url */
|
||||
public static final String param_qrCode = "qrCode";
|
||||
|
||||
/*原交易商户订单号*/
|
||||
public static final String param_origOrderId = "origOrderId";
|
||||
|
||||
/*原交易商户发送交易时间*/
|
||||
public static final String param_origTxnTime = "origTxnTime";
|
||||
}
|
@@ -0,0 +1,71 @@
|
||||
package org.dromara.daxpay.channel.union.sdk.bean;
|
||||
|
||||
import org.dromara.daxpay.unisdk.common.bean.BillType;
|
||||
import org.dromara.daxpay.unisdk.common.util.str.StringUtils;
|
||||
|
||||
/**
|
||||
* 银联账单类型
|
||||
*
|
||||
* @author Egan
|
||||
* <pre>
|
||||
* email egzosn@gmail.com
|
||||
* date 2021/2/23
|
||||
* </pre>
|
||||
*/
|
||||
public class UnionPayBillType implements BillType {
|
||||
|
||||
private String fileType = "00";
|
||||
|
||||
/**
|
||||
* 获取类型名称
|
||||
*
|
||||
* @return 类型
|
||||
*/
|
||||
@Override
|
||||
public String getType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取类型对应的日期格式化表达式
|
||||
*
|
||||
* @return 日期格式化表达式
|
||||
*/
|
||||
@Override
|
||||
public String getDatePattern() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件类型
|
||||
*
|
||||
* @return 文件类型
|
||||
*/
|
||||
@Override
|
||||
public String getFileType() {
|
||||
return fileType;
|
||||
}
|
||||
|
||||
public void setFileType(String fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
/**
|
||||
* 自定义属性
|
||||
*
|
||||
* @return 自定义属性
|
||||
*/
|
||||
@Override
|
||||
public String getCustom() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public UnionPayBillType() {
|
||||
}
|
||||
|
||||
public UnionPayBillType(String fileType) {
|
||||
if (StringUtils.isNotEmpty(fileType)) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,354 @@
|
||||
package org.dromara.daxpay.channel.union.sdk.bean;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import org.dromara.daxpay.unisdk.common.bean.PayMessage;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 银联回调信息
|
||||
*
|
||||
* @author egan
|
||||
* <pre>
|
||||
* email egzosn@gmail.com
|
||||
* date 2019/7/3.22:07
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
public class UnionPayMessage extends PayMessage {
|
||||
// 查询流水号 queryId AN20..21 M-必填 消费交易的流水号,供后续查询用
|
||||
private String queryId;
|
||||
// 交易币种 currencyCode AN3 M-必填 默认为156
|
||||
private String currencyCode;
|
||||
// 交易传输时间 traceTime MMDDhhmmss M-必填
|
||||
@JSONField(name = "traceTime", format = "MMDDhhmmss")
|
||||
private String traceTime;
|
||||
// 签名 signature ANS1..1024 M-必填
|
||||
private String signature;
|
||||
// 签名方法 signMethod N2 M-必填
|
||||
private String signMethod;
|
||||
// 清算币种 settleCurrencyCode AN3 M-必填
|
||||
private String settleCurrencyCode;
|
||||
// 清算金额 settleAmt N1..12 M-必填
|
||||
private BigDecimal settleAmt;
|
||||
// 清算日期 settleDate MMDD M-必填
|
||||
@JSONField(name = "settleDate", format = "MMDD")
|
||||
private String settleDate;
|
||||
// 系统跟踪号 traceNo N6 M-必填
|
||||
private String traceNo;
|
||||
// 应答码 respCode AN2 M-必填
|
||||
private String respCode;
|
||||
// 应答信息 respMsg ANS1..256 M-必填
|
||||
private String respMsg;
|
||||
// 兑换日期 exchangeDate MMDD C-按条件必填 交易成功,交易币种和清算币种不一致的时候返回
|
||||
@JSONField(name = "exchangeDate", format = "MMDD")
|
||||
private String exchangeDate;
|
||||
// 签名公钥证书 signPubKeyCert AN1..2048 C-按条件必填 使用RSA签名方式时必选,此域填写银联签名公钥证书。
|
||||
private String signPubKeyCert;
|
||||
// 清算汇率 exchangeRate N8 C-按条件必填 交易成功,交易币种和清算币种不一致的时候返回
|
||||
private String exchangeRate;
|
||||
// 账号 accNo AN1..1024 C-按条件必填 根据商户配置返回
|
||||
private String accNo;
|
||||
// 支付方式 payType N4 C-按条件必填 根据商户配置返回
|
||||
private String payType;
|
||||
// 支付卡标识 payCardNo ANS1..19 C-按条件必填 移动支付交易时,根据商户配置返回
|
||||
private String payCardNo;
|
||||
// 支付卡类型 payCardType N2 C-按条件必填 根据商户配置返回
|
||||
private String payCardType;
|
||||
// 支付卡名称 payCardIssueName ANS1..64 C-按条件必填 移动支付交易时,根据商户配置返回
|
||||
private String payCardIssueName;
|
||||
// 版本号 version NS5 R-需要返回
|
||||
private String version;
|
||||
// 绑定标识号 bindId ANS1..128 R-需要返回 绑定支付时,根据商户配置返回
|
||||
private String bindId;
|
||||
// 编码方式 encoding ANS1..20 R-需要返回
|
||||
private String encoding;
|
||||
// 产品类型 bizType N6 R-需要返回
|
||||
private String bizType;
|
||||
// 订单发送时间 txnTime YYYYMMDDhhmmss R-需要返回
|
||||
@JSONField(name = "txnTime", format = "YYYYMMDDhhmmss")
|
||||
private String txnTime;
|
||||
// 交易金额 txnAmt N1..12 R-需要返回
|
||||
private BigDecimal txnAmt;
|
||||
// 交易类型 txnType N2 R-需要返回
|
||||
private String txnType;
|
||||
// 交易子类 txnSubType N2 R-需要返回
|
||||
private String txnSubType;
|
||||
// 接入类型 accessType N1 R-需要返回 0:商户直连接入 1:收单机构接入2:平台商户接入
|
||||
private String accessType;
|
||||
// 请求方保留域 reqReserved ANS1..1024 R-需要返回
|
||||
private String reqReserved;
|
||||
// 商户代码 merId AN15 R-需要返回
|
||||
private String merId;
|
||||
// 商户订单号 orderId AN8..40 R-需要返回 商户订单号,不能含“-”或“_”; 商户自定义,同一交易日期内不可重复; 商户代码merId、商户订单号orderId、订单发送时间txnTime三要素唯一确定一笔交易。 保留域 reserved ANS1..2048O- 选填 查看详情
|
||||
private String orderId;
|
||||
// 分账域 accSplitData ANS1..512O- 选填 查看详情
|
||||
private String accSplitData;
|
||||
|
||||
public String getQueryId() {
|
||||
return queryId;
|
||||
}
|
||||
|
||||
public void setQueryId(String queryId) {
|
||||
this.queryId = queryId;
|
||||
}
|
||||
|
||||
public String getCurrencyCode() {
|
||||
return currencyCode;
|
||||
}
|
||||
|
||||
public void setCurrencyCode(String currencyCode) {
|
||||
this.currencyCode = currencyCode;
|
||||
}
|
||||
|
||||
public String getTraceTime() {
|
||||
return traceTime;
|
||||
}
|
||||
|
||||
public void setTraceTime(String traceTime) {
|
||||
this.traceTime = traceTime;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(String signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getSignMethod() {
|
||||
return signMethod;
|
||||
}
|
||||
|
||||
public void setSignMethod(String signMethod) {
|
||||
this.signMethod = signMethod;
|
||||
}
|
||||
|
||||
public String getSettleCurrencyCode() {
|
||||
return settleCurrencyCode;
|
||||
}
|
||||
|
||||
public void setSettleCurrencyCode(String settleCurrencyCode) {
|
||||
this.settleCurrencyCode = settleCurrencyCode;
|
||||
}
|
||||
|
||||
public BigDecimal getSettleAmt() {
|
||||
return settleAmt;
|
||||
}
|
||||
|
||||
public void setSettleAmt(BigDecimal settleAmt) {
|
||||
this.settleAmt = settleAmt;
|
||||
}
|
||||
|
||||
public String getSettleDate() {
|
||||
return settleDate;
|
||||
}
|
||||
|
||||
public void setSettleDate(String settleDate) {
|
||||
this.settleDate = settleDate;
|
||||
}
|
||||
|
||||
public String getTraceNo() {
|
||||
return traceNo;
|
||||
}
|
||||
|
||||
public void setTraceNo(String traceNo) {
|
||||
this.traceNo = traceNo;
|
||||
}
|
||||
|
||||
public String getRespCode() {
|
||||
return respCode;
|
||||
}
|
||||
|
||||
public void setRespCode(String respCode) {
|
||||
this.respCode = respCode;
|
||||
}
|
||||
|
||||
public String getRespMsg() {
|
||||
return respMsg;
|
||||
}
|
||||
|
||||
public void setRespMsg(String respMsg) {
|
||||
this.respMsg = respMsg;
|
||||
}
|
||||
|
||||
public String getExchangeDate() {
|
||||
return exchangeDate;
|
||||
}
|
||||
|
||||
public void setExchangeDate(String exchangeDate) {
|
||||
this.exchangeDate = exchangeDate;
|
||||
}
|
||||
|
||||
public String getSignPubKeyCert() {
|
||||
return signPubKeyCert;
|
||||
}
|
||||
|
||||
public void setSignPubKeyCert(String signPubKeyCert) {
|
||||
this.signPubKeyCert = signPubKeyCert;
|
||||
}
|
||||
|
||||
public String getExchangeRate() {
|
||||
return exchangeRate;
|
||||
}
|
||||
|
||||
public void setExchangeRate(String exchangeRate) {
|
||||
this.exchangeRate = exchangeRate;
|
||||
}
|
||||
|
||||
public String getAccNo() {
|
||||
return accNo;
|
||||
}
|
||||
|
||||
public void setAccNo(String accNo) {
|
||||
this.accNo = accNo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPayType() {
|
||||
return payType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setPayType(String payType) {
|
||||
this.payType = payType;
|
||||
}
|
||||
|
||||
public String getPayCardNo() {
|
||||
return payCardNo;
|
||||
}
|
||||
|
||||
public void setPayCardNo(String payCardNo) {
|
||||
this.payCardNo = payCardNo;
|
||||
}
|
||||
|
||||
public String getPayCardType() {
|
||||
return payCardType;
|
||||
}
|
||||
|
||||
public void setPayCardType(String payCardType) {
|
||||
this.payCardType = payCardType;
|
||||
}
|
||||
|
||||
public String getPayCardIssueName() {
|
||||
return payCardIssueName;
|
||||
}
|
||||
|
||||
public void setPayCardIssueName(String payCardIssueName) {
|
||||
this.payCardIssueName = payCardIssueName;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getBindId() {
|
||||
return bindId;
|
||||
}
|
||||
|
||||
public void setBindId(String bindId) {
|
||||
this.bindId = bindId;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public void setEncoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public String getBizType() {
|
||||
return bizType;
|
||||
}
|
||||
|
||||
public void setBizType(String bizType) {
|
||||
this.bizType = bizType;
|
||||
}
|
||||
|
||||
public String getTxnTime() {
|
||||
return txnTime;
|
||||
}
|
||||
|
||||
public void setTxnTime(String txnTime) {
|
||||
this.txnTime = txnTime;
|
||||
}
|
||||
|
||||
public BigDecimal getTxnAmt() {
|
||||
return txnAmt;
|
||||
}
|
||||
|
||||
public void setTxnAmt(BigDecimal txnAmt) {
|
||||
this.txnAmt = txnAmt;
|
||||
}
|
||||
|
||||
public String getTxnType() {
|
||||
return txnType;
|
||||
}
|
||||
|
||||
public void setTxnType(String txnType) {
|
||||
this.txnType = txnType;
|
||||
}
|
||||
|
||||
public String getTxnSubType() {
|
||||
return txnSubType;
|
||||
}
|
||||
|
||||
public void setTxnSubType(String txnSubType) {
|
||||
this.txnSubType = txnSubType;
|
||||
}
|
||||
|
||||
public String getAccessType() {
|
||||
return accessType;
|
||||
}
|
||||
|
||||
public void setAccessType(String accessType) {
|
||||
this.accessType = accessType;
|
||||
}
|
||||
|
||||
public String getReqReserved() {
|
||||
return reqReserved;
|
||||
}
|
||||
|
||||
public void setReqReserved(String reqReserved) {
|
||||
this.reqReserved = reqReserved;
|
||||
}
|
||||
|
||||
public String getMerId() {
|
||||
return merId;
|
||||
}
|
||||
|
||||
public void setMerId(String merId) {
|
||||
this.merId = merId;
|
||||
}
|
||||
|
||||
public String getOrderId() {
|
||||
return orderId;
|
||||
}
|
||||
|
||||
public void setOrderId(String orderId) {
|
||||
this.orderId = orderId;
|
||||
}
|
||||
|
||||
public String getAccSplitData() {
|
||||
return accSplitData;
|
||||
}
|
||||
|
||||
public void setAccSplitData(String accSplitData) {
|
||||
this.accSplitData = accSplitData;
|
||||
}
|
||||
|
||||
public static final UnionPayMessage create(Map<String, Object> message) {
|
||||
UnionPayMessage payMessage = new JSONObject(message).toJavaObject(UnionPayMessage.class);
|
||||
payMessage.setPayMessage(message);
|
||||
return payMessage;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,326 @@
|
||||
package org.dromara.daxpay.channel.union.sdk.bean;
|
||||
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import org.dromara.daxpay.unisdk.common.bean.BaseRefundResult;
|
||||
import org.dromara.daxpay.unisdk.common.bean.CurType;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 银联退款结果
|
||||
*
|
||||
* @author Egan
|
||||
* email egzosn@gmail.com
|
||||
* date 2020/8/16 22:15
|
||||
*/
|
||||
public class UnionRefundResult extends BaseRefundResult {
|
||||
|
||||
/**
|
||||
* 二维码数据
|
||||
*/
|
||||
private String qrCode;
|
||||
/**
|
||||
* 签名
|
||||
*/
|
||||
private String signature;
|
||||
/**
|
||||
* 签名方法
|
||||
*/
|
||||
private String signMethod;
|
||||
/**
|
||||
* 应答码
|
||||
*/
|
||||
private String respCode;
|
||||
/**
|
||||
* 应答信息
|
||||
*/
|
||||
private String respMsg;
|
||||
/**
|
||||
* 签名公钥证书
|
||||
*/
|
||||
private String signPubKeyCert;
|
||||
/**
|
||||
* 版本号
|
||||
*/
|
||||
private String version;
|
||||
/**
|
||||
* 编码方式
|
||||
*/
|
||||
private String encoding;
|
||||
/**
|
||||
* 产品类型
|
||||
*/
|
||||
private String bizType;
|
||||
/**
|
||||
* 订单发送时间
|
||||
*/
|
||||
private String txnTime;
|
||||
|
||||
/**
|
||||
* 交易类型
|
||||
*/
|
||||
private String txnType;
|
||||
/**
|
||||
* 交易子类
|
||||
*/
|
||||
private String txnSubType;
|
||||
/**
|
||||
* 接入类型
|
||||
* 0:商户直连接入
|
||||
* 1:收单机构接入
|
||||
* 2:平台商户接入
|
||||
*/
|
||||
private String accessType;
|
||||
/**
|
||||
* 请求方保留域
|
||||
*/
|
||||
private String reqReserved;
|
||||
/**
|
||||
* 商户代码
|
||||
*/
|
||||
private String merId;
|
||||
/**
|
||||
* 商户订单号
|
||||
*/
|
||||
private String orderId;
|
||||
/**
|
||||
* 保留域
|
||||
*/
|
||||
private String reserved;
|
||||
|
||||
|
||||
/**
|
||||
* 获取退款请求结果状态码
|
||||
*
|
||||
* @return 状态码
|
||||
*/
|
||||
@Override
|
||||
public String getCode() {
|
||||
return respCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取退款请求结果状态提示信息
|
||||
*
|
||||
* @return 提示信息
|
||||
*/
|
||||
@Override
|
||||
public String getMsg() {
|
||||
return respMsg;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回业务结果状态码
|
||||
*
|
||||
* @return 业务结果状态码
|
||||
*/
|
||||
@Override
|
||||
public String getResultCode() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回业务结果状态提示信息
|
||||
*
|
||||
* @return 业务结果状态提示信息
|
||||
*/
|
||||
@Override
|
||||
public String getResultMsg() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款金额
|
||||
*
|
||||
* @return 退款金额
|
||||
*/
|
||||
@Override
|
||||
public BigDecimal getRefundFee() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 退款币种信息
|
||||
*
|
||||
* @return 币种信息
|
||||
*/
|
||||
@Override
|
||||
public CurType getRefundCurrency() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付平台交易号
|
||||
* 发起支付时 支付平台(如支付宝)返回的交易订单号
|
||||
*
|
||||
* @return 支付平台交易号
|
||||
*/
|
||||
@Override
|
||||
public String getTradeNo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 支付订单号
|
||||
* 发起支付时,用户系统的订单号
|
||||
*
|
||||
* @return 支付订单号
|
||||
*/
|
||||
@Override
|
||||
public String getOutTradeNo() {
|
||||
return orderId;
|
||||
}
|
||||
|
||||
/**
|
||||
* 商户退款单号
|
||||
*
|
||||
* @return 商户退款单号
|
||||
*/
|
||||
@Override
|
||||
public String getRefundNo() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getQrCode() {
|
||||
return qrCode;
|
||||
}
|
||||
|
||||
public void setQrCode(String qrCode) {
|
||||
this.qrCode = qrCode;
|
||||
}
|
||||
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
public void setSignature(String signature) {
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
public String getSignMethod() {
|
||||
return signMethod;
|
||||
}
|
||||
|
||||
public void setSignMethod(String signMethod) {
|
||||
this.signMethod = signMethod;
|
||||
}
|
||||
|
||||
public String getRespCode() {
|
||||
return respCode;
|
||||
}
|
||||
|
||||
public void setRespCode(String respCode) {
|
||||
this.respCode = respCode;
|
||||
}
|
||||
|
||||
public String getRespMsg() {
|
||||
return respMsg;
|
||||
}
|
||||
|
||||
public void setRespMsg(String respMsg) {
|
||||
this.respMsg = respMsg;
|
||||
}
|
||||
|
||||
public String getSignPubKeyCert() {
|
||||
return signPubKeyCert;
|
||||
}
|
||||
|
||||
public void setSignPubKeyCert(String signPubKeyCert) {
|
||||
this.signPubKeyCert = signPubKeyCert;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
public void setVersion(String version) {
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public String getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
|
||||
public void setEncoding(String encoding) {
|
||||
this.encoding = encoding;
|
||||
}
|
||||
|
||||
public String getBizType() {
|
||||
return bizType;
|
||||
}
|
||||
|
||||
public void setBizType(String bizType) {
|
||||
this.bizType = bizType;
|
||||
}
|
||||
|
||||
public String getTxnTime() {
|
||||
return txnTime;
|
||||
}
|
||||
|
||||
public void setTxnTime(String txnTime) {
|
||||
this.txnTime = txnTime;
|
||||
}
|
||||
|
||||
public String getTxnType() {
|
||||
return txnType;
|
||||
}
|
||||
|
||||
public void setTxnType(String txnType) {
|
||||
this.txnType = txnType;
|
||||
}
|
||||
|
||||
public String getTxnSubType() {
|
||||
return txnSubType;
|
||||
}
|
||||
|
||||
public void setTxnSubType(String txnSubType) {
|
||||
this.txnSubType = txnSubType;
|
||||
}
|
||||
|
||||
public String getAccessType() {
|
||||
return accessType;
|
||||
}
|
||||
|
||||
public void setAccessType(String accessType) {
|
||||
this.accessType = accessType;
|
||||
}
|
||||
|
||||
public String getReqReserved() {
|
||||
return reqReserved;
|
||||
}
|
||||
|
||||
public void setReqReserved(String reqReserved) {
|
||||
this.reqReserved = reqReserved;
|
||||
}
|
||||
|
||||
public String getMerId() {
|
||||
return merId;
|
||||
}
|
||||
|
||||
public void setMerId(String merId) {
|
||||
this.merId = merId;
|
||||
}
|
||||
|
||||
public String getOrderId() {
|
||||
return orderId;
|
||||
}
|
||||
|
||||
public void setOrderId(String orderId) {
|
||||
this.orderId = orderId;
|
||||
}
|
||||
|
||||
public String getReserved() {
|
||||
return reserved;
|
||||
}
|
||||
|
||||
public void setReserved(String reserved) {
|
||||
this.reserved = reserved;
|
||||
}
|
||||
|
||||
public static final UnionRefundResult create(Map<String, Object> result){
|
||||
UnionRefundResult refundResult = new JSONObject(result).toJavaObject(UnionRefundResult.class);
|
||||
refundResult.setAttrs(result);
|
||||
return refundResult;
|
||||
}
|
||||
}
|
@@ -0,0 +1,144 @@
|
||||
package org.dromara.daxpay.channel.union.sdk.bean;
|
||||
|
||||
import org.dromara.daxpay.unisdk.common.bean.TransactionType;
|
||||
import org.dromara.daxpay.unisdk.common.util.str.StringUtils;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Actinia
|
||||
* <pre>
|
||||
* email hayesfu@qq.com
|
||||
create 2017 2017/11/4 0004
|
||||
* </pre>
|
||||
*/
|
||||
public enum UnionTransactionType implements TransactionType{
|
||||
|
||||
/**
|
||||
* ---尚未接入---
|
||||
* 苹果支付
|
||||
* 官方文档:https://open.unionpay.com/tjweb/acproduct/list?apiservId=460
|
||||
*/
|
||||
APPLE_PAY("01","01","000802","08"),
|
||||
/**
|
||||
* 手机控件
|
||||
*/
|
||||
APP("01","01","000000","08"),
|
||||
/**
|
||||
* 手机控件支付产品(安卓/IOS/ApplePay 对应后台)
|
||||
* 官方文档:https://open.unionpay.com/tjweb/acproduct/list?apiservId=450
|
||||
*/
|
||||
WAP("01","01","000201","08"),
|
||||
/**
|
||||
* 网关支付(B2C)
|
||||
* 官方文档:https://open.unionpay.com/tjweb/acproduct/list?apiservId=448
|
||||
* 手机网页支付(WAP支付),
|
||||
* 官方文档:https://open.unionpay.com/tjweb/acproduct/list?apiservId=453
|
||||
*/
|
||||
WEB("01","01","000201","07"),
|
||||
/**
|
||||
* ---尚未接入---
|
||||
* 无跳转支付()
|
||||
* 官方文档:https://open.unionpay.com/tjweb/acproduct/list?apiservId=449
|
||||
*/
|
||||
NO_JUMP("01","01","000301","07"),
|
||||
/**
|
||||
* 企业网银支付(B2B支付)
|
||||
* 官方文档:https://open.unionpay.com/tjweb/acproduct/list?apiservId=452
|
||||
*/
|
||||
B2B("01","01","000202","07"),
|
||||
/**
|
||||
* 申码(主扫场景)
|
||||
* 官方文档:https://open.unionpay.com/tjweb/acproduct/list?apiservId=468
|
||||
*/
|
||||
APPLY_QR_CODE("01","07","000000","08"),
|
||||
/**
|
||||
* 消费(被扫场景)
|
||||
* 官方文档:同申码(主扫场景)
|
||||
*/
|
||||
CONSUME("01","06","000000","08"),
|
||||
//消费撤销
|
||||
CONSUME_UNDO("31","00","000000","08"),
|
||||
//退款
|
||||
REFUND("04","00","000000","08"),
|
||||
//查询
|
||||
QUERY("00","00","000201",""),
|
||||
//对账文件下载
|
||||
FILE_TRANSFER("76","01","000000","")
|
||||
;
|
||||
|
||||
/**
|
||||
* 交易类型
|
||||
*/
|
||||
private String txnType;
|
||||
/**
|
||||
* 交易子类型
|
||||
*/
|
||||
private String txnSubType;
|
||||
|
||||
/**
|
||||
* 业务类型
|
||||
*/
|
||||
private String bizType;
|
||||
|
||||
/**
|
||||
* 渠道类型 05:语音 07:PC,平板 08:手机 16:数字机顶盒
|
||||
*/
|
||||
private String channelType;
|
||||
|
||||
|
||||
UnionTransactionType (String txnType, String txnSubType, String bizType, String channelType) {
|
||||
this.txnType = txnType;
|
||||
this.txnSubType = txnSubType;
|
||||
this.bizType = bizType;
|
||||
this.channelType = channelType;
|
||||
}
|
||||
|
||||
public void convertMap(Map<String ,Object> contentData){
|
||||
//交易类型
|
||||
contentData.put(SDKConstants.param_txnType, this.getTxnType());
|
||||
//交易子类
|
||||
contentData.put(SDKConstants.param_txnSubType,this.getTxnSubType());
|
||||
//业务类型
|
||||
contentData.put(SDKConstants.param_bizType,this.getBizType());
|
||||
//渠道类型
|
||||
if(StringUtils.isNotBlank(this.getChannelType())){
|
||||
contentData.put(SDKConstants.param_channelType,this.getChannelType());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public String getTxnType () {
|
||||
return txnType;
|
||||
}
|
||||
|
||||
public String getTxnSubType () {
|
||||
return txnSubType;
|
||||
}
|
||||
|
||||
public String getBizType () {
|
||||
return bizType;
|
||||
}
|
||||
|
||||
public String getChannelType () {
|
||||
return channelType;
|
||||
}
|
||||
|
||||
/**
|
||||
*获取交易对类型枚举
|
||||
*
|
||||
* @return 交易类型
|
||||
*/
|
||||
@Override
|
||||
public String getType () {
|
||||
return this.name();
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public String getMethod () {
|
||||
return null;
|
||||
}
|
||||
}
|
@@ -2,6 +2,8 @@ package org.dromara.daxpay.channel.union.service.config;
|
||||
|
||||
import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.bean.copier.CopyOptions;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.util.CharsetUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -9,6 +11,8 @@ import org.dromara.daxpay.channel.union.convert.UnionPayConfigConvert;
|
||||
import org.dromara.daxpay.channel.union.entity.config.UnionPayConfig;
|
||||
import org.dromara.daxpay.channel.union.param.config.UnionPayConfigParam;
|
||||
import org.dromara.daxpay.channel.union.result.UnionPayConfigResult;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayConfigStorage;
|
||||
import org.dromara.daxpay.channel.union.sdk.api.UnionPayKit;
|
||||
import org.dromara.daxpay.core.enums.ChannelEnum;
|
||||
import org.dromara.daxpay.core.exception.ConfigNotEnableException;
|
||||
import org.dromara.daxpay.core.exception.DataErrorException;
|
||||
@@ -18,9 +22,13 @@ import org.dromara.daxpay.service.common.local.PaymentContextLocal;
|
||||
import org.dromara.daxpay.service.dao.config.ChannelConfigManager;
|
||||
import org.dromara.daxpay.service.entity.config.ChannelConfig;
|
||||
import org.dromara.daxpay.service.service.config.PlatformConfigService;
|
||||
import org.dromara.daxpay.unisdk.common.bean.CertStoreType;
|
||||
import org.dromara.daxpay.unisdk.common.http.HttpConfigStorage;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
||||
/**
|
||||
* 银联支付
|
||||
* @author xxm
|
||||
@@ -113,4 +121,43 @@ public class UnionPayConfigService {
|
||||
return StrUtil.format("{}/unipay/return/{}/{}/union",platformInfo.getGatewayServiceUrl(),mchAppInfo.getAppId());
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成云闪付支付服务
|
||||
*/
|
||||
public UnionPayKit initPayService(UnionPayConfig config){
|
||||
UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage();
|
||||
unionPayConfigStorage.setInputCharset(CharsetUtil.UTF_8);
|
||||
// 商户号
|
||||
unionPayConfigStorage.setMerId(config.getUnionMachId());
|
||||
// 云闪付必须使用证书才可以进行调用
|
||||
unionPayConfigStorage.setCertSign(true);
|
||||
|
||||
// 中级证书 流
|
||||
unionPayConfigStorage.setAcpMiddleCert(new ByteArrayInputStream(Base64.decode(config.getAcpMiddleCert())));
|
||||
// 根证书 流
|
||||
unionPayConfigStorage.setAcpRootCert(new ByteArrayInputStream(Base64.decode(config.getAcpRootCert())));
|
||||
// 私钥证书 流
|
||||
unionPayConfigStorage.setKeyPrivateCert(new ByteArrayInputStream(Base64.decode(config.getKeyPrivateCert())));
|
||||
|
||||
//私钥证书对应的密码 私钥证书对应的密码
|
||||
unionPayConfigStorage.setKeyPrivateCertPwd(config.getKeyPrivateCertPwd());
|
||||
//设置证书对应的存储方式,证书流
|
||||
unionPayConfigStorage.setCertStoreType(CertStoreType.INPUT_STREAM);
|
||||
|
||||
unionPayConfigStorage.setSignType(config.getSignType());
|
||||
//是否为测试账号,沙箱环境
|
||||
unionPayConfigStorage.setTest(config.isSandbox());
|
||||
|
||||
// 网络请求配置
|
||||
HttpConfigStorage httpConfigStorage = new HttpConfigStorage();
|
||||
httpConfigStorage.setCertStoreType(CertStoreType.INPUT_STREAM);
|
||||
//最大连接数
|
||||
httpConfigStorage.setMaxTotal(20);
|
||||
//默认的每个路由的最大连接数
|
||||
httpConfigStorage.setDefaultMaxPerRoute(10);
|
||||
|
||||
// 创建支付服务
|
||||
return new UnionPayKit(unionPayConfigStorage, httpConfigStorage);
|
||||
}
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user