mirror of
https://gitee.com/dromara/dax-pay.git
synced 2025-10-15 14:20:26 +00:00
feat 结算台和各种支付记录
This commit is contained in:
@@ -24,7 +24,7 @@ public interface PayStatusCode {
|
||||
/** 支付取消(超时/手动取消/订单已经关闭,撤销支付单) */
|
||||
String TRADE_CANCEL = "trade_cancel";
|
||||
|
||||
/** 退款中 */
|
||||
/** 退款中(部分退款) */
|
||||
String TRADE_REFUNDING = "trade_refunding";
|
||||
|
||||
/** 已退款 */
|
||||
|
@@ -43,11 +43,11 @@ public class CashierController {
|
||||
return Res.ok(cashierService.combinationPay(param));
|
||||
}
|
||||
|
||||
@Operation(summary = "扫码聚合支付(单渠道)")
|
||||
@GetMapping("/aggregatePay/{mchAppCode}")
|
||||
public ModelAndView aggregatePay(String key,@PathVariable String mchAppCode, @RequestHeader(USER_AGENT) String ua) {
|
||||
@Operation(summary = "扫码聚合支付(单通道)")
|
||||
@GetMapping("/aggregatePay/{mchCode}/{mchAppCode}")
|
||||
public ModelAndView aggregatePay(String key,@PathVariable String mchCode,@PathVariable String mchAppCode, @RequestHeader(USER_AGENT) String ua) {
|
||||
try {
|
||||
String url = cashierService.aggregatePay(key, mchAppCode, ua);
|
||||
String url = cashierService.aggregatePay(key, mchCode, mchAppCode, ua);
|
||||
return new ModelAndView("redirect:" + url);
|
||||
}
|
||||
catch (PayUnsupportedMethodException e) {
|
||||
@@ -56,9 +56,9 @@ public class CashierController {
|
||||
}
|
||||
|
||||
@Operation(summary = "微信jsapi支付(回调)")
|
||||
@GetMapping("/wxJsapiPay/{mchAppCode}")
|
||||
public ModelAndView wxJsapiPay(String code, @PathVariable String mchAppCode, String state) {
|
||||
Map<String, String> map = cashierService.wxJsapiPay(code,mchAppCode,state);
|
||||
@GetMapping("/wxJsapiPay/{mchCode}/{mchAppCode}")
|
||||
public ModelAndView wxJsapiPay(String code, @PathVariable String mchCode, @PathVariable String mchAppCode, String state) {
|
||||
Map<String, String> map = cashierService.wxJsapiPay(code,mchCode,mchAppCode,state);
|
||||
// 跳转页面, 调起微信jsapi支付
|
||||
return new ModelAndView("wechatJsapiPay").addAllObjects(map);
|
||||
}
|
||||
|
@@ -12,6 +12,7 @@ import cn.bootx.platform.daxpay.core.aggregate.entity.AggregatePayInfo;
|
||||
import cn.bootx.platform.daxpay.core.aggregate.service.AggregateService;
|
||||
import cn.bootx.platform.daxpay.core.channel.wechat.dao.WeChatPayConfigManager;
|
||||
import cn.bootx.platform.daxpay.core.channel.wechat.entity.WeChatPayConfig;
|
||||
import cn.bootx.platform.daxpay.core.merchant.dao.MchAppManager;
|
||||
import cn.bootx.platform.daxpay.core.pay.service.PayService;
|
||||
import cn.bootx.platform.daxpay.dto.pay.PayResult;
|
||||
import cn.bootx.platform.daxpay.exception.payment.PayFailureException;
|
||||
@@ -22,6 +23,7 @@ import cn.bootx.platform.daxpay.param.pay.PayParam;
|
||||
import cn.bootx.platform.daxpay.param.pay.PayWayParam;
|
||||
import cn.bootx.platform.daxpay.util.PayWaylUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.ijpay.core.enums.SignType;
|
||||
import com.ijpay.core.kit.WxPayKit;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -54,13 +56,14 @@ public class CashierService {
|
||||
|
||||
private final WeChatPayConfigManager weChatPayConfigManager;
|
||||
|
||||
private final MchAppManager mchAppManager;
|
||||
|
||||
private final SystemParamManager systemParamManager;
|
||||
|
||||
/**
|
||||
* 发起支付(单渠道支付)
|
||||
*/
|
||||
public PayResult singlePay(CashierSinglePayParam param) {
|
||||
|
||||
// 如果是聚合支付,存在付款码时特殊处理(聚合扫码支付不用额外处理)
|
||||
if (Objects.equals(PayChannelEnum.AGGREGATION.getCode(), param.getPayChannel())) {
|
||||
String payChannel = aggregateService.getPayChannel(param.getAuthCode()).getCode();
|
||||
@@ -96,8 +99,9 @@ public class CashierService {
|
||||
/**
|
||||
* 扫码发起自动支付
|
||||
*/
|
||||
public String aggregatePay(String key, String mchAppCode, String ua) {
|
||||
public String aggregatePay(String key, String mchCode, String mchAppCode, String ua) {
|
||||
CashierSinglePayParam cashierSinglePayParam = new CashierSinglePayParam()
|
||||
.setMchCode(mchCode)
|
||||
.setMchAppCode(mchAppCode)
|
||||
.setPayWay(PayWayEnum.QRCODE.getCode());
|
||||
// 判断是哪种支付方式
|
||||
@@ -106,7 +110,7 @@ public class CashierService {
|
||||
}
|
||||
else if (ua.contains(PayChannelEnum.UA_WECHAT_PAY)) {
|
||||
// 跳转微信授权页面, 调用jsapi进行支付
|
||||
return this.wxJsapiAuth(key,mchAppCode);
|
||||
return this.wxJsapiAuth(key,mchCode,mchAppCode);
|
||||
}
|
||||
else {
|
||||
throw new PayUnsupportedMethodException();
|
||||
@@ -123,26 +127,27 @@ public class CashierService {
|
||||
/**
|
||||
* 微信jsapi支付 - 跳转到授权页面
|
||||
*/
|
||||
private String wxJsapiAuth(String key, String mchAppCode) {
|
||||
private String wxJsapiAuth(String key, String mchCode, String mchAppCode) {
|
||||
WeChatPayConfig config = weChatPayConfigManager.findByMchAppCode(mchAppCode)
|
||||
.orElseThrow(() -> new PayFailureException("未找到启用的微信支付配置"));
|
||||
WxMpService wxMpService = getWxMpService(config.getWxAppId(), config.getAppSecret());
|
||||
// 回调地址为 结算台微信jsapi支付的回调地址
|
||||
SystemParameter systemParameter = systemParamManager.findByParamKey(WeChatPayCode.JSAPI_REDIRECT_URL)
|
||||
.orElseThrow(() -> new PayFailureException("微信支付回调地址参数不存在"));
|
||||
String url = systemParameter.getValue() + "cashier/wxJsapiPay";
|
||||
String url = StrUtil.format("{}cashier/wxJsapiPay/{}/{}",systemParameter.getValue(),mchCode,mchAppCode);
|
||||
return wxMpService.getOAuth2Service().buildAuthorizationUrl(url, WxConsts.OAuth2Scope.SNSAPI_BASE, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信jsapi支付 - 回调发起预支付, 同时调起微信页面jsapi支付
|
||||
* @param code 微信授权码, 用来获取id
|
||||
* @param mchCode 商户编码
|
||||
* @param mchAppCode 商户应用编码
|
||||
* @param state 聚合支付参数记录的key
|
||||
* @return 页面中调起jsapi支付的参数
|
||||
*/
|
||||
@SneakyThrows
|
||||
public Map<String, String> wxJsapiPay(String code, String mchAppCode, String state) {
|
||||
public Map<String, String> wxJsapiPay(String code, String mchCode, String mchAppCode, String state) {
|
||||
WeChatPayConfig config = weChatPayConfigManager.findByMchAppCode(mchAppCode)
|
||||
.orElseThrow(() -> new PayFailureException("未找到启用的微信支付配置"));
|
||||
WxMpService wxMpService = this.getWxMpService(config.getWxAppId(), config.getAppSecret());
|
||||
@@ -153,6 +158,8 @@ public class CashierService {
|
||||
CashierSinglePayParam cashierSinglePayParam = new CashierSinglePayParam()
|
||||
.setPayChannel(PayChannelEnum.WECHAT.getCode())
|
||||
.setPayWay(PayWayEnum.JSAPI.getCode())
|
||||
.setMchCode(mchCode)
|
||||
.setMchAppCode(mchAppCode)
|
||||
.setTitle(aggregatePayInfo.getTitle())
|
||||
.setAmount(aggregatePayInfo.getAmount())
|
||||
.setOpenId(openId)
|
||||
@@ -188,6 +195,8 @@ public class CashierService {
|
||||
}
|
||||
// 发起支付
|
||||
PayParam payParam = new PayParam().setTitle(param.getTitle())
|
||||
.setMchCode(param.getMchCode())
|
||||
.setMchAppCode(param.getMchAppCode())
|
||||
.setBusinessId(param.getBusinessId())
|
||||
.setPayWayList(param.getPayWayList());
|
||||
PayResult payResult = payService.pay(payParam);
|
||||
|
@@ -23,8 +23,11 @@ import org.springframework.stereotype.Service;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
import static cn.bootx.platform.daxpay.code.pay.PayStatusCode.REFUND_PROCESS_FAIL;
|
||||
|
||||
/**
|
||||
* 微信支付关闭和退款
|
||||
*
|
||||
@@ -76,6 +79,12 @@ public class WeChatPayCancelService {
|
||||
.build()
|
||||
.createSign(weChatPayConfig.getApiKeyV2(), SignType.HMACSHA256);
|
||||
// 获取证书文件流
|
||||
if (Objects.isNull(weChatPayConfig.getP12())){
|
||||
String errorMsg = "微信p.12证书未配置,无法进行退款";
|
||||
AsyncRefundLocal.setErrorMsg(errorMsg);
|
||||
AsyncRefundLocal.setErrorCode(REFUND_PROCESS_FAIL);
|
||||
throw new PayFailureException(errorMsg);
|
||||
}
|
||||
byte[] fileBytes = uploadService.getFileBytes(weChatPayConfig.getP12());
|
||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(fileBytes);
|
||||
// 证书密码为 微信商户号
|
||||
|
@@ -41,27 +41,28 @@ public class PaymentBuilder {
|
||||
String ip = ServletUtil.getClientIP(request);
|
||||
// 基础信息
|
||||
payment.setBusinessId(payParam.getBusinessId())
|
||||
.setMchCode(payParam.getMchCode())
|
||||
.setMchAppCode(payment.getMchAppCode())
|
||||
.setTitle(payParam.getTitle())
|
||||
.setDescription(payParam.getDescription());
|
||||
.setMchCode(payParam.getMchCode())
|
||||
.setMchAppCode(payParam.getMchAppCode())
|
||||
.setTitle(payParam.getTitle())
|
||||
.setDescription(payParam.getDescription());
|
||||
|
||||
// 支付方式和状态
|
||||
List<PayChannelInfo> payTypeInfos = buildPayTypeInfo(payParam.getPayWayList());
|
||||
List<RefundableInfo> refundableInfos = buildRefundableInfo(payParam.getPayWayList());
|
||||
// 计算总价
|
||||
BigDecimal sumAmount = payTypeInfos.stream()
|
||||
.map(PayChannelInfo::getAmount)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal::add)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
.map(PayChannelInfo::getAmount)
|
||||
.filter(Objects::nonNull)
|
||||
.reduce(BigDecimal::add)
|
||||
.orElse(BigDecimal.ZERO);
|
||||
// 支付通道信息
|
||||
payment.setPayChannelInfo(payTypeInfos)
|
||||
.setRefundableInfo(refundableInfos)
|
||||
.setPayStatus(PayStatusCode.TRADE_PROGRESS)
|
||||
.setAmount(sumAmount)
|
||||
.setClientIp(ip)
|
||||
.setRefundableBalance(sumAmount);
|
||||
.setRefundableInfo(refundableInfos)
|
||||
.setPayStatus(PayStatusCode.TRADE_PROGRESS)
|
||||
.setAmount(sumAmount)
|
||||
.setCombinationPayMode(payTypeInfos.size()>1)
|
||||
.setClientIp(ip)
|
||||
.setRefundableBalance(sumAmount);
|
||||
return payment;
|
||||
}
|
||||
|
||||
@@ -88,16 +89,17 @@ public class PaymentBuilder {
|
||||
PayParam payParam = new PayParam();
|
||||
// 恢复 payModeList
|
||||
List<PayWayParam> payWayParams = payment.getPayChannelInfo()
|
||||
.stream()
|
||||
.map(payTypeInfo -> new PayWayParam().setAmount(payTypeInfo.getAmount())
|
||||
.setPayChannel(payTypeInfo.getPayChannel())
|
||||
.setExtraParamsJson(payTypeInfo.getExtraParamsJson()))
|
||||
.collect(Collectors.toList());
|
||||
.stream()
|
||||
.map(payTypeInfo -> new PayWayParam().setAmount(payTypeInfo.getAmount())
|
||||
.setPayChannel(payTypeInfo.getPayChannel())
|
||||
.setExtraParamsJson(payTypeInfo.getExtraParamsJson()))
|
||||
.collect(Collectors.toList());
|
||||
payParam.setPayWayList(payWayParams)
|
||||
.setBusinessId(payment.getBusinessId())
|
||||
.setTitle(payment.getTitle())
|
||||
.setTitle(payment.getTitle())
|
||||
.setDescription(payment.getDescription());
|
||||
.setBusinessId(payment.getBusinessId())
|
||||
.setTitle(payment.getTitle())
|
||||
.setMchCode(payment.getMchCode())
|
||||
.setMchAppCode(payment.getMchAppCode())
|
||||
.setDescription(payment.getDescription());
|
||||
return payParam;
|
||||
}
|
||||
|
||||
@@ -112,21 +114,21 @@ public class PaymentBuilder {
|
||||
paymentResult = new PayResult();
|
||||
// 异步支付信息
|
||||
paymentResult.setAsyncPayChannel(payment.getAsyncPayChannel())
|
||||
.setAsyncPayMode(payment.isAsyncPayMode())
|
||||
.setPayStatus(payment.getPayStatus());
|
||||
.setAsyncPayMode(payment.isAsyncPayMode())
|
||||
.setPayStatus(payment.getPayStatus());
|
||||
|
||||
List<PayChannelInfo> channelInfos = payment.getPayChannelInfo();
|
||||
|
||||
// 设置异步支付参数
|
||||
List<PayChannelInfo> moneyPayTypeInfos = channelInfos.stream()
|
||||
.filter(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getPayChannel()))
|
||||
.collect(Collectors.toList());
|
||||
.filter(payTypeInfo -> PayChannelEnum.ASYNC_TYPE_CODE.contains(payTypeInfo.getPayChannel()))
|
||||
.collect(Collectors.toList());
|
||||
if (!CollUtil.isEmpty(moneyPayTypeInfos)) {
|
||||
paymentResult.setAsyncPayInfo(AsyncPayInfoLocal.get());
|
||||
}
|
||||
// 清空线程变量
|
||||
}
|
||||
finally {
|
||||
// 清空线程变量
|
||||
AsyncPayInfoLocal.clear();
|
||||
}
|
||||
return paymentResult;
|
||||
|
@@ -4,7 +4,9 @@ import cn.bootx.mybatis.table.modify.annotation.DbColumn;
|
||||
import cn.bootx.mybatis.table.modify.annotation.DbComment;
|
||||
import cn.bootx.mybatis.table.modify.annotation.DbTable;
|
||||
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlFieldType;
|
||||
import cn.bootx.mybatis.table.modify.mybatis.mysq.annotation.DbMySqlIndex;
|
||||
import cn.bootx.mybatis.table.modify.mybatis.mysq.constants.MySqlFieldTypeEnum;
|
||||
import cn.bootx.mybatis.table.modify.mybatis.mysq.constants.MySqlIndexType;
|
||||
import cn.bootx.platform.common.core.annotation.BigField;
|
||||
import cn.bootx.platform.common.core.function.EntityBaseFunction;
|
||||
import cn.bootx.platform.common.mybatisplus.base.MpBaseEntity;
|
||||
@@ -41,7 +43,8 @@ import java.util.List;
|
||||
public class Payment extends MpBaseEntity implements EntityBaseFunction<PaymentDto> {
|
||||
|
||||
/** 关联的业务id */
|
||||
@DbColumn(comment = "商户编码")
|
||||
@DbMySqlIndex(comment = "业务Id索引",type = MySqlIndexType.UNIQUE)
|
||||
@DbColumn(comment = "关联的业务id")
|
||||
private String businessId;
|
||||
|
||||
/** 商户编码 */
|
||||
@@ -53,30 +56,42 @@ public class Payment extends MpBaseEntity implements EntityBaseFunction<PaymentD
|
||||
private String mchAppCode;
|
||||
|
||||
/** 标题 */
|
||||
@DbColumn(comment = "标题")
|
||||
private String title;
|
||||
|
||||
/** 描述 */
|
||||
@DbColumn(comment = "描述")
|
||||
private String description;
|
||||
|
||||
/** 是否是异步支付 */
|
||||
@DbColumn(comment = "是否是异步支付")
|
||||
private boolean asyncPayMode;
|
||||
|
||||
/** 是否是组合支付 */
|
||||
@DbColumn(comment = "是否是组合支付")
|
||||
private boolean combinationPayMode;
|
||||
|
||||
/**
|
||||
* 异步支付通道
|
||||
* @see cn.bootx.platform.daxpay.code.pay.PayChannelEnum#ALI
|
||||
*/
|
||||
@DbColumn(comment = "异步支付通道")
|
||||
private String asyncPayChannel;
|
||||
|
||||
/** 金额 */
|
||||
@DbColumn(comment = "金额")
|
||||
private BigDecimal amount;
|
||||
|
||||
/** 可退款余额 */
|
||||
@DbColumn(comment = "可退款余额")
|
||||
private BigDecimal refundableBalance;
|
||||
|
||||
/** 错误码 */
|
||||
@DbColumn(comment = "错误码")
|
||||
private String errorCode;
|
||||
|
||||
/** 错误信息 */
|
||||
@DbColumn(comment = "错误信息")
|
||||
private String errorMsg;
|
||||
|
||||
/**
|
||||
@@ -86,6 +101,7 @@ public class Payment extends MpBaseEntity implements EntityBaseFunction<PaymentD
|
||||
@TableField(typeHandler = JacksonRawTypeHandler.class)
|
||||
@BigField
|
||||
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
|
||||
@DbColumn(comment = "支付通道信息列表")
|
||||
private List<PayChannelInfo> payChannelInfo;
|
||||
|
||||
/**
|
||||
@@ -95,21 +111,26 @@ public class Payment extends MpBaseEntity implements EntityBaseFunction<PaymentD
|
||||
@TableField(typeHandler = JacksonRawTypeHandler.class)
|
||||
@BigField
|
||||
@DbMySqlFieldType(MySqlFieldTypeEnum.LONGTEXT)
|
||||
@DbColumn(comment = "退款信息列表")
|
||||
private List<RefundableInfo> refundableInfo;
|
||||
|
||||
/**
|
||||
* 支付状态
|
||||
* @see PayStatusCode#TRADE_PROGRESS
|
||||
*/
|
||||
@DbColumn(comment = "支付状态")
|
||||
private String payStatus;
|
||||
|
||||
/** 支付时间 */
|
||||
@DbColumn(comment = "支付时间")
|
||||
private LocalDateTime payTime;
|
||||
|
||||
/** 支付终端ip */
|
||||
@DbColumn(comment = "支付终端ip")
|
||||
private String clientIp;
|
||||
|
||||
/** 过期时间 */
|
||||
@DbColumn(comment = "过期时间")
|
||||
private LocalDateTime expiredTime;
|
||||
|
||||
@Override
|
||||
|
@@ -28,10 +28,10 @@ public class PayNotifyRecordDto extends BaseDto implements Serializable {
|
||||
private String notifyInfo;
|
||||
|
||||
@Schema(description = "支付通道")
|
||||
private Integer payChannel;
|
||||
private String payChannel;
|
||||
|
||||
@Schema(description = "处理状态")
|
||||
private Integer status;
|
||||
private String status;
|
||||
|
||||
@Schema(description = "提示信息")
|
||||
private String msg;
|
||||
|
@@ -42,6 +42,9 @@ public class PaymentDto extends BaseDto implements Serializable {
|
||||
@Schema(description = "是否是异步支付")
|
||||
private boolean asyncPayMode;
|
||||
|
||||
@Schema(description = "是否是组合支付")
|
||||
private boolean combinationPayMode;
|
||||
|
||||
/**
|
||||
* @see cn.bootx.platform.daxpay.code.pay.PayChannelEnum#ASYNC_TYPE_CODE
|
||||
*/
|
||||
|
@@ -58,7 +58,7 @@ public class RefundRecordDto extends BaseDto {
|
||||
* @see PayStatusCode#REFUND_PROCESS_FAIL
|
||||
*/
|
||||
@Schema(description = "退款状态")
|
||||
private int refundStatus;
|
||||
private String refundStatus;
|
||||
|
||||
@Schema(description = "错误码")
|
||||
private String errorCode;
|
||||
|
@@ -19,6 +19,9 @@ import java.util.List;
|
||||
@Schema(title = "结算台组合支付参数")
|
||||
public class CashierCombinationPayParam {
|
||||
|
||||
@Schema(description = "商户编码")
|
||||
private String mchCode;
|
||||
|
||||
@Schema(description = "商户应用编码")
|
||||
private String mchAppCode;
|
||||
|
||||
|
@@ -1,7 +1,6 @@
|
||||
package cn.bootx.platform.daxpay.param.channel.wechat;
|
||||
|
||||
import cn.bootx.platform.common.core.annotation.QueryParam;
|
||||
import cn.bootx.platform.daxpay.code.paymodel.WeChatPayCode;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
@@ -38,12 +37,6 @@ public class WeChatPayConfigParam {
|
||||
@Schema(description = "微信应用appId")
|
||||
private String wxAppId;
|
||||
|
||||
/**
|
||||
* @see WeChatPayCode#API_V2
|
||||
*/
|
||||
// @Schema(description = "api版本")
|
||||
// private String apiVersion;
|
||||
|
||||
@Schema(description = "商户平台「API安全」中的 APIv2 密钥")
|
||||
private String apiKeyV2;
|
||||
|
||||
|
Reference in New Issue
Block a user