Commit b76fd25cc6c52d15a8f256a2bb8f33bff93ce793

Authored by 芋道源码
Committed by Gitee
2 parents add9317d 156a5910

!142 【支付宝通知回调BUG修复】 【微信支付 小程序支付功能添加】 【微信支付回调】

Merge pull request !142 from zwy/feature/1.6.2-pay
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
@@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; @@ -7,6 +7,7 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
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.wx.WXLitePayClient;
10 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXNativePayClient; 11 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXNativePayClient;
11 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; 12 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig;
12 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient; 13 import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient;
@@ -62,7 +63,7 @@ public class PayClientFactoryImpl implements PayClientFactory { @@ -62,7 +63,7 @@ public class PayClientFactoryImpl implements PayClientFactory {
62 // TODO @芋艿 WX_LITE WX_APP 如果不添加在 项目启动的时候去初始化会报错无法启动。所以我手动加了两个,具体需要你来配 63 // TODO @芋艿 WX_LITE WX_APP 如果不添加在 项目启动的时候去初始化会报错无法启动。所以我手动加了两个,具体需要你来配
63 switch (channelEnum) { 64 switch (channelEnum) {
64 case WX_PUB: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config); 65 case WX_PUB: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);
65 - case WX_LITE: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config); 66 + case WX_LITE: return (AbstractPayClient<Config>) new WXLitePayClient(channelId, (WXPayClientConfig) config); //微信小程序请求支付
66 case WX_APP: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config); 67 case WX_APP: return (AbstractPayClient<Config>) new WXPubPayClient(channelId, (WXPayClientConfig) config);
67 case WX_NATIVE: return (AbstractPayClient<Config>) new WXNativePayClient(channelId, (WXPayClientConfig) config); 68 case WX_NATIVE: return (AbstractPayClient<Config>) new WXNativePayClient(channelId, (WXPayClientConfig) config);
68 case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config); 69 case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(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
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
3 import cn.hutool.core.bean.BeanUtil; 3 import cn.hutool.core.bean.BeanUtil;
4 import cn.hutool.core.date.DateUtil; 4 import cn.hutool.core.date.DateUtil;
  5 +import cn.hutool.http.HttpUtil;
5 import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping; 6 import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
6 import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; 7 import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
7 import cn.iocoder.yudao.framework.pay.core.client.dto.*; 8 import cn.iocoder.yudao.framework.pay.core.client.dto.*;
@@ -130,11 +131,20 @@ public abstract class AbstractAlipayClient extends AbstractPayClient&lt;AlipayPayCl @@ -130,11 +131,20 @@ public abstract class AbstractAlipayClient extends AbstractPayClient&lt;AlipayPayCl
130 } 131 }
131 } 132 }
132 133
  134 +
  135 +
  136 + /**
  137 + * 支付宝统一回调参数 str 转 map
  138 + *
  139 + * @param s 支付宝支付通知回调参数
  140 + * @return map 支付宝集合
  141 + */
133 public static Map<String, String> strToMap(String s) { 142 public static Map<String, String> strToMap(String s) {
  143 + // TODO @zxy:这个可以使用 hutool 的 HttpUtil decodeParams 方法么?
134 Map<String, String> stringStringMap = new HashMap<>(); 144 Map<String, String> stringStringMap = new HashMap<>();
135 - //调整时间格式 145 + // 调整时间格式
136 String s3 = s.replaceAll("%3A", ":"); 146 String s3 = s.replaceAll("%3A", ":");
137 - //获取map 147 + // 获取 map
138 String s4 = s3.replace("+", " "); 148 String s4 = s3.replace("+", " ");
139 String[] split = s4.split("&"); 149 String[] split = s4.split("&");
140 for (String s1 : split) { 150 for (String s1 : split) {
@@ -143,4 +153,5 @@ public abstract class AbstractAlipayClient extends AbstractPayClient&lt;AlipayPayCl @@ -143,4 +153,5 @@ public abstract class AbstractAlipayClient extends AbstractPayClient&lt;AlipayPayCl
143 } 153 }
144 return stringStringMap; 154 return stringStringMap;
145 } 155 }
  156 +
146 } 157 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
  2 +
  3 +import cn.hutool.core.bean.BeanUtil;
  4 +import cn.hutool.core.date.DateUtil;
  5 +import cn.hutool.core.lang.Assert;
  6 +import cn.hutool.core.map.MapUtil;
  7 +import cn.hutool.core.util.StrUtil;
  8 +import cn.iocoder.yudao.framework.common.util.io.FileUtils;
  9 +import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
  10 +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
  11 +import cn.iocoder.yudao.framework.pay.core.client.dto.*;
  12 +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
  13 +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
  14 +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
  15 +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
  16 +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
  17 +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
  18 +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
  19 +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
  20 +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
  21 +import com.github.binarywang.wxpay.config.WxPayConfig;
  22 +import com.github.binarywang.wxpay.constant.WxPayConstants;
  23 +import com.github.binarywang.wxpay.exception.WxPayException;
  24 +import com.github.binarywang.wxpay.service.WxPayService;
  25 +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
  26 +import lombok.extern.slf4j.Slf4j;
  27 +
  28 +import java.util.Objects;
  29 +
  30 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
  31 +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS;
  32 +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS;
  33 +
  34 +
  35 +/**
  36 + * 微信小程序下支付
  37 + *
  38 + * @author zwy
  39 + */
  40 +@Slf4j
  41 +public class WXLitePayClient extends AbstractPayClient<WXPayClientConfig> {
  42 +
  43 + private WxPayService client;
  44 +
  45 + public WXLitePayClient(Long channelId, WXPayClientConfig config) {
  46 + super(channelId, PayChannelEnum.WX_LITE.getCode(), config, new WXCodeMapping());
  47 + }
  48 +
  49 + @Override
  50 + protected void doInit() {
  51 + WxPayConfig payConfig = new WxPayConfig();
  52 + BeanUtil.copyProperties(config, payConfig, "keyContent");
  53 + payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式
  54 +// if (StrUtil.isNotEmpty(config.getKeyContent())) {
  55 +// payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8));
  56 +// }
  57 + if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
  58 + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
  59 + payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
  60 + }
  61 + if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
  62 + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
  63 + payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
  64 + }
  65 + // 真实客户端
  66 + this.client = new WxPayServiceImpl();
  67 + client.setConfig(payConfig);
  68 + }
  69 +
  70 + @Override
  71 + public PayCommonResult<WxPayMpOrderResult> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
  72 + WxPayMpOrderResult response;
  73 + try {
  74 + switch (config.getApiVersion()) {
  75 + case WXPayClientConfig.API_VERSION_V2:
  76 + response = this.unifiedOrderV2(reqDTO);
  77 + break;
  78 + case WXPayClientConfig.API_VERSION_V3:
  79 + WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO);
  80 + // 将 V3 的结果,统一转换成 V2。返回的字段是一致的
  81 + response = new WxPayMpOrderResult();
  82 + BeanUtil.copyProperties(responseV3, response, true);
  83 + break;
  84 + default:
  85 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  86 + }
  87 + } catch (WxPayException e) {
  88 + log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e);
  89 + return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"),
  90 + ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()), null, codeMapping);
  91 + }
  92 + return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping);
  93 + }
  94 +
  95 + private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
  96 + // 构建 WxPayUnifiedOrderRequest 对象
  97 + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
  98 + .outTradeNo(reqDTO.getMerchantOrderId())
  99 + .body(reqDTO.getBody())
  100 + .totalFee(reqDTO.getAmount().intValue()) // 单位分
  101 + .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")) // v2的时间格式
  102 + .spbillCreateIp(reqDTO.getUserIp())
  103 + .openid(getOpenid(reqDTO))
  104 + .notifyUrl(reqDTO.getNotifyUrl())
  105 + .build();
  106 + // 执行请求
  107 + return client.createOrder(request);
  108 + }
  109 +
  110 + private WxPayUnifiedOrderV3Result.JsapiResult unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
  111 + // 构建 WxPayUnifiedOrderRequest 对象
  112 + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
  113 + request.setOutTradeNo(reqDTO.getMerchantOrderId());
  114 +
  115 + request.setDescription(reqDTO.getBody());
  116 + request.setAmount(new WxPayUnifiedOrderV3Request
  117 + .Amount()
  118 + .setTotal(reqDTO
  119 + .getAmount()
  120 + .intValue())); // 单位分
  121 + request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")); // v3的时间格式
  122 + request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
  123 + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
  124 + request.setNotifyUrl(reqDTO.getNotifyUrl());
  125 + // 执行请求
  126 + return client.createOrderV3(TradeTypeEnum.JSAPI, request);
  127 + }
  128 +
  129 + private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) {
  130 + String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid");
  131 + if (StrUtil.isEmpty(openid)) {
  132 + throw new IllegalArgumentException("支付请求的 openid 不能为空!");
  133 + }
  134 + return openid;
  135 + }
  136 +
  137 + /**
  138 + *
  139 + * 微信支付回调 分 v2 和v3 的处理方式
  140 + *
  141 + * @param data 通知结果
  142 + * @return 支付回调对象
  143 + * @throws WxPayException 微信异常类
  144 + */
  145 + @Override
  146 + public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException {
  147 + log.info("[parseOrderNotify][微信支付回调data数据:{}]", data.getBody());
  148 + // 微信支付 v2 回调结果处理
  149 + switch (config.getApiVersion()) {
  150 + case WXPayClientConfig.API_VERSION_V2:
  151 + return parseOrderNotifyV2(data);
  152 + case WXPayClientConfig.API_VERSION_V3:
  153 + return parseOrderNotifyV3(data);
  154 + default:
  155 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  156 + }
  157 + }
  158 +
  159 + private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyDataDTO data) throws WxPayException {
  160 + WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);
  161 + WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
  162 + // 转换结果
  163 + Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),
  164 + "支付结果非 SUCCESS");
  165 +
  166 + return PayOrderNotifyRespDTO
  167 + .builder()
  168 + .orderExtensionNo(result.getOutTradeNo())
  169 + .channelOrderNo(result.getTradeState())
  170 + .successTime(DateUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
  171 + .data(data.getBody())
  172 + .build();
  173 + }
  174 +
  175 + private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyDataDTO data) throws WxPayException {
  176 + WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
  177 + Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
  178 + // 转换结果
  179 + return PayOrderNotifyRespDTO
  180 + .builder()
  181 + .orderExtensionNo(notifyResult.getOutTradeNo())
  182 + .channelOrderNo(notifyResult.getTransactionId())
  183 + .channelUserId(notifyResult.getOpenid())
  184 + .successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
  185 + .data(data.getBody())
  186 + .build();
  187 +
  188 + }
  189 +
  190 + @Override
  191 + public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) {
  192 + //TODO 需要实现
  193 + throw new UnsupportedOperationException("需要实现");
  194 + }
  195 +
  196 +
  197 + @Override
  198 + protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
  199 + //TODO 需要实现
  200 + throw new UnsupportedOperationException();
  201 + }
  202 +
  203 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXNativePayClient.java
@@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.*; @@ -11,6 +11,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.*;
11 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; 11 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
12 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; 12 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
13 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; 13 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
  14 +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
14 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; 15 import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
15 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; 16 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
16 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; 17 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
@@ -28,9 +29,14 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString @@ -28,9 +29,14 @@ import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString
28 import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS; 29 import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS;
29 import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS; 30 import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS;
30 31
31 - 32 +/**
  33 + * 微信 App 支付
  34 + *
  35 + * @author zwy
  36 + */
32 @Slf4j 37 @Slf4j
33 public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> { 38 public class WXNativePayClient extends AbstractPayClient<WXPayClientConfig> {
  39 +
34 private WxPayService client; 40 private WxPayService client;
35 41
36 public WXNativePayClient(Long channelId, WXPayClientConfig config) { 42 public WXNativePayClient(Long channelId, WXPayClientConfig config) {
@@ -61,7 +67,7 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -61,7 +67,7 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
61 @Override 67 @Override
62 public PayCommonResult<String> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { 68 public PayCommonResult<String> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
63 // 这里原生的返回的是支付的 url 所以直接使用string接收 69 // 这里原生的返回的是支付的 url 所以直接使用string接收
64 - //"invokeResponse": "weixin://wxpay/bizpayurl?pr=EGYAem7zz" 70 + // "invokeResponse": "weixin://wxpay/bizpayurl?pr=EGYAem7zz"
65 String responseV3; 71 String responseV3;
66 try { 72 try {
67 switch (config.getApiVersion()) { 73 switch (config.getApiVersion()) {
@@ -84,7 +90,7 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -84,7 +90,7 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
84 90
85 private WxPayNativeOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { 91 private WxPayNativeOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
86 //前端 92 //前端
87 - String trade_type = reqDTO.getChannelExtras().get("trade_type"); 93 + String tradeType = reqDTO.getChannelExtras().get("trade_type");
88 // 构建 WxPayUnifiedOrderRequest 对象 94 // 构建 WxPayUnifiedOrderRequest 对象
89 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest 95 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest
90 .newBuilder() 96 .newBuilder()
@@ -94,7 +100,7 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -94,7 +100,7 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
94 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")) 100 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
95 .spbillCreateIp(reqDTO.getUserIp()) 101 .spbillCreateIp(reqDTO.getUserIp())
96 .notifyUrl(reqDTO.getNotifyUrl()) 102 .notifyUrl(reqDTO.getNotifyUrl())
97 - .productId(trade_type) 103 + .productId(tradeType)
98 .build(); 104 .build();
99 // 执行请求 105 // 执行请求
100 return client.createOrder(request); 106 return client.createOrder(request);
@@ -109,33 +115,72 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -109,33 +115,72 @@ public class WXNativePayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
109 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); 115 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
110 request.setNotifyUrl(reqDTO.getNotifyUrl()); 116 request.setNotifyUrl(reqDTO.getNotifyUrl());
111 // 执行请求 117 // 执行请求
112 -// log.info("支付字段request:{}",request.getTimeExpire());  
113 -  
114 return client.createOrderV3(TradeTypeEnum.NATIVE, request); 118 return client.createOrderV3(TradeTypeEnum.NATIVE, request);
115 } 119 }
116 120
117 - 121 + /**
  122 + *
  123 + * 微信支付回调 分v2 和v3 的处理方式
  124 + *
  125 + * @param data 通知结果
  126 + * @return 支付回调对象
  127 + * @throws WxPayException 微信异常类
  128 + */
118 @Override 129 @Override
119 public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException { 130 public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException {
  131 + log.info("微信支付回调data数据:{}", data.getBody());
  132 + // 微信支付 v2 回调结果处理
  133 + switch (config.getApiVersion()) {
  134 + case WXPayClientConfig.API_VERSION_V2:
  135 + return parseOrderNotifyV2(data);
  136 + case WXPayClientConfig.API_VERSION_V3:
  137 + return parseOrderNotifyV3(data);
  138 + default:
  139 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  140 + }
  141 + }
  142 +
  143 + private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyDataDTO data) throws WxPayException {
  144 + WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);
  145 + WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
  146 + // 转换结果
  147 + Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),
  148 + "支付结果非 SUCCESS");
  149 + return PayOrderNotifyRespDTO
  150 + .builder()
  151 + .orderExtensionNo(result.getOutTradeNo())
  152 + .channelOrderNo(result.getTradeState())
  153 + .successTime(DateUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
  154 + .data(data.getBody())
  155 + .build();
  156 + }
  157 +
  158 + private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyDataDTO data) throws WxPayException {
120 WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody()); 159 WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
121 Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS"); 160 Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
122 // 转换结果 161 // 转换结果
123 - return PayOrderNotifyRespDTO.builder().orderExtensionNo(notifyResult.getOutTradeNo())  
124 - .channelOrderNo(notifyResult.getTransactionId()).channelUserId(notifyResult.getOpenid())  
125 - .successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyy-MM-dd'T'HH:mm:ssXXX"))  
126 - .data(data.getBody()).build(); 162 + return PayOrderNotifyRespDTO
  163 + .builder()
  164 + .orderExtensionNo(notifyResult.getOutTradeNo())
  165 + .channelOrderNo(notifyResult.getTransactionId())
  166 + .channelUserId(notifyResult.getOpenid())
  167 + .successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
  168 + .data(data.getBody())
  169 + .build();
  170 +
127 } 171 }
128 172
129 @Override 173 @Override
130 public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) { 174 public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) {
131 - //TODO 需要实现 175 + // TODO 需要实现
132 throw new UnsupportedOperationException("需要实现"); 176 throw new UnsupportedOperationException("需要实现");
133 } 177 }
134 178
135 179
136 @Override 180 @Override
137 protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { 181 protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
138 - //TODO 需要实现 182 + // TODO 需要实现
139 throw new UnsupportedOperationException(); 183 throw new UnsupportedOperationException();
140 } 184 }
  185 +
141 } 186 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java
@@ -11,8 +11,6 @@ import java.io.FileInputStream; @@ -11,8 +11,6 @@ import java.io.FileInputStream;
11 import java.io.FileNotFoundException; 11 import java.io.FileNotFoundException;
12 import java.util.Set; 12 import java.util.Set;
13 13
14 -// TODO 芋艿:参数校验  
15 -  
16 /** 14 /**
17 * 微信支付的 PayClientConfig 实现类 15 * 微信支付的 PayClientConfig 实现类
18 * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性 16 * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性
@@ -22,7 +20,6 @@ import java.util.Set; @@ -22,7 +20,6 @@ import java.util.Set;
22 @Data 20 @Data
23 public class WXPayClientConfig implements PayClientConfig { 21 public class WXPayClientConfig implements PayClientConfig {
24 22
25 - // TODO 芋艿:V2 or V3 客户端  
26 /** 23 /**
27 * API 版本 - V2 24 * API 版本 - V2
28 * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1 25 * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java
@@ -12,6 +12,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.*; @@ -12,6 +12,7 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.*;
12 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; 12 import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
13 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; 13 import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum;
14 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; 14 import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;
  15 +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;
15 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; 16 import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
16 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; 17 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
17 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; 18 import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
@@ -95,7 +96,6 @@ public class WXPubPayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -95,7 +96,6 @@ public class WXPubPayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
95 // 构建 WxPayUnifiedOrderRequest 对象 96 // 构建 WxPayUnifiedOrderRequest 对象
96 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() 97 WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
97 .outTradeNo(reqDTO.getMerchantOrderId()) 98 .outTradeNo(reqDTO.getMerchantOrderId())
98 - // TODO 芋艿:貌似没 title?  
99 .body(reqDTO.getBody()) 99 .body(reqDTO.getBody())
100 .totalFee(reqDTO.getAmount().intValue()) // 单位分 100 .totalFee(reqDTO.getAmount().intValue()) // 单位分
101 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")) 101 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
@@ -111,7 +111,6 @@ public class WXPubPayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -111,7 +111,6 @@ public class WXPubPayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
111 // 构建 WxPayUnifiedOrderRequest 对象 111 // 构建 WxPayUnifiedOrderRequest 对象
112 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); 112 WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
113 request.setOutTradeNo(reqDTO.getMerchantOrderId()); 113 request.setOutTradeNo(reqDTO.getMerchantOrderId());
114 - // TODO 芋艿:貌似没 title?  
115 request.setDescription(reqDTO.getBody()); 114 request.setDescription(reqDTO.getBody());
116 request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分 115 request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分
117 request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")); 116 request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"));
@@ -130,27 +129,67 @@ public class WXPubPayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; { @@ -130,27 +129,67 @@ public class WXPubPayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
130 return openid; 129 return openid;
131 } 130 }
132 131
  132 + /**
  133 + *
  134 + * 微信支付回调 分v2 和v3 的处理方式
  135 + *
  136 + * @param data 通知结果
  137 + * @return 支付回调对象
  138 + * @throws WxPayException 微信异常类
  139 + */
133 @Override 140 @Override
134 public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException { 141 public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws WxPayException {
  142 + log.info("[parseOrderNotify][微信支付回调data数据: {}]", data.getBody());
  143 + // 微信支付 v2 回调结果处理
  144 + switch (config.getApiVersion()) {
  145 + case WXPayClientConfig.API_VERSION_V2:
  146 + return parseOrderNotifyV2(data);
  147 + case WXPayClientConfig.API_VERSION_V3:
  148 + return parseOrderNotifyV3(data);
  149 + default:
  150 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  151 + }
  152 + }
  153 +
  154 + private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyDataDTO data) throws WxPayException {
  155 + WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);
  156 + WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();
  157 + // 转换结果
  158 + Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),
  159 + "支付结果非 SUCCESS");
  160 + return PayOrderNotifyRespDTO
  161 + .builder()
  162 + .orderExtensionNo(result.getOutTradeNo())
  163 + .channelOrderNo(result.getTradeState())
  164 + .successTime(DateUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))
  165 + .data(data.getBody())
  166 + .build();
  167 + }
  168 +
  169 + private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyDataDTO data) throws WxPayException {
135 WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody()); 170 WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());
136 Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS"); 171 Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");
137 // 转换结果 172 // 转换结果
138 - return PayOrderNotifyRespDTO.builder().orderExtensionNo(notifyResult.getOutTradeNo())  
139 - .channelOrderNo(notifyResult.getTransactionId()).channelUserId(notifyResult.getOpenid()) 173 + return PayOrderNotifyRespDTO
  174 + .builder()
  175 + .orderExtensionNo(notifyResult.getOutTradeNo())
  176 + .channelOrderNo(notifyResult.getTransactionId())
  177 + .channelUserId(notifyResult.getOpenid())
140 .successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss")) 178 .successTime(DateUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))
141 - .data(data.getBody()).build(); 179 + .data(data.getBody())
  180 + .build();
  181 +
142 } 182 }
143 183
144 @Override 184 @Override
145 public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) { 185 public PayRefundNotifyDTO parseRefundNotify(PayNotifyDataDTO notifyData) {
146 - //TODO 需要实现 186 + // TODO 需要实现
147 throw new UnsupportedOperationException("需要实现"); 187 throw new UnsupportedOperationException("需要实现");
148 } 188 }
149 189
150 -  
151 @Override 190 @Override
152 protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { 191 protected PayCommonResult<PayRefundUnifiedRespDTO> doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
153 - //TODO 需要实现 192 + // TODO 需要实现
154 throw new UnsupportedOperationException(); 193 throw new UnsupportedOperationException();
155 } 194 }
156 195
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java
@@ -22,7 +22,6 @@ public enum PayChannelEnum { @@ -22,7 +22,6 @@ public enum PayChannelEnum {
22 WX_APP("wx_app", "微信 App 支付", WXPayClientConfig.class), 22 WX_APP("wx_app", "微信 App 支付", WXPayClientConfig.class),
23 WX_NATIVE("wx_native", "微信 native 支付", WXPayClientConfig.class), 23 WX_NATIVE("wx_native", "微信 native 支付", WXPayClientConfig.class),
24 24
25 -  
26 ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付", AlipayPayClientConfig.class), 25 ALIPAY_PC("alipay_pc", "支付宝 PC 网站支付", AlipayPayClientConfig.class),
27 ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付", AlipayPayClientConfig.class), 26 ALIPAY_WAP("alipay_wap", "支付宝 Wap 网站支付", AlipayPayClientConfig.class),
28 ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class), 27 ALIPAY_APP("alipay_app", "支付宝App 支付", AlipayPayClientConfig.class),