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 62 case WX_LITE: return (AbstractPayClient<Config>) new WxLitePayClient(channelId, (WxPayClientConfig) config);
63 63 case WX_APP: return (AbstractPayClient<Config>) new WxPubPayClient(channelId, (WxPayClientConfig) config);
64 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 67 case ALIPAY_WAP: return (AbstractPayClient<Config>) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config);
68 68 case ALIPAY_QR: return (AbstractPayClient<Config>) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config);
69 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 16 IFRAME("iframe"), // IFrame 内嵌链接的方式【目前暂时用不到】
17 17 FORM("form"), // HTML 表单提交
18 18 QR_CODE("qr_code"), // 二维码的文字内容
19   - QR_CODE_URL("qr_code_url"), // 二维码的图片链接【目前暂时用不到】
  19 + QR_CODE_URL("qr_code_url"), // 二维码的图片链接
20 20 BAR_CODE("bar_code"), // 条形码
21 21 APP("app"), // 应用【目前暂时用不到】
22 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 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 +}
... ...