Commit b06a21f9af99968bd815c6a60dc8974098e20096

Authored by YunaiV
1 parent e31b40ee

mall + pay:

1. 实现 WxNativePayClient 的支付功能
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java
@@ -62,8 +62,8 @@ public class PayClientFactoryImpl implements PayClientFactory { @@ -62,8 +62,8 @@ public class PayClientFactoryImpl implements PayClientFactory {
62 case WX_LITE: return (AbstractPayClient<Config>) new WxLitePayClient(channelId, (WxPayClientConfig) config); 62 case WX_LITE: return (AbstractPayClient<Config>) new WxLitePayClient(channelId, (WxPayClientConfig) config);
63 case WX_APP: return (AbstractPayClient<Config>) new WxPubPayClient(channelId, (WxPayClientConfig) config); 63 case WX_APP: return (AbstractPayClient<Config>) new WxPubPayClient(channelId, (WxPayClientConfig) config);
64 case WX_BAR: return (AbstractPayClient<Config>) new WxBarPayClient(channelId, (WxPayClientConfig) config); 64 case WX_BAR: return (AbstractPayClient<Config>) new WxBarPayClient(channelId, (WxPayClientConfig) config);
  65 + case WX_NATIVE: return (AbstractPayClient<Config>) new WxNativePayClient(channelId, (WxPayClientConfig) config);
65 // 支付宝支付 66 // 支付宝支付
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/weixin/WXNativePayClient.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.DateUtil;  
5 -import cn.hutool.core.date.LocalDateTimeUtil;  
6 -import cn.hutool.core.lang.Assert;  
7 -import cn.hutool.core.util.StrUtil;  
8 -import cn.iocoder.yudao.framework.common.util.io.FileUtils;  
9 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO;  
10 -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO;  
11 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;  
12 -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;  
13 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;  
14 -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;  
15 -import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;  
16 -import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;  
17 -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult;  
18 -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result;  
19 -import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;  
20 -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;  
21 -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;  
22 -import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;  
23 -import com.github.binarywang.wxpay.config.WxPayConfig;  
24 -import com.github.binarywang.wxpay.constant.WxPayConstants;  
25 -import com.github.binarywang.wxpay.exception.WxPayException;  
26 -import com.github.binarywang.wxpay.service.WxPayService;  
27 -import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;  
28 -import lombok.extern.slf4j.Slf4j;  
29 -  
30 -import java.time.ZoneId;  
31 -import java.util.Date;  
32 -import java.util.Objects;  
33 -  
34 -/**  
35 - * 微信 App 支付  
36 - *  
37 - * TODO 芋艿:待测试,可能实现也不太对;  
38 - *  
39 - * @author zwy  
40 - */  
41 -@Slf4j  
42 -public class WXNativePayClient extends AbstractPayClient<WxPayClientConfig> {  
43 -  
44 - private WxPayService client;  
45 -  
46 - public WXNativePayClient(Long channelId, WxPayClientConfig config) {  
47 - super(channelId, PayChannelEnum.WX_NATIVE.getCode(), config);  
48 - }  
49 -  
50 - @Override  
51 - protected void doInit() {  
52 - WxPayConfig payConfig = new WxPayConfig();  
53 - BeanUtil.copyProperties(config, payConfig, "keyContent");  
54 - payConfig.setTradeType(WxPayConstants.TradeType.NATIVE); // 设置使用 native 支付方式  
55 -// if (StrUtil.isNotEmpty(config.getKeyContent())) {  
56 -// payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8));  
57 -// }  
58 - if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {  
59 - // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决  
60 - payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());  
61 - }  
62 - if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {  
63 - // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决  
64 - payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());  
65 - }  
66 - // 真实客户端  
67 - this.client = new WxPayServiceImpl();  
68 - client.setConfig(payConfig);  
69 - }  
70 -  
71 - @Override  
72 - public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) {  
73 - throw new UnsupportedOperationException();  
74 -// // 这里原生的返回的是支付的 url 所以直接使用string接收  
75 -// // "invokeResponse": "weixin://wxpay/bizpayurl?pr=EGYAem7zz"  
76 -// String responseV3;  
77 -// try {  
78 -// switch (config.getApiVersion()) {  
79 -// case WXPayClientConfig.API_VERSION_V2:  
80 -// responseV3 = unifiedOrderV2(reqDTO).getCodeUrl();  
81 -// break;  
82 -// case WXPayClientConfig.API_VERSION_V3:  
83 -// responseV3 = this.unifiedOrderV3(reqDTO);  
84 -// break;  
85 -// default:  
86 -// throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));  
87 -// }  
88 -// } catch (WxPayException e) {  
89 -// log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e);  
90 -// return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"),  
91 -// ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()), null, codeMapping);  
92 -// }  
93 -// return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, responseV3, codeMapping);  
94 - }  
95 -  
96 - private WxPayNativeOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {  
97 - //前端  
98 - String tradeType = reqDTO.getChannelExtras().get("trade_type");  
99 - // 构建 WxPayUnifiedOrderRequest 对象  
100 - WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest  
101 - .newBuilder()  
102 - .outTradeNo(reqDTO.getMerchantOrderId())  
103 - .body(reqDTO.getBody())  
104 - .totalFee(reqDTO.getAmount().intValue()) // 单位分  
105 - .timeExpire(DateUtil.format(Date.from(reqDTO.getExpireTime().atZone(ZoneId.systemDefault()).toInstant()), "yyyy-MM-dd'T'HH:mm:ssXXX"))  
106 - .spbillCreateIp(reqDTO.getUserIp())  
107 - .notifyUrl(reqDTO.getNotifyUrl())  
108 - .productId(tradeType)  
109 - .build();  
110 - // 执行请求  
111 - return client.createOrder(request);  
112 - }  
113 -  
114 - private String unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {  
115 - // 构建 WxPayUnifiedOrderRequest 对象  
116 - WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();  
117 - request.setOutTradeNo(reqDTO.getMerchantOrderId());  
118 - request.setDescription(reqDTO.getBody());  
119 - request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分  
120 - request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));  
121 - request.setNotifyUrl(reqDTO.getNotifyUrl());  
122 - // 执行请求  
123 - return client.createOrderV3(TradeTypeEnum.NATIVE, request);  
124 - }  
125 -  
126 - /**  
127 - *  
128 - * 微信支付回调 分v2 和v3 的处理方式  
129 - *  
130 - * @param data 通知结果  
131 - * @return 支付回调对象  
132 - * @throws WxPayException 微信异常类  
133 - */  
134 -// @Override  
135 - public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException {  
136 - log.info("微信支付回调data数据:{}", data.getBody());  
137 - // 微信支付 v2 回调结果处理  
138 - switch (config.getApiVersion()) {  
139 - case WxPayClientConfig.API_VERSION_V2:  
140 - return parseOrderNotifyV2(data);  
141 - case WxPayClientConfig.API_VERSION_V3:  
142 - return parseOrderNotifyV3(data);  
143 - default:  
144 - throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));  
145 - }  
146 - }  
147 -  
148 - private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException {  
149 - WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null);  
150 - WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult();  
151 - // 转换结果  
152 - Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"),  
153 - "支付结果非 SUCCESS");  
154 - return PayOrderNotifyRespDTO  
155 - .builder()  
156 - .orderExtensionNo(result.getOutTradeNo())  
157 - .channelOrderNo(result.getTradeState())  
158 - .successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX"))  
159 - .build();  
160 - }  
161 -  
162 - private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException {  
163 - WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody());  
164 - Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS");  
165 - // 转换结果  
166 - return PayOrderNotifyRespDTO  
167 - .builder()  
168 - .orderExtensionNo(notifyResult.getOutTradeNo())  
169 - .channelOrderNo(notifyResult.getTransactionId())  
170 - .channelUserId(notifyResult.getOpenid())  
171 - .successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss"))  
172 - .build();  
173 -  
174 - }  
175 -  
176 - @Override  
177 - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) {  
178 - // TODO 需要实现  
179 - throw new UnsupportedOperationException();  
180 - }  
181 -  
182 -}  
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
  2 +
  3 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
  4 +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
  5 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO;
  6 +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
  7 +import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
  8 +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
  9 +import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult;
  10 +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
  11 +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
  12 +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
  13 +import com.github.binarywang.wxpay.constant.WxPayConstants;
  14 +import com.github.binarywang.wxpay.exception.WxPayException;
  15 +import lombok.extern.slf4j.Slf4j;
  16 +
  17 +/**
  18 + * 微信支付【Native 二维码】的 PayClient 实现类
  19 + *
  20 + * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_4_1.shtml">Native 下单</a>
  21 + *
  22 + * @author zwy
  23 + */
  24 +@Slf4j
  25 +public class WxNativePayClient extends AbstractWxPayClient {
  26 +
  27 + public WxNativePayClient(Long channelId, WxPayClientConfig config) {
  28 + super(channelId, PayChannelEnum.WX_NATIVE.getCode(), config);
  29 + }
  30 +
  31 + @Override
  32 + protected void doInit() {
  33 + super.doInit(WxPayConstants.TradeType.NATIVE);
  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.getSubject())
  42 + .detail(reqDTO.getBody())
  43 + .totalFee(reqDTO.getAmount()) // 单位分
  44 + .productId(reqDTO.getMerchantOrderId())
  45 + .timeExpire(formatDateV2(reqDTO.getExpireTime()))
  46 + .spbillCreateIp(reqDTO.getUserIp())
  47 + .notifyUrl(reqDTO.getNotifyUrl())
  48 + .build();
  49 + // 执行请求
  50 + WxPayNativeOrderResult response = client.createOrder(request);
  51 +
  52 + // 转换结果
  53 + return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE_URL.getMode(),
  54 + response.getCodeUrl());
  55 + }
  56 +
  57 + @Override
  58 + protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
  59 + // 构建 WxPayUnifiedOrderRequest 对象
  60 + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
  61 + request.setOutTradeNo(reqDTO.getMerchantOrderId());
  62 + request.setDescription(reqDTO.getBody());
  63 + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
  64 + request.setTimeExpire(formatDateV3(reqDTO.getExpireTime()));
  65 + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
  66 + request.setNotifyUrl(reqDTO.getNotifyUrl());
  67 + // 执行请求
  68 + WxPayNativeOrderResult response = client.createOrderV3(TradeTypeEnum.NATIVE, request);
  69 +
  70 + // 转换结果
  71 + return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE_URL.getMode(),
  72 + response.getCodeUrl());
  73 + }
  74 +
  75 + @Override
  76 + protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable {
  77 + return null;
  78 + }
  79 +
  80 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderDisplayModeEnum.java
@@ -16,7 +16,7 @@ public enum PayOrderDisplayModeEnum { @@ -16,7 +16,7 @@ public enum PayOrderDisplayModeEnum {
16 IFRAME("iframe"), // IFrame 内嵌链接的方式【目前暂时用不到】 16 IFRAME("iframe"), // IFrame 内嵌链接的方式【目前暂时用不到】
17 FORM("form"), // HTML 表单提交 17 FORM("form"), // HTML 表单提交
18 QR_CODE("qr_code"), // 二维码的文字内容 18 QR_CODE("qr_code"), // 二维码的文字内容
19 - QR_CODE_URL("qr_code_url"), // 二维码的图片链接【目前暂时用不到】 19 + QR_CODE_URL("qr_code_url"), // 二维码的图片链接
20 BAR_CODE("bar_code"), // 条形码 20 BAR_CODE("bar_code"), // 条形码
21 APP("app"), // 应用【目前暂时用不到】 21 APP("app"), // 应用【目前暂时用不到】
22 CUSTOM("custom"), // 自定义:每种支付方式,做个性化处理;例如说,微信公众号支付时,调用 JSAPI 接口 22 CUSTOM("custom"), // 自定义:每种支付方式,做个性化处理;例如说,微信公众号支付时,调用 JSAPI 接口
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http
@@ -50,3 +50,14 @@ tenant-id: {{appTenentId}} @@ -50,3 +50,14 @@ tenant-id: {{appTenentId}}
50 "openid": "oLefc4g5GjKWHJjLjMSXB3wX0fD0" 50 "openid": "oLefc4g5GjKWHJjLjMSXB3wX0fD0"
51 } 51 }
52 } 52 }
  53 +
  54 +### /pay/create 提交支付订单【wx_native】
  55 +POST {{appApi}}/pay/order/submit
  56 +Content-Type: application/json
  57 +Authorization: Bearer {{appToken}}
  58 +tenant-id: {{appTenentId}}
  59 +
  60 +{
  61 + "id": 202,
  62 + "channelCode": "wx_native"
  63 +}