feat 集成 egan/pay-java-parent 做后续二次开发

This commit is contained in:
DaxPay
2024-10-08 19:56:44 +08:00
parent a74c8e79a7
commit a2333cd11d
90 changed files with 15134 additions and 0 deletions

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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";
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View File

@@ -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语音 07PC,平板 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;
}
}

View File

@@ -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);
}
}