feat 云闪付对接调试, 重写SDK中一份调用逻辑

This commit is contained in:
bootx
2024-03-10 22:45:57 +08:00
parent aee7c18ebb
commit 8404e17086
15 changed files with 1097 additions and 81 deletions

View File

@@ -15,6 +15,7 @@
- [ ] 增加定时同步退款中的退款订单
2.0.x 版本内容
- [ ] 首页驾驶舱功能: 各通道收入和支付情况
- [ ] 增加各类日志记录,例如钱包的各项操作
- [ ] 支付流程涉及异步支付时, 更换支付方式需要控制预防客户重复付款
- [ ] 增加撤销功能,用于处理线下支付订单的情况

View File

@@ -0,0 +1,57 @@
package cn.bootx.platform.daxpay.admin.controller.channel;
import cn.bootx.platform.common.core.rest.Res;
import cn.bootx.platform.common.core.rest.ResResult;
import cn.bootx.platform.common.core.rest.dto.LabelValue;
import cn.bootx.platform.daxpay.service.core.channel.union.service.UnionPayConfigService;
import cn.bootx.platform.daxpay.service.dto.channel.union.UnionPayConfigDto;
import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayConfigParam;
import cn.hutool.core.codec.Base64;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* 云闪付配置
* @author xxm
* @since 2024/3/9
*/
@Tag(name = "云闪付配置")
@RestController
@RequestMapping("/union/pay/config")
@RequiredArgsConstructor
public class UnionPayConfigController {
private final UnionPayConfigService unionPayConfigService;
@Operation(summary = "获取配置")
@GetMapping("/getConfig")
public ResResult<UnionPayConfigDto> getConfig() {
return Res.ok(unionPayConfigService.getConfig().toDto());
}
@Operation(summary = "更新")
@PostMapping("/update")
public ResResult<Void> update(@RequestBody UnionPayConfigParam param) {
unionPayConfigService.update(param);
return Res.ok();
}
@Operation(summary = "支持的支付方式")
@GetMapping("/findPayWays")
public ResResult<List<LabelValue>> findPayWays() {
return Res.ok(unionPayConfigService.findPayWays());
}
@SneakyThrows
@Operation(summary = "读取证书文件内容")
@PostMapping("/toBase64")
public ResResult<String> toBase64(MultipartFile file){
return Res.ok(Base64.encode(file.getBytes()));
}
}

View File

@@ -0,0 +1,18 @@
package cn.bootx.platform.daxpay.admin.controller.channel;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 云闪付控制器
* @author xxm
* @since 2024/3/9
*/
@Tag(name = "云闪付控制器")
@RestController
@RequestMapping("/union/pay")
@RequiredArgsConstructor
public class UnionPayController {
}

View File

@@ -28,7 +28,7 @@ public enum PaySyncStatusEnum {
* 所以查询为了区分,增加一个未知的状态, 用于处理这种特殊情况, 然后根据业务需要,关闭订单或者进行其他操作
*/
NOT_FOUND_UNKNOWN("pay_not_found_unknown","交易不存在(特殊)"),
/** 不属于网关同步过来的状态, 需要手动设置, 处理本地订单到了超时时间, 但是网关和本地都未关闭, 需要触发关闭相关处理 */
/** 本地订单到了超时时间, 但是网关和本地都未关闭, 需要触发关闭相关处理, 可以进行手动设置 */
TIMEOUT("pay_timeout", "支付超时");
/** 编码 */

View File

@@ -36,4 +36,10 @@ public class PayReturnController {
public ModelAndView wechat(){
return null;
}
@Operation(summary = "云闪付同步通知")
@GetMapping("/union")
public ModelAndView union(){
return null;
}
}

View File

@@ -18,26 +18,26 @@ public interface UnionPayCode {
String RESULT_CODE = "result_code";
/** 网关订单号 */
String TRANSACTION_ID = "transaction_id";
String QUERY_ID = "queryId";
/** 第三方订单号 */
String OUT_TRANSACTION_ID = "out_transaction_id";
String ORDER_ID = "orderId";
/** 退款ID */
String REFUND_ID = "refund_id";
/**
* 支付完成时间
* 订单发送时间
* 格式: yyyyMMddHHmmss
*/
String TIME_END = "time_end";
String TXN_TIME = "txnTime";
/** 支付结果 */
String PAY_RESULT = "pay_result";
/** 总金额 */
String TOTAL_FEE = "total_fee";
String TOTAL_FEE = "settleAmt";
/** 交易状态 */
String TRADE_STATE = "trade_state";

View File

@@ -0,0 +1,39 @@
package cn.bootx.platform.daxpay.service.code;
import cn.bootx.platform.daxpay.code.PayWayEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import lombok.experimental.UtilityClass;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* 云闪付支付方式
* @author xxm
* @since 2024/3/9
*/
@UtilityClass
public class UnionPayWay {
// 支付方式
private static final List<PayWayEnum> PAY_WAYS = Arrays.asList(PayWayEnum.WAP, PayWayEnum.APP, PayWayEnum.WEB,
PayWayEnum.QRCODE, PayWayEnum.BARCODE);
/**
* 根据编码获取
*/
public PayWayEnum findByCode(String code) {
return PAY_WAYS.stream()
.filter(e -> Objects.equals(code, e.getCode()))
.findFirst()
.orElseThrow(() -> new PayFailureException("不存在的支付方式"));
}
/**
* 获取支持的支付方式
*/
public List<PayWayEnum> getPayWays() {
return PAY_WAYS;
}
}

View File

@@ -53,7 +53,7 @@ public class UnionPayConfig extends MpBaseEntity implements EntityBaseFunction<U
* @see UnionPaySignTypeEnum
*/
@DbColumn(comment = "签名类型")
public String signType;
private String signType;
/**
* 是否为证书签名

View File

@@ -89,15 +89,15 @@ public class UnionPayCallbackService extends AbsCallbackStrategy {
Map<String, String> callbackParam = callbackInfo.getCallbackParam();
// 网关订单号
callbackInfo.setGatewayOrderNo(callbackParam.get(TRANSACTION_ID));
callbackInfo.setGatewayOrderNo(callbackParam.get(QUERY_ID));
// 支付订单ID
callbackInfo.setOrderId(Long.valueOf(callbackParam.get(OUT_TRANSACTION_ID)));
callbackInfo.setOrderId(Long.valueOf(callbackParam.get(ORDER_ID)));
// 支付结果
PayStatusEnum payStatus = WxPayKit.codeIsOk(callbackParam.get(PAY_RESULT)) ? PayStatusEnum.SUCCESS : PayStatusEnum.FAIL;
callbackInfo.setGatewayStatus(payStatus.getCode());
// 支付金额
callbackInfo.setAmount(callbackParam.get(TOTAL_FEE));
String timeEnd = callbackParam.get(TIME_END);
String timeEnd = callbackParam.get(TXN_TIME);
if (StrUtil.isNotBlank(timeEnd)) {
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
callbackInfo.setFinishTime(time);

View File

@@ -1,20 +1,12 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
import cn.bootx.platform.daxpay.service.core.order.pay.entity.PayOrder;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
import cn.hutool.core.util.StrUtil;
import com.egzosn.pay.common.bean.AssistOrder;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Map;
import java.util.Objects;
import static cn.bootx.platform.daxpay.service.code.UnionPayCode.*;
/**
* 云闪付支付关闭
* @author xxm
@@ -29,30 +21,7 @@ public class UnionPayCloseService {
* 关闭订单
*/
public void close(PayOrder payOrder, UnionPayKit unionPayKit) {
AssistOrder closeOrder = new AssistOrder();
closeOrder.setOutTradeNo(String.valueOf(payOrder.getId()));
Map<String, Object> result = unionPayKit.close(closeOrder);
throw new PayFailureException("云闪付没有关闭订单功能!");
// this.verifyErrorMsg(result);
}
/**
* 验证错误信息
*/
private void verifyErrorMsg(Map<String, String> result) {
String status = result.get(UnionPayCode.STATUS);
String returnCode = result.get(UnionPayCode.RESULT_CODE);
// 判断查询是否成功
if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
String errorMsg = result.get(ERR_MSG);
if (StrUtil.isBlank(errorMsg)) {
errorMsg = result.get(MESSAGE);
}
log.error("订单关闭失败 {}", errorMsg);
throw new PayFailureException(errorMsg);
}
}
}

View File

@@ -4,14 +4,16 @@ import cn.bootx.platform.common.core.exception.DataNotExistException;
import cn.bootx.platform.common.core.rest.dto.LabelValue;
import cn.bootx.platform.daxpay.code.PayChannelEnum;
import cn.bootx.platform.daxpay.exception.pay.PayFailureException;
import cn.bootx.platform.daxpay.service.code.AliPayWay;
import cn.bootx.platform.daxpay.service.code.UnionPayWay;
import cn.bootx.platform.daxpay.service.core.channel.union.dao.UnionPayConfigManager;
import cn.bootx.platform.daxpay.service.core.channel.union.entity.UnionPayConfig;
import cn.bootx.platform.daxpay.service.core.system.config.service.PayChannelConfigService;
import cn.bootx.platform.daxpay.service.param.channel.alipay.AliPayConfigParam;
import cn.bootx.platform.daxpay.service.param.channel.union.UnionPayConfigParam;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
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 com.egzosn.pay.common.bean.CertStoreType;
import com.egzosn.pay.common.http.HttpConfigStorage;
import com.egzosn.pay.union.api.UnionPayConfigStorage;
@@ -20,6 +22,7 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.io.ByteArrayInputStream;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -42,7 +45,7 @@ public class UnionPayConfigService {
* 修改
*/
@Transactional(rollbackFor = Exception.class)
public void update(AliPayConfigParam param) {
public void update(UnionPayConfigParam param) {
UnionPayConfig unionPayConfig = unionPayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("支付宝配置不存在"));
// 启用或停用
if (!Objects.equals(param.getEnable(), unionPayConfig.getEnable())){
@@ -54,10 +57,10 @@ public class UnionPayConfigService {
}
/**
* 支付宝支持支付方式
* 云闪付支持支付方式
*/
public List<LabelValue> findPayWays() {
return AliPayWay.getPayWays()
return UnionPayWay.getPayWays()
.stream()
.map(e -> new LabelValue(e.getName(),e.getCode()))
.collect(Collectors.toList());
@@ -67,7 +70,7 @@ public class UnionPayConfigService {
* 获取支付配置
*/
public UnionPayConfig getConfig(){
return unionPayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("支付配置不存在"));
return unionPayConfigManager.findById(ID).orElseThrow(() -> new DataNotExistException("云闪付支付配置不存在"));
}
/**
@@ -87,32 +90,35 @@ public class UnionPayConfigService {
*/
public UnionPayKit initPayService(UnionPayConfig config){
UnionPayConfigStorage unionPayConfigStorage = new UnionPayConfigStorage();
unionPayConfigStorage.setInputCharset(CharsetUtil.UTF_8);
// 商户号
unionPayConfigStorage.setMerId(config.getMachId());
//是否为证书签名
unionPayConfigStorage.setCertSign(config.isCertSign());
//中级证书 证书字符串信息
unionPayConfigStorage.setAcpMiddleCert(config.getAcpMiddleCert());
//根证书路径 证书字符串信息
unionPayConfigStorage.setAcpRootCert(config.getAcpRootCert());
// 私钥证书路径 证书字符串信息
unionPayConfigStorage.setKeyPrivateCert(config.getKeyPrivateCert());
//中级证书
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.STR);
unionPayConfigStorage.setCertStoreType(CertStoreType.INPUT_STREAM);
// 回调地址
unionPayConfigStorage.setNotifyUrl(config.getNotifyUrl());
// 同步回调可不填
unionPayConfigStorage.setReturnUrl(config.getReturnUrl());
unionPayConfigStorage.setSignType(config.signType);
unionPayConfigStorage.setSignType(config.getSignType());
//是否为测试账号,沙箱环境
unionPayConfigStorage.setTest(config.isSandbox());
// 网络请求配置
HttpConfigStorage httpConfigStorage = new HttpConfigStorage();
httpConfigStorage.setCertStoreType(CertStoreType.STR);
httpConfigStorage.setCertStoreType(CertStoreType.INPUT_STREAM);
//最大连接数
httpConfigStorage.setMaxTotal(20);
//默认的每个路由的最大连接数

View File

@@ -1,5 +1,6 @@
package cn.bootx.platform.daxpay.service.core.channel.union.service;
import cn.bootx.platform.common.core.util.LocalDateTimeUtil;
import cn.bootx.platform.daxpay.code.PaySyncStatusEnum;
import cn.bootx.platform.daxpay.code.RefundSyncStatusEnum;
import cn.bootx.platform.daxpay.service.code.UnionPayCode;
@@ -9,13 +10,18 @@ import cn.bootx.platform.daxpay.service.core.payment.sync.result.PayGatewaySyncR
import cn.bootx.platform.daxpay.service.core.payment.sync.result.RefundGatewaySyncResult;
import cn.bootx.platform.daxpay.service.sdk.union.api.UnionPayKit;
import cn.bootx.platform.daxpay.service.sdk.union.bean.UnionRefundOrder;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.egzosn.pay.common.bean.AssistOrder;
import com.egzosn.pay.common.bean.NoticeParams;
import com.egzosn.pay.union.bean.SDKConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Objects;
@@ -39,33 +45,42 @@ public class UnionPaySyncService {
AssistOrder query = new AssistOrder();
query.setOutTradeNo(String.valueOf(order.getId()));
Map<String, Object> result = unionPayKit.query(query);
syncResult.setSyncInfo(JSONUtil.toJsonStr(result));
if (!unionPayKit.verify(new NoticeParams(result))) {
log.warn("查询云闪付订单验签失败:{}", result);
return syncResult.setErrorMsg("查询订单验签失败");
}
// String status = result.get(STATUS);
// String returnCode = result.get(RESULT_CODE);
//
// // 判断查询是否成功
// if (!(Objects.equals(SUCCESS, status) && Objects.equals(SUCCESS, returnCode))){
// log.warn("查询云闪付订单失败:{}", result);
// return syncResult;
// }
//
// // 设置微信支付网关订单号
// syncResult.setGatewayOrderNo(result.get(TRANSACTION_ID));
// // 查询到订单的状态
// String tradeStatus = result.get(TRADE_STATE);
// // 支付完成
// if (Objects.equals(tradeStatus, SUCCESS)) {
// String timeEnd = result.get(TIME_END);
// LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
// return syncResult.setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS);
// }
// // 待支付
// if (Objects.equals(tradeStatus, TRADE_NOT_PAY)) {
// return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS);
// }
// 查询失败
String resultCode = MapUtil.getStr(result, SDKConstants.param_respCode);
if (!SDKConstants.OK_RESP_CODE.equals(resultCode)) {
log.warn("查询云闪付订单失败:{}", result);
return syncResult.setErrorMsg(MapUtil.getStr(result, SDKConstants.param_respMsg));
}
String origRespCode = MapUtil.getStr(result, SDKConstants.param_origRespCode);
// 查询流水号, 相当于网关订单号
// 成功
if (Objects.equals(origRespCode, SDKConstants.OK_RESP_CODE)) {
String queryId = MapUtil.getStr(result, QUERY_ID);
String timeEnd = MapUtil.getStr(result, TXN_TIME);
LocalDateTime time = LocalDateTimeUtil.parse(timeEnd, DatePattern.PURE_DATETIME_PATTERN);
return syncResult.setGatewayOrderNo(queryId).setPayTime(time).setSyncStatus(PaySyncStatusEnum.SUCCESS);
}
// 支付超时 交易不在受理时间范围内
if (Objects.equals(origRespCode, "39")) {
return syncResult.setSyncStatus(PaySyncStatusEnum.TIMEOUT)
.setErrorMsg(MapUtil.getStr(result, SDKConstants.param_origRespMsg));
}
// 待支付
if (Objects.equals(origRespCode, "05")) {
return syncResult.setSyncStatus(PaySyncStatusEnum.PROGRESS);
}
//
// // 已退款/退款中
// if (Objects.equals(tradeStatus, TRADE_REFUND)) {

View File

@@ -1,11 +1,15 @@
package cn.bootx.platform.daxpay.service.dto.channel.union;
import cn.bootx.platform.common.core.rest.dto.BaseDto;
import cn.bootx.platform.daxpay.service.code.UnionPaySignTypeEnum;
import cn.bootx.platform.starter.data.perm.sensitive.SensitiveInfo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.List;
/**
* @author xxm
* @since 2022/3/11
@@ -16,4 +20,90 @@ import lombok.experimental.Accessors;
@Schema(title = "云闪付配置")
public class UnionPayConfigDto extends BaseDto {
/** 商户号 */
@Schema(description = "商户号")
private String machId;
/** 是否启用, 只影响支付和退款操作 */
@Schema(description = "是否启用")
private Boolean enable;
/**
* 商户收款账号
*/
@Schema(description = "商户收款账号")
private String seller;
/**
* 签名类型
* @see UnionPaySignTypeEnum
*/
@Schema(description = "签名类型")
public String signType;
/**
* 是否为证书签名
*/
@Schema(description = "是否为证书签名")
private boolean certSign;
/**
* 应用私钥证书 字符串
*/
@SensitiveInfo(value = SensitiveInfo.SensitiveType.OTHER, front = 15)
@Schema(description = "应用私钥证书")
private String keyPrivateCert;
/**
* 私钥证书对应的密码
*/
@SensitiveInfo(value = SensitiveInfo.SensitiveType.PASSWORD)
@Schema(description = "私钥证书对应的密码")
private String keyPrivateCertPwd;
/**
* 中级证书
*/
@SensitiveInfo(value = SensitiveInfo.SensitiveType.OTHER, front = 15)
@Schema(description = "中级证书")
private String acpMiddleCert;
/**
* 根证书
*/
@SensitiveInfo(value = SensitiveInfo.SensitiveType.OTHER, front = 15)
@Schema(description = "根证书")
private String acpRootCert;
/** 是否沙箱环境 */
@Schema(description = "是否沙箱环境")
private boolean sandbox;
/** 支付网关地址 */
@Schema(description = "支付网关地址")
private String serverUrl;
/**
* 服务器异步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址
* 1. 需http://或者https://格式的完整路径,
* 2. 不能加?id=123这类自定义参数必须外网可以正常访问
* 3. 消息顺序 银联网关 -> 本网关进行处理 -> 发送消息通知业务系统
*/
@Schema(description = "异步通知路径")
private String notifyUrl;
/**
* 服务器同步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址
* 1. 需http://或者https://格式的完整路径,
* 2. 不能加?id=123这类自定义参数必须外网可以正常访问
* 3. 消息顺序 银联网关 -> 本网关进行处理 -> 重定向到业务系统中
*/
@Schema(description = "同步通知页面路径")
private String returnUrl;
/** 可用支付方式 */
@Schema(description = "可用支付方式")
private List<String> payWays;
@Schema(description = "备注")
private String remark;
}

View File

@@ -0,0 +1,97 @@
package cn.bootx.platform.daxpay.service.param.channel.union;
import cn.bootx.platform.daxpay.service.code.UnionPaySignTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/**
* 云闪付支付配置参数
* @author xxm
* @since 2024/3/9
*/
@Data
@Accessors(chain = true)
@Schema(title = "云闪付支付配置参数")
public class UnionPayConfigParam {
/** 商户号 */
@Schema(description = "商户号")
private String machId;
/** 是否启用, 只影响支付和退款操作 */
@Schema(description = "是否启用")
private Boolean enable;
/**
* 商户收款账号
*/
@Schema(description = "商户收款账号")
private String seller;
/**
* 签名类型
* @see UnionPaySignTypeEnum
*/
@Schema(description = "签名类型")
public String signType;
/**
* 是否为证书签名
*/
@Schema(description = "是否为证书签名")
private boolean certSign;
/**
* 应用私钥证书
*/
@Schema(description = "应用私钥证书")
private String keyPrivateCert;
/**
* 私钥证书对应的密码
*/
@Schema(description = "私钥证书对应的密码")
private String keyPrivateCertPwd;
/**
* 中级证书
*/
@Schema(description = "中级证书")
private String acpMiddleCert;
/**
* 根证书
*/
@Schema(description = "根证书")
private String acpRootCert;
/** 是否沙箱环境 */
@Schema(description = "是否沙箱环境")
private boolean sandbox;
/**
* 服务器异步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址
* 1. 需http://或者https://格式的完整路径,
* 2. 不能加?id=123这类自定义参数必须外网可以正常访问
* 3. 消息顺序 银联网关 -> 本网关进行处理 -> 发送消息通知业务系统
*/
@Schema(description = "异步通知路径")
private String notifyUrl;
/**
* 服务器同步通知页面路径, 需要填写本网关服务的地址, 不可以直接填写业务系统的地址
* 1. 需http://或者https://格式的完整路径,
* 2. 不能加?id=123这类自定义参数必须外网可以正常访问
* 3. 消息顺序 银联网关 -> 本网关进行处理 -> 重定向到业务系统中
*/
@Schema(description = "同步通知页面路径")
private String returnUrl;
/** 可用支付方式 */
@Schema(description = "可用支付方式")
private List<String> payWays;
@Schema(description = "备注")
private String remark;
}

View File

@@ -1,8 +1,32 @@
package cn.bootx.platform.daxpay.service.sdk.union.api;
import com.alibaba.fastjson.JSONObject;
import com.egzosn.pay.common.bean.*;
import com.egzosn.pay.common.bean.outbuilder.PayTextOutMessage;
import com.egzosn.pay.common.bean.result.PayException;
import com.egzosn.pay.common.exception.PayErrorException;
import com.egzosn.pay.common.http.HttpConfigStorage;
import com.egzosn.pay.common.http.UriVariables;
import com.egzosn.pay.common.util.DateUtils;
import com.egzosn.pay.common.util.Util;
import com.egzosn.pay.common.util.sign.CertDescriptor;
import com.egzosn.pay.common.util.sign.SignTextUtils;
import com.egzosn.pay.common.util.sign.SignUtils;
import com.egzosn.pay.common.util.sign.encrypt.RSA;
import com.egzosn.pay.common.util.sign.encrypt.RSA2;
import com.egzosn.pay.common.util.str.StringUtils;
import com.egzosn.pay.union.api.UnionPayConfigStorage;
import com.egzosn.pay.union.api.UnionPayService;
import com.egzosn.pay.union.bean.*;
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.*;
/**
* 云闪付支付服务类重命名, 避免与系统中类名冲突
@@ -10,6 +34,29 @@ import com.egzosn.pay.union.api.UnionPayService;
* @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;
/**
* 构造函数
*
@@ -22,4 +69,675 @@ public class UnionPayKit extends UnionPayService {
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;
}
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);
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 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);
}
}