Commit cbc61184bd399666cfa0fe7d0d789068a8cab636

Authored by YunaiV
1 parent 67d60e32

mall + pay:

1、优化微信公众号 MP 支付的实现
Showing 19 changed files with 348 additions and 267 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/notify/PayOrderNotifyRespDTO.java
@@ -35,14 +35,4 @@ public class PayOrderNotifyRespDTO { @@ -35,14 +35,4 @@ public class PayOrderNotifyRespDTO {
35 */ 35 */
36 private LocalDateTime successTime; 36 private LocalDateTime successTime;
37 37
38 - /**  
39 - * TODO @jason 结合其他的渠道定义成枚举,  
40 - *  
41 - * alipay  
42 - * TRADE_CLOSED,未付款交易超时关闭,或支付完成后全额退款。  
43 - * TRADE_SUCCESS, 交易支付成功  
44 - * TRADE_FINISHED 交易结束,不可退款。  
45 - */  
46 - private String tradeStatus;  
47 -  
48 } 38 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/exception/PayException.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.exception;
  2 +
  3 +import lombok.AllArgsConstructor;
  4 +import lombok.Data;
  5 +import lombok.EqualsAndHashCode;
  6 +import lombok.NoArgsConstructor;
  7 +
  8 +/**
  9 + * 业务逻辑异常 Exception
  10 + */
  11 +@Data
  12 +@EqualsAndHashCode(callSuper = true)
  13 +@NoArgsConstructor
  14 +@AllArgsConstructor
  15 +public class PayException extends RuntimeException {
  16 +
  17 + /**
  18 + * 第三方平台的错误码
  19 + */
  20 + private String code;
  21 + /**
  22 + * 第三方平台的错误提示
  23 + */
  24 + private String message;
  25 +
  26 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
@@ -7,18 +7,18 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDT @@ -7,18 +7,18 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDT
7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; 7 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; 8 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; 9 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
10 -import com.alipay.api.AlipayResponse;import lombok.extern.slf4j.Slf4j; 10 +import com.alipay.api.AlipayResponse;
  11 +import lombok.extern.slf4j.Slf4j;
11 12
12 import javax.validation.Validation; 13 import javax.validation.Validation;
13 import java.time.LocalDateTime; 14 import java.time.LocalDateTime;
14 15
15 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER; 16 import static cn.hutool.core.date.DatePattern.NORM_DATETIME_FORMATTER;
16 -import static cn.hutool.core.date.DatePattern.NORM_DATETIME_MS_FORMATTER;  
17 -import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;  
18 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0; 17 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception0;
19 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 18 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
20 import static cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants.PAY_EXCEPTION; 19 import static cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants.PAY_EXCEPTION;
21 20
  21 +// TODO 芋艿:优化下,替换异常;
22 /** 22 /**
23 * 支付客户端的抽象类,提供模板方法,减少子类的冗余代码 23 * 支付客户端的抽象类,提供模板方法,减少子类的冗余代码
24 * 24 *
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
@@ -7,8 +7,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; @@ -7,8 +7,8 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
7 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.*; 7 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.*;
8 import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXLitePayClient; 8 import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXLitePayClient;
9 import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXNativePayClient; 9 import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXNativePayClient;
10 -import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig;  
11 -import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPubPayClient; 10 +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig;
  11 +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPubPayClient;
12 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; 12 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
13 import lombok.extern.slf4j.Slf4j; 13 import lombok.extern.slf4j.Slf4j;
14 14
@@ -60,10 +60,10 @@ public class PayClientFactoryImpl implements PayClientFactory { @@ -60,10 +60,10 @@ public class PayClientFactoryImpl implements PayClientFactory {
60 // 创建客户端 60 // 创建客户端
61 // TODO @芋艿 WX_LITE WX_APP 如果不添加在 项目启动的时候去初始化会报错无法启动。所以我手动加了两个,具体需要你来配 61 // TODO @芋艿 WX_LITE WX_APP 如果不添加在 项目启动的时候去初始化会报错无法启动。所以我手动加了两个,具体需要你来配
62 switch (channelEnum) { 62 switch (channelEnum) {
63 - case WX_PUB: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);  
64 - case WX_LITE: return (AbstractPayClient<Config>) new WXLitePayClient(channelId, (WXPayClientConfig) config); //微信小程序请求支付  
65 - case WX_APP: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);  
66 - case WX_NATIVE: return (AbstractPayClient<Config>) new WXNativePayClient(channelId, (WXPayClientConfig) config); 63 + case WX_PUB: return (AbstractPayClient<Config>) new WxPubPayClient(channelId, (WxPayClientConfig) config);
  64 + case WX_LITE: return (AbstractPayClient<Config>) new WXLitePayClient(channelId, (WxPayClientConfig) config); //微信小程序请求支付
  65 + case WX_APP: return (AbstractPayClient<Config>) new WxPubPayClient(channelId, (WxPayClientConfig) config);
  66 + case WX_NATIVE: return (AbstractPayClient<Config>) new WXNativePayClient(channelId, (WxPayClientConfig) config);
67 case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config); 67 case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config);
68 case ALIPAY_QR: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config); 68 case ALIPAY_QR: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
69 case ALIPAY_APP: return (AbstractPayClient<Config>) new AlipayAppPayClient(channelId, (AlipayPayClientConfig) config); 69 case ALIPAY_APP: return (AbstractPayClient<Config>) new AlipayAppPayClient(channelId, (AlipayPayClientConfig) config);
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayClient.java renamed to yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AbstractAlipayPayClient.java
@@ -25,16 +25,16 @@ import java.util.Map; @@ -25,16 +25,16 @@ import java.util.Map;
25 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; 25 import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
26 26
27 /** 27 /**
28 - * 支付宝抽象类, 实现支付宝统一的接口。如退款 28 + * 支付宝抽象类,实现支付宝统一的接口、以及部分实现(退款)
29 * 29 *
30 - * @author jason 30 + * @author jason
31 */ 31 */
32 @Slf4j 32 @Slf4j
33 -public abstract class AbstractAlipayClient extends AbstractPayClient<AlipayPayClientConfig> { 33 +public abstract class AbstractAlipayPayClient extends AbstractPayClient<AlipayPayClientConfig> {
34 34
35 protected DefaultAlipayClient client; 35 protected DefaultAlipayClient client;
36 36
37 - public AbstractAlipayClient(Long channelId, String channelCode, AlipayPayClientConfig config) { 37 + public AbstractAlipayPayClient(Long channelId, String channelCode, AlipayPayClientConfig config) {
38 super(channelId, channelCode, config); 38 super(channelId, channelCode, config);
39 } 39 }
40 40
@@ -106,9 +106,11 @@ public abstract class AbstractAlipayClient extends AbstractPayClient&lt;AlipayPayCl @@ -106,9 +106,11 @@ public abstract class AbstractAlipayClient extends AbstractPayClient&lt;AlipayPayCl
106 .build(); 106 .build();
107 } 107 }
108 // 2.2 支付的情况 108 // 2.2 支付的情况
109 - return PayOrderNotifyRespDTO.builder().orderExtensionNo(bodyObj.get("out_trade_no"))  
110 - .channelOrderNo(bodyObj.get("trade_no")).channelUserId(bodyObj.get("seller_id"))  
111 - .tradeStatus(bodyObj.get("trade_status")).successTime(parseTime(params.get("notify_time"))) 109 + return PayOrderNotifyRespDTO.builder()
  110 + .orderExtensionNo(bodyObj.get("out_trade_no"))
  111 + .channelOrderNo(bodyObj.get("trade_no"))
  112 + .channelUserId(bodyObj.get("seller_id"))
  113 + .successTime(parseTime(params.get("notify_time")))
112 .build(); 114 .build();
113 } 115 }
114 116
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayAppPayClient.java
@@ -20,7 +20,7 @@ import lombok.extern.slf4j.Slf4j; @@ -20,7 +20,7 @@ import lombok.extern.slf4j.Slf4j;
20 * @author 芋道源码 20 * @author 芋道源码
21 */ 21 */
22 @Slf4j 22 @Slf4j
23 -public class AlipayAppPayClient extends AbstractAlipayClient { 23 +public class AlipayAppPayClient extends AbstractAlipayPayClient {
24 24
25 public AlipayAppPayClient(Long channelId, AlipayPayClientConfig config) { 25 public AlipayAppPayClient(Long channelId, AlipayPayClientConfig config) {
26 super(channelId, PayChannelEnum.ALIPAY_APP.getCode(), config); 26 super(channelId, PayChannelEnum.ALIPAY_APP.getCode(), config);
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayBarPayClient.java
@@ -24,7 +24,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU @@ -24,7 +24,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
24 * @author 芋道源码 24 * @author 芋道源码
25 */ 25 */
26 @Slf4j 26 @Slf4j
27 -public class AlipayBarPayClient extends AbstractAlipayClient { 27 +public class AlipayBarPayClient extends AbstractAlipayPayClient {
28 28
29 public AlipayBarPayClient(Long channelId, AlipayPayClientConfig config) { 29 public AlipayBarPayClient(Long channelId, AlipayPayClientConfig config) {
30 super(channelId, PayChannelEnum.ALIPAY_BAR.getCode(), config); 30 super(channelId, PayChannelEnum.ALIPAY_BAR.getCode(), config);
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
@@ -22,7 +22,7 @@ import java.util.Objects; @@ -22,7 +22,7 @@ import java.util.Objects;
22 * @author XGD 22 * @author XGD
23 */ 23 */
24 @Slf4j 24 @Slf4j
25 -public class AlipayPcPayClient extends AbstractAlipayClient { 25 +public class AlipayPcPayClient extends AbstractAlipayPayClient {
26 26
27 public AlipayPcPayClient(Long channelId, AlipayPayClientConfig config) { 27 public AlipayPcPayClient(Long channelId, AlipayPayClientConfig config) {
28 super(channelId, PayChannelEnum.ALIPAY_PC.getCode(), config); 28 super(channelId, PayChannelEnum.ALIPAY_PC.getCode(), config);
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
@@ -18,7 +18,7 @@ import lombok.extern.slf4j.Slf4j; @@ -18,7 +18,7 @@ import lombok.extern.slf4j.Slf4j;
18 * @author 芋道源码 18 * @author 芋道源码
19 */ 19 */
20 @Slf4j 20 @Slf4j
21 -public class AlipayQrPayClient extends AbstractAlipayClient { 21 +public class AlipayQrPayClient extends AbstractAlipayPayClient {
22 22
23 public AlipayQrPayClient(Long channelId, AlipayPayClientConfig config) { 23 public AlipayQrPayClient(Long channelId, AlipayPayClientConfig config) {
24 super(channelId, PayChannelEnum.ALIPAY_QR.getCode(), config); 24 super(channelId, PayChannelEnum.ALIPAY_QR.getCode(), config);
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
@@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j; @@ -19,7 +19,7 @@ import lombok.extern.slf4j.Slf4j;
19 * @author 芋道源码 19 * @author 芋道源码
20 */ 20 */
21 @Slf4j 21 @Slf4j
22 -public class AlipayWapPayClient extends AbstractAlipayClient { 22 +public class AlipayWapPayClient extends AbstractAlipayPayClient {
23 23
24 public AlipayWapPayClient(Long channelId, AlipayPayClientConfig config) { 24 public AlipayWapPayClient(Long channelId, AlipayPayClientConfig config) {
25 super(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), config); 25 super(channelId, PayChannelEnum.ALIPAY_WAP.getCode(), config);
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
  2 +
  3 +import cn.hutool.core.bean.BeanUtil;
  4 +import cn.hutool.core.date.LocalDateTimeUtil;
  5 +import cn.hutool.core.date.TemporalAccessorUtil;
  6 +import cn.hutool.core.lang.Assert;
  7 +import cn.hutool.core.map.MapUtil;
  8 +import cn.hutool.core.util.StrUtil;
  9 +import cn.iocoder.yudao.framework.common.util.io.FileUtils;
  10 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  11 +import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;
  12 +import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;
  13 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  14 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  15 +import cn.iocoder.yudao.framework.pay.core.client.exception.PayException;
  16 +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
  17 +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
  18 +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
  19 +import com.github.binarywang.wxpay.config.WxPayConfig;
  20 +import com.github.binarywang.wxpay.exception.WxPayException;
  21 +import com.github.binarywang.wxpay.service.WxPayService;
  22 +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
  23 +import lombok.extern.slf4j.Slf4j;
  24 +
  25 +import java.time.LocalDateTime;
  26 +import java.time.ZoneId;
  27 +import java.util.Objects;
  28 +
  29 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
  30 +
  31 +/**
  32 + * 微信支付抽象类,实现微信统一的接口、以及部分实现(退款)
  33 + *
  34 + * @author 遇到源码
  35 + */
  36 +@Slf4j
  37 +public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientConfig> {
  38 +
  39 + protected WxPayService client;
  40 +
  41 + public AbstractWxPayClient(Long channelId, String channelCode, WxPayClientConfig config) {
  42 + super(channelId, channelCode, config);
  43 + }
  44 +
  45 + /**
  46 + * 初始化 client 客户端
  47 + *
  48 + * @param tradeType 交易类型
  49 + */
  50 + protected void doInit(String tradeType) {
  51 + // 创建 config 配置
  52 + WxPayConfig payConfig = new WxPayConfig();
  53 + BeanUtil.copyProperties(config, payConfig, "keyContent");
  54 + payConfig.setTradeType(tradeType);
  55 + if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
  56 + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
  57 + payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
  58 + }
  59 + if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
  60 + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
  61 + payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
  62 + }
  63 +
  64 + // 创建 client 客户端
  65 + client = new WxPayServiceImpl();
  66 + client.setConfig(payConfig);
  67 + }
  68 +
  69 + @Override
  70 + protected PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO)
  71 + throws Throwable {
  72 + try {
  73 + switch (config.getApiVersion()) {
  74 + case WxPayClientConfig.API_VERSION_V2:
  75 + return doUnifiedOrderV2(reqDTO);
  76 + case WxPayClientConfig.API_VERSION_V3:
  77 + return doUnifiedOrderV3(reqDTO);
  78 + default:
  79 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  80 + }
  81 + } catch (WxPayException e) {
  82 + log.error("[doUnifiedOrder][request({}) 发起支付失败]", toJsonString(reqDTO), e);
  83 + throw buildPayException(e);
  84 + }
  85 + }
  86 +
  87 + /**
  88 + * 【V2】调用支付渠道,统一下单
  89 + *
  90 + * @param reqDTO 下单信息
  91 + * @return 各支付渠道的返回结果
  92 + */
  93 + protected abstract PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO)
  94 + throws WxPayException;
  95 +
  96 + /**
  97 + * 【V3】调用支付渠道,统一下单
  98 + *
  99 + * @param reqDTO 下单信息
  100 + * @return 各支付渠道的返回结果
  101 + */
  102 + protected abstract PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO)
  103 + throws WxPayException;
  104 +
  105 +
  106 + @Override
  107 + public Object parseNotify(PayNotifyReqDTO rawNotify) {
  108 + log.info("[parseNotify][微信支付回调 data 数据: {}]", rawNotify.getBody());
  109 + try {
  110 + // 微信支付 v2 回调结果处理
  111 + switch (config.getApiVersion()) {
  112 + case WxPayClientConfig.API_VERSION_V2:
  113 + return parseOrderNotifyV2(rawNotify);
  114 + case WxPayClientConfig.API_VERSION_V3:
  115 + return parseOrderNotifyV3(rawNotify);
  116 + default:
  117 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  118 + }
  119 + } catch (WxPayException e) {
  120 + log.error("[parseNotify][rawNotify({}) 解析失败]", toJsonString(rawNotify), e);
  121 + throw buildPayException(e);
  122 + }
  123 + }
  124 +
  125 + private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException {
  126 + WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
  127 + Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
  128 + // 转换结果
  129 + return PayOrderNotifyRespDTO
  130 + .builder()
  131 + .orderExtensionNo(notifyResult.getOutTradeNo())
  132 + .channelOrderNo(notifyResult.getTransactionId())
  133 + .channelUserId(notifyResult.getOpenid())
  134 + .successTime(parseDateV2(notifyResult.getTimeEnd()))
  135 + .build();
  136 + }
  137 +
  138 + private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException {
  139 + WxPayOrderNotifyV3Result notifyResult = client.parseOrderNotifyV3Result(data.getBody(), null);
  140 + WxPayOrderNotifyV3Result.DecryptNotifyResult result = notifyResult.getResult();
  141 + // 转换结果
  142 + Assert.isTrue(Objects.equals(notifyResult.getResult().getTradeState(), "SUCCESS"),
  143 + "支付结果非 SUCCESS");
  144 + return PayOrderNotifyRespDTO.builder()
  145 + .orderExtensionNo(result.getOutTradeNo())
  146 + .channelOrderNo(result.getTradeState())
  147 + .channelUserId(result.getPayer() != null ? result.getPayer().getOpenid() : null)
  148 + .successTime(parseDateV3(result.getSuccessTime()))
  149 + .build();
  150 + }
  151 +
  152 + // ========== 各种工具方法 ==========
  153 +
  154 + static String getOpenid(PayOrderUnifiedReqDTO reqDTO) {
  155 + String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid");
  156 + if (StrUtil.isEmpty(openid)) {
  157 + throw new IllegalArgumentException("支付请求的 openid 不能为空!");
  158 + }
  159 + return openid;
  160 + }
  161 +
  162 + static PayException buildPayException(WxPayException e) {
  163 + return new PayException(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode()),
  164 + ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()));
  165 + }
  166 +
  167 + static String formatDateV2(LocalDateTime time) {
  168 + return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), "yyyyMMddHHmmss");
  169 + }
  170 +
  171 + static LocalDateTime parseDateV2(String time) {
  172 + return LocalDateTimeUtil.parse(time, "yyyyMMddHHmmss");
  173 + }
  174 +
  175 + static String formatDateV3(LocalDateTime time) {
  176 + return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), "yyyy-MM-dd'T'HH:mm:ssXXX");
  177 + }
  178 +
  179 + static LocalDateTime parseDateV3(String time) {
  180 + return LocalDateTimeUtil.parse(time, "yyyy-MM-dd'T'HH:mm:ssXXX");
  181 + }
  182 +
  183 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXLitePayClient.java
@@ -39,11 +39,11 @@ import java.util.Objects; @@ -39,11 +39,11 @@ import java.util.Objects;
39 * @author zwy 39 * @author zwy
40 */ 40 */
41 @Slf4j 41 @Slf4j
42 -public class WXLitePayClient extends AbstractPayClient<WXPayClientConfig> { 42 +public class WXLitePayClient extends AbstractPayClient<WxPayClientConfig> {
43 43
44 private WxPayService client; 44 private WxPayService client;
45 45
46 - public WXLitePayClient(Long channelId, WXPayClientConfig config) { 46 + public WXLitePayClient(Long channelId, WxPayClientConfig config) {
47 super(channelId, PayChannelEnum.WX_LITE.getCode(), config); 47 super(channelId, PayChannelEnum.WX_LITE.getCode(), config);
48 } 48 }
49 49
@@ -99,7 +99,7 @@ public class WXLitePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -99,7 +99,7 @@ public class WXLitePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
99 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() 99 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
100 .outTradeNo(reqDTO.getMerchantOrderId()) 100 .outTradeNo(reqDTO.getMerchantOrderId())
101 .body(reqDTO.getBody()) 101 .body(reqDTO.getBody())
102 - .totalFee(reqDTO.getAmount().intValue()) // 单位分 102 + .totalFee(reqDTO.getAmount()) // 单位分
103 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")) // v2的时间格式 103 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")) // v2的时间格式
104 .spbillCreateIp(reqDTO.getUserIp()) 104 .spbillCreateIp(reqDTO.getUserIp())
105 .openid(getOpenid(reqDTO)) 105 .openid(getOpenid(reqDTO))
@@ -117,9 +117,7 @@ public class WXLitePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -117,9 +117,7 @@ public class WXLitePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
117 request.setDescription(reqDTO.getBody()); 117 request.setDescription(reqDTO.getBody());
118 request.setAmount(new WxPayUnifiedOrderV3Request 118 request.setAmount(new WxPayUnifiedOrderV3Request
119 .Amount() 119 .Amount()
120 - .setTotal(reqDTO  
121 - .getAmount()  
122 - .intValue())); // 单位分 120 + .setTotal(reqDTO.getAmount())); // 单位分
123 request.setTimeExpire(DateUtil.format(Date.from(reqDTO.getExpireTime().atZone(ZoneId.systemDefault()).toInstant()), "yyyy-MM-dd'T'HH:mm:ssXXX")); // v3的时间格式 121 request.setTimeExpire(DateUtil.format(Date.from(reqDTO.getExpireTime().atZone(ZoneId.systemDefault()).toInstant()), "yyyy-MM-dd'T'HH:mm:ssXXX")); // v3的时间格式
124 request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO))); 122 request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
125 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); 123 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
@@ -149,9 +147,9 @@ public class WXLitePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -149,9 +147,9 @@ public class WXLitePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
149 log.info("[parseOrderNotify][微信支付回调data数据:{}]", data.getBody()); 147 log.info("[parseOrderNotify][微信支付回调data数据:{}]", data.getBody());
150 // 微信支付 v2 回调结果处理 148 // 微信支付 v2 回调结果处理
151 switch (config.getApiVersion()) { 149 switch (config.getApiVersion()) {
152 - case WXPayClientConfig.API_VERSION_V2: 150 + case WxPayClientConfig.API_VERSION_V2:
153 return parseOrderNotifyV2(data); 151 return parseOrderNotifyV2(data);
154 - case WXPayClientConfig.API_VERSION_V3: 152 + case WxPayClientConfig.API_VERSION_V3:
155 return parseOrderNotifyV3(data); 153 return parseOrderNotifyV3(data);
156 default: 154 default:
157 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); 155 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXNativePayClient.java
@@ -37,11 +37,11 @@ import java.util.Objects; @@ -37,11 +37,11 @@ import java.util.Objects;
37 * @author zwy 37 * @author zwy
38 */ 38 */
39 @Slf4j 39 @Slf4j
40 -public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> { 40 +public class WXNativePayClient extends AbstractPayClient<WxPayClientConfig> {
41 41
42 private WxPayService client; 42 private WxPayService client;
43 43
44 - public WXNativePayClient(Long channelId, WXPayClientConfig config) { 44 + public WXNativePayClient(Long channelId, WxPayClientConfig config) {
45 super(channelId, PayChannelEnum.WX_NATIVE.getCode(), config); 45 super(channelId, PayChannelEnum.WX_NATIVE.getCode(), config);
46 } 46 }
47 47
@@ -134,9 +134,9 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -134,9 +134,9 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
134 log.info("微信支付回调data数据:{}", data.getBody()); 134 log.info("微信支付回调data数据:{}", data.getBody());
135 // 微信支付 v2 回调结果处理 135 // 微信支付 v2 回调结果处理
136 switch (config.getApiVersion()) { 136 switch (config.getApiVersion()) {
137 - case WXPayClientConfig.API_VERSION_V2: 137 + case WxPayClientConfig.API_VERSION_V2:
138 return parseOrderNotifyV2(data); 138 return parseOrderNotifyV2(data);
139 - case WXPayClientConfig.API_VERSION_V3: 139 + case WxPayClientConfig.API_VERSION_V3:
140 return parseOrderNotifyV3(data); 140 return parseOrderNotifyV3(data);
141 default: 141 default:
142 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); 142 throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPubPayClient.java deleted
1 -package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;  
2 -  
3 -import cn.hutool.core.bean.BeanUtil;  
4 -import cn.hutool.core.date.LocalDateTimeUtil;  
5 -import cn.hutool.core.date.TemporalAccessorUtil;  
6 -import cn.hutool.core.lang.Assert;  
7 -import cn.hutool.core.map.MapUtil;  
8 -import cn.hutool.core.util.StrUtil;  
9 -import cn.iocoder.yudao.framework.common.util.io.FileUtils;  
10 -import cn.iocoder.yudao.framework.common.util.json.JsonUtils;  
11 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;  
12 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;  
13 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;  
14 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
15 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;  
16 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;  
17 -import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;  
18 -import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;  
19 -import cn.iocoder.yudao.framework.pay.core.enums.PayDisplayModeEnum;  
20 -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;  
21 -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;  
22 -import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;  
23 -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;  
24 -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;  
25 -import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;  
26 -import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;  
27 -import com.github.binarywang.wxpay.config.WxPayConfig;  
28 -import com.github.binarywang.wxpay.constant.WxPayConstants;  
29 -import com.github.binarywang.wxpay.exception.WxPayException;  
30 -import com.github.binarywang.wxpay.service.WxPayService;  
31 -import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;  
32 -import lombok.extern.slf4j.Slf4j;  
33 -  
34 -import java.time.LocalDateTime;  
35 -import java.time.ZoneId;  
36 -import java.util.Objects;  
37 -  
38 -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;  
39 -  
40 -/**  
41 - * 微信支付(公众号)的 PayClient 实现类  
42 - *  
43 - * @author 芋道源码  
44 - */  
45 -@Slf4j  
46 -public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {  
47 -  
48 - private WxPayService client;  
49 -  
50 - public WXPubPayClient(Long channelId, WXPayClientConfig config) {  
51 - super(channelId, PayChannelEnum.WX_PUB.getCode(), config);  
52 - }  
53 -  
54 - @Override  
55 - protected void doInit() {  
56 - WxPayConfig payConfig = new WxPayConfig();  
57 - BeanUtil.copyProperties(config, payConfig, "keyContent");  
58 - payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式  
59 -// if (StrUtil.isNotEmpty(config.getKeyContent())) {  
60 -// payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8));  
61 -// }  
62 - if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {  
63 - // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决  
64 - payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());  
65 - }  
66 - if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {  
67 - // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决  
68 - payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());  
69 - }  
70 - // 真实客户端  
71 - this.client = new WxPayServiceImpl();  
72 - client.setConfig(payConfig);  
73 - }  
74 -  
75 - @Override  
76 - public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {  
77 - WxPayMpOrderResult response = null;  
78 - try {  
79 - switch (config.getApiVersion()) {  
80 - case WXPayClientConfig.API_VERSION_V2:  
81 - response = this.unifiedOrderV2(reqDTO);  
82 - break;  
83 - case WXPayClientConfig.API_VERSION_V3:  
84 - WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO);  
85 - // 将 V3 的结果,统一转换成 V2。返回的字段是一致的  
86 - response = new WxPayMpOrderResult();  
87 - BeanUtil.copyProperties(responseV3, response, true);  
88 - break;  
89 - default:  
90 - throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));  
91 - }  
92 - } catch (WxPayException e) {  
93 - log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e);  
94 -// return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"),  
95 -// ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()),null, codeMapping);  
96 - System.out.println();  
97 - }  
98 - return new PayOrderUnifiedRespDTO().setDisplayMode(PayDisplayModeEnum.CUSTOM.getMode())  
99 - .setDisplayContent(JsonUtils.toJsonString(response));  
100 - }  
101 -  
102 -  
103 - private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {  
104 - // 构建 WxPayUnifiedOrderRequest 对象  
105 - WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()  
106 - .outTradeNo(reqDTO.getMerchantOrderId())  
107 - .body(reqDTO.getBody())  
108 - .totalFee(reqDTO.getAmount()) // 单位分  
109 - .timeExpire(formatDate(reqDTO.getExpireTime()))  
110 - .spbillCreateIp(reqDTO.getUserIp())  
111 - .openid(getOpenid(reqDTO))  
112 - .notifyUrl(reqDTO.getNotifyUrl())  
113 - .build();  
114 - // 执行请求  
115 - return client.createOrder(request);  
116 - }  
117 -  
118 - private WxPayUnifiedOrderV3Result.JsapiResult unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {  
119 - // 构建 WxPayUnifiedOrderRequest 对象  
120 - WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();  
121 - request.setOutTradeNo(reqDTO.getMerchantOrderId());  
122 - request.setDescription(reqDTO.getBody());  
123 - request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分  
124 - request.setTimeExpire(formatDate(reqDTO.getExpireTime()));  
125 - request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));  
126 - request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));  
127 - request.setNotifyUrl(reqDTO.getNotifyUrl());  
128 - // 执行请求  
129 - return client.createOrderV3(TradeTypeEnum.JSAPI, request);  
130 - }  
131 -  
132 - private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) {  
133 - String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid");  
134 - if (StrUtil.isEmpty(openid)) {  
135 - throw new IllegalArgumentException("支付请求的 openid 不能为空!");  
136 - }  
137 - return openid;  
138 - }  
139 -  
140 - /**  
141 - *  
142 - * 微信支付回调 分v2 和v3 的处理方式  
143 - *  
144 - * @param data 通知结果  
145 - * @return 支付回调对象  
146 - * @throws WxPayException 微信异常类  
147 - */  
148 -// @Override  
149 - public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException {  
150 - log.info("[parseOrderNotify][微信支付回调data数据: {}]", data.getBody());  
151 - // 微信支付 v2 回调结果处理  
152 - switch (config.getApiVersion()) {  
153 - case WXPayClientConfig.API_VERSION_V2:  
154 - return parseOrderNotifyV2(data);  
155 - case WXPayClientConfig.API_VERSION_V3:  
156 - return parseOrderNotifyV3(data);  
157 - default:  
158 - throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));  
159 - }  
160 - }  
161 -  
162 - private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException {  
163 - WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);  
164 - WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();  
165 - // 转换结果  
166 - Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),  
167 - "支付结果非 SUCCESS");  
168 - return PayOrderNotifyRespDTO  
169 - .builder()  
170 - .orderExtensionNo(result.getOutTradeNo())  
171 - .channelOrderNo(result.getTradeState())  
172 - .successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))  
173 - .build();  
174 - }  
175 -  
176 - private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException {  
177 - WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());  
178 - Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");  
179 - // 转换结果  
180 - return PayOrderNotifyRespDTO  
181 - .builder()  
182 - .orderExtensionNo(notifyResult.getOutTradeNo())  
183 - .channelOrderNo(notifyResult.getTransactionId())  
184 - .channelUserId(notifyResult.getOpenid())  
185 - .successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))  
186 - .build();  
187 -  
188 - }  
189 -  
190 - @Override  
191 - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {  
192 - // TODO 需要实现  
193 - throw new UnsupportedOperationException();  
194 - }  
195 -  
196 - private static String formatDate(LocalDateTime time) {  
197 -// return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), "yyyy-MM-dd'T'HH:mm:ssXXX");  
198 - return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), "yyyyMMddHHmmss");  
199 - }  
200 -  
201 -}  
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPayClientConfig.java renamed to yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java
@@ -18,15 +18,17 @@ import java.util.Set; @@ -18,15 +18,17 @@ import java.util.Set;
18 * @author 芋道源码 18 * @author 芋道源码
19 */ 19 */
20 @Data 20 @Data
21 -public class WXPayClientConfig implements PayClientConfig { 21 +public class WxPayClientConfig implements PayClientConfig {
22 22
23 /** 23 /**
24 * API 版本 - V2 24 * API 版本 - V2
  25 + *
25 * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1 26 * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1
26 */ 27 */
27 public static final String API_VERSION_V2 = "v2"; 28 public static final String API_VERSION_V2 = "v2";
28 /** 29 /**
29 * API 版本 - V3 30 * API 版本 - V3
  31 + *
30 * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml 32 * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml
31 */ 33 */
32 public static final String API_VERSION_V3 = "v3"; 34 public static final String API_VERSION_V3 = "v3";
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
  2 +
  3 +import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  4 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  6 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
  8 +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
  9 +import cn.iocoder.yudao.framework.pay.core.enums.PayDisplayModeEnum;
  10 +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
  11 +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
  12 +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
  13 +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
  14 +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
  15 +import com.github.binarywang.wxpay.constant.WxPayConstants;
  16 +import com.github.binarywang.wxpay.exception.WxPayException;
  17 +import lombok.extern.slf4j.Slf4j;
  18 +
  19 +/**
  20 + * 微信支付(公众号)的 PayClient 实现类
  21 + *
  22 + * @author 芋道源码
  23 + */
  24 +@Slf4j
  25 +public class WxPubPayClient extends AbstractWxPayClient {
  26 +
  27 + public WxPubPayClient(Long channelId, WxPayClientConfig config) {
  28 + super(channelId, PayChannelEnum.WX_PUB.getCode(), config);
  29 + }
  30 +
  31 + @Override
  32 + protected void doInit() {
  33 + super.doInit(WxPayConstants.TradeType.JSAPI);
  34 + }
  35 +
  36 + @Override
  37 + protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
  38 + // 构建 WxPayUnifiedOrderRequest 对象
  39 + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
  40 + .outTradeNo(reqDTO.getMerchantOrderId())
  41 + .body(reqDTO.getBody())
  42 + .totalFee(reqDTO.getAmount()) // 单位分
  43 + .timeExpire(formatDateV2(reqDTO.getExpireTime()))
  44 + .spbillCreateIp(reqDTO.getUserIp())
  45 + .openid(getOpenid(reqDTO))
  46 + .notifyUrl(reqDTO.getNotifyUrl())
  47 + .build();
  48 + // 执行请求
  49 + WxPayMpOrderResult response = client.createOrder(request);
  50 +
  51 + // 转换结果
  52 + return new PayOrderUnifiedRespDTO().setDisplayMode(PayDisplayModeEnum.CUSTOM.getMode())
  53 + .setDisplayContent(JsonUtils.toJsonString(response));
  54 + }
  55 +
  56 + @Override
  57 + protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
  58 + // 构建 WxPayUnifiedOrderRequest 对象
  59 + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
  60 + request.setOutTradeNo(reqDTO.getMerchantOrderId());
  61 + request.setDescription(reqDTO.getBody());
  62 + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
  63 + request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
  64 + request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
  65 + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
  66 + request.setNotifyUrl(reqDTO.getNotifyUrl());
  67 + // 执行请求
  68 + WxPayUnifiedOrderV3Result.JsapiResult response = client.createOrderV3(TradeTypeEnum.JSAPI, request);
  69 +
  70 + // 转换结果
  71 + return new PayOrderUnifiedRespDTO().setDisplayMode(PayDisplayModeEnum.CUSTOM.getMode())
  72 + .setDisplayContent(JsonUtils.toJsonString(response));
  73 + }
  74 +
  75 + @Override
  76 + protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {
  77 + // TODO 需要实现
  78 + throw new UnsupportedOperationException();
  79 + }
  80 +
  81 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java
@@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.pay.core.enums; @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.pay.core.enums;
3 import cn.hutool.core.util.ArrayUtil; 3 import cn.hutool.core.util.ArrayUtil;
4 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; 4 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
5 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; 5 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
6 -import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig; 6 +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig;
7 import lombok.AllArgsConstructor; 7 import lombok.AllArgsConstructor;
8 import lombok.Getter; 8 import lombok.Getter;
9 9
@@ -17,10 +17,10 @@ import lombok.Getter; @@ -17,10 +17,10 @@ import lombok.Getter;
17 @AllArgsConstructor 17 @AllArgsConstructor
18 public enum PayChannelEnum { 18 public enum PayChannelEnum {
19 19
20 - WX_PUB("wx_pub", "微信 JSAPI 支付", WXPayClientConfig.class), // 公众号网页  
21 - WX_LITE("wx_lite", "微信小程序支付", WXPayClientConfig.class),  
22 - WX_APP("wx_app", "微信 App 支付", WXPayClientConfig.class),  
23 - WX_NATIVE("wx_native", "微信 native 支付", WXPayClientConfig.class), 20 + WX_PUB("wx_pub", "微信 JSAPI 支付", WxPayClientConfig.class), // 公众号网页
  21 + WX_LITE("wx_lite", "微信小程序支付", WxPayClientConfig.class),
  22 + WX_APP("wx_app", "微信 App 支付", WxPayClientConfig.class),
  23 + WX_NATIVE("wx_native", "微信 native 支付", WxPayClientConfig.class),
24 24
25 ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付", AlipayPayClientConfig.class), 25 ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付", AlipayPayClientConfig.class),
26 ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付", AlipayPayClientConfig.class), 26 ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付", AlipayPayClientConfig.class),
yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java
@@ -7,8 +7,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDT @@ -7,8 +7,8 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDT
7 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; 7 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
8 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayQrPayClient; 8 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayQrPayClient;
9 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayWapPayClient; 9 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayWapPayClient;
10 -import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig;  
11 -import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPubPayClient; 10 +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig;
  11 +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPubPayClient;
12 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; 12 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
13 import org.junit.jupiter.api.Disabled; 13 import org.junit.jupiter.api.Disabled;
14 import org.junit.jupiter.api.Test; 14 import org.junit.jupiter.api.Test;
@@ -27,15 +27,15 @@ public class PayClientFactoryImplIntegrationTest { @@ -27,15 +27,15 @@ public class PayClientFactoryImplIntegrationTest {
27 private final PayClientFactoryImpl payClientFactory = new PayClientFactoryImpl(); 27 private final PayClientFactoryImpl payClientFactory = new PayClientFactoryImpl();
28 28
29 /** 29 /**
30 - * {@link WXPubPayClient} 的 V2 版本 30 + * {@link WxPubPayClient} 的 V2 版本
31 */ 31 */
32 @Test 32 @Test
33 public void testCreatePayClient_WX_PUB_V2() { 33 public void testCreatePayClient_WX_PUB_V2() {
34 // 创建配置 34 // 创建配置
35 - WXPayClientConfig config = new WXPayClientConfig(); 35 + WxPayClientConfig config = new WxPayClientConfig();
36 config.setAppId("wx041349c6f39b268b"); 36 config.setAppId("wx041349c6f39b268b");
37 config.setMchId("1545083881"); 37 config.setMchId("1545083881");
38 - config.setApiVersion(WXPayClientConfig.API_VERSION_V2); 38 + config.setApiVersion(WxPayClientConfig.API_VERSION_V2);
39 config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p"); 39 config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
40 // 创建客户端 40 // 创建客户端
41 Long channelId = RandomUtil.randomLong(); 41 Long channelId = RandomUtil.randomLong();
@@ -48,15 +48,15 @@ public class PayClientFactoryImplIntegrationTest { @@ -48,15 +48,15 @@ public class PayClientFactoryImplIntegrationTest {
48 } 48 }
49 49
50 /** 50 /**
51 - * {@link WXPubPayClient} 的 V3 版本 51 + * {@link WxPubPayClient} 的 V3 版本
52 */ 52 */
53 @Test 53 @Test
54 public void testCreatePayClient_WX_PUB_V3() throws FileNotFoundException { 54 public void testCreatePayClient_WX_PUB_V3() throws FileNotFoundException {
55 // 创建配置 55 // 创建配置
56 - WXPayClientConfig config = new WXPayClientConfig(); 56 + WxPayClientConfig config = new WxPayClientConfig();
57 config.setAppId("wx041349c6f39b268b"); 57 config.setAppId("wx041349c6f39b268b");
58 config.setMchId("1545083881"); 58 config.setMchId("1545083881");
59 - config.setApiVersion(WXPayClientConfig.API_VERSION_V3); 59 + config.setApiVersion(WxPayClientConfig.API_VERSION_V3);
60 config.setPrivateKeyContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_key.pem"))); 60 config.setPrivateKeyContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_key.pem")));
61 config.setPrivateCertContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"))); 61 config.setPrivateCertContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem")));
62 config.setApiV3Key("joerVi8y5DJ3o4ttA0o1uH47Xz1u2Ase"); 62 config.setApiV3Key("joerVi8y5DJ3o4ttA0o1uH47Xz1u2Ase");
yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/merchant/PayChannelServiceTest.java
@@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
4 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
5 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; 5 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
6 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; 6 import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig;
7 -import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig; 7 +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WxPayClientConfig;
8 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; 8 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
9 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; 9 import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest;
10 import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChannelCreateReqVO; 10 import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChannelCreateReqVO;
@@ -49,7 +49,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest { @@ -49,7 +49,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
49 @Test 49 @Test
50 public void testCreateWechatVersion2Channel_success() { 50 public void testCreateWechatVersion2Channel_success() {
51 // 准备参数 51 // 准备参数
52 - WXPayClientConfig v2Config = getV2Config(); 52 + WxPayClientConfig v2Config = getV2Config();
53 PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { 53 PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
54 o.setCode(PayChannelEnum.WX_PUB.getCode()); 54 o.setCode(PayChannelEnum.WX_PUB.getCode());
55 o.setStatus(CommonStatusEnum.ENABLE.getStatus()); 55 o.setStatus(CommonStatusEnum.ENABLE.getStatus());
@@ -70,7 +70,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest { @@ -70,7 +70,7 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
70 @Test 70 @Test
71 public void testCreateWechatVersion3Channel_success() { 71 public void testCreateWechatVersion3Channel_success() {
72 // 准备参数 72 // 准备参数
73 - WXPayClientConfig v3Config = getV3Config(); 73 + WxPayClientConfig v3Config = getV3Config();
74 PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> { 74 PayChannelCreateReqVO reqVO = randomPojo(PayChannelCreateReqVO.class, o -> {
75 o.setCode(PayChannelEnum.WX_PUB.getCode()); 75 o.setCode(PayChannelEnum.WX_PUB.getCode());
76 o.setStatus(CommonStatusEnum.ENABLE.getStatus()); 76 o.setStatus(CommonStatusEnum.ENABLE.getStatus());
@@ -346,22 +346,22 @@ public class PayChannelServiceTest extends BaseDbUnitTest { @@ -346,22 +346,22 @@ public class PayChannelServiceTest extends BaseDbUnitTest {
346 assertPojoEquals(payClientConfig, list.get(0).getConfig()); 346 assertPojoEquals(payClientConfig, list.get(0).getConfig());
347 } 347 }
348 348
349 - public WXPayClientConfig getV2Config() {  
350 - return new WXPayClientConfig() 349 + public WxPayClientConfig getV2Config() {
  350 + return new WxPayClientConfig()
351 .setAppId("APP00001") 351 .setAppId("APP00001")
352 .setMchId("MCH00001") 352 .setMchId("MCH00001")
353 - .setApiVersion(WXPayClientConfig.API_VERSION_V2) 353 + .setApiVersion(WxPayClientConfig.API_VERSION_V2)
354 .setMchKey("dsa1d5s6a1d6sa16d1sa56d15a61das6") 354 .setMchKey("dsa1d5s6a1d6sa16d1sa56d15a61das6")
355 .setApiV3Key("") 355 .setApiV3Key("")
356 .setPrivateCertContent("") 356 .setPrivateCertContent("")
357 .setPrivateKeyContent(""); 357 .setPrivateKeyContent("");
358 } 358 }
359 359
360 - public WXPayClientConfig getV3Config() {  
361 - return new WXPayClientConfig() 360 + public WxPayClientConfig getV3Config() {
  361 + return new WxPayClientConfig()
362 .setAppId("APP00001") 362 .setAppId("APP00001")
363 .setMchId("MCH00001") 363 .setMchId("MCH00001")
364 - .setApiVersion(WXPayClientConfig.API_VERSION_V3) 364 + .setApiVersion(WxPayClientConfig.API_VERSION_V3)
365 .setMchKey("") 365 .setMchKey("")
366 .setApiV3Key("sdadasdsadadsa") 366 .setApiV3Key("sdadasdsadadsa")
367 .setPrivateKeyContent("dsa445das415d15asd16ad156as") 367 .setPrivateKeyContent("dsa445das415d15asd16ad156as")