浏览代码

mall + pay:
1. 实现 WxBarPayClient 的支付功能

YunaiV 1 年之前
父节点
当前提交
e31b40eefa

+ 1 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java

@@ -169,7 +169,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC
             if (Objects.equals(e.getErrCode(), "PARAM_ERROR")) {
                 throw invalidParamException(e.getErrCodeDes());
             }
-            throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getReturnMsg());
+            throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getErrCodeDes());
         }
         // 情况二:状态码结果为 FAIL
         if (Objects.equals(e.getReturnCode(), "FAIL")) {

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXNativePayClient.java

@@ -34,6 +34,8 @@ import java.util.Objects;
 /**
  * 微信 App 支付
  *
+ * TODO 芋艿:待测试,可能实现也不太对;
+ *
  * @author zwy
  */
 @Slf4j

+ 50 - 8
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java

@@ -1,7 +1,9 @@
 package cn.iocoder.yudao.framework.pay.core.client.impl.weixin;
 
 import cn.hutool.core.map.MapUtil;
+import cn.hutool.core.thread.ThreadUtil;
 import cn.hutool.core.util.StrUtil;
+import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
 import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
@@ -9,15 +11,34 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReq
 import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO;
 import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum;
+import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum;
 import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest;
 import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult;
 import com.github.binarywang.wxpay.constant.WxPayConstants;
 import com.github.binarywang.wxpay.exception.WxPayException;
+import lombok.extern.slf4j.Slf4j;
+
+import java.time.Duration;
+import java.time.LocalDateTime;
+import java.util.concurrent.TimeUnit;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
 
+/**
+ * 微信支付【付款码支付】的 PayClient 实现类
+ *
+ * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1">付款码支付</a>
+ *
+ * @author 芋道源码
+ */
+@Slf4j
 public class WxBarPayClient extends AbstractWxPayClient {
 
+    /**
+     * 微信付款码的过期时间
+     */
+    private static final Duration AUTH_CODE_EXPIRE = Duration.ofMinutes(3);
+
     public WxBarPayClient(Long channelId, WxPayClientConfig config) {
         super(channelId, PayChannelEnum.WX_BAR.getCode(), config);
     }
@@ -29,23 +50,44 @@ public class WxBarPayClient extends AbstractWxPayClient {
 
     @Override
     protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
+        // 由于付款码需要不断轮询,所以需要在较短的时间完成支付
+        LocalDateTime expireTime = LocalDateTimeUtils.addTime(AUTH_CODE_EXPIRE);
+        if (expireTime.isAfter(reqDTO.getExpireTime())) {
+            expireTime = reqDTO.getExpireTime();
+        }
         // 构建 WxPayMicropayRequest 对象
         WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder()
                 .outTradeNo(reqDTO.getMerchantOrderId())
                 .body(reqDTO.getSubject())
                 .detail(reqDTO.getBody())
                 .totalFee(reqDTO.getAmount()) // 单位分
-                .timeExpire(formatDateV2(reqDTO.getExpireTime()))
+                .timeExpire(formatDateV2(expireTime))
                 .spbillCreateIp(reqDTO.getUserIp())
                 .authCode(getAuthCode(reqDTO))
                 .build();
-        // 执行请求
-        WxPayMicropayResult response = client.micropay(request);
-
-        // 转换结果
-        // TODO 芋艿:这里后面要看看
-        return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.CUSTOM.getMode(),
-                JsonUtils.toJsonString(response));
+        // 执行请求,重试直到失败(过期),或者成功
+        for (int i = 1; i < Byte.MAX_VALUE; i++) {
+            try {
+                WxPayMicropayResult response = client.micropay(request);
+                // 支付成功(例如说,用户输入了密码)
+                return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(),
+                        JsonUtils.toJsonString(response),
+                        PayOrderStatusRespEnum.SUCCESS.getStatus());
+            } catch (WxPayException ex) {
+                // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理
+                // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。
+                // 2. USERPAYING:用户支付中,需要输入密码:等待 5 秒,然后调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。
+                // 3. BANKERROR:银行系统异常:请立即调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。
+                if (!StrUtil.equalsAny(ex.getErrCode(), "SYSTEMERROR", "USERPAYING", "BANKERROR")) {
+                    throw ex;
+                }
+                // 等待 5 秒,继续下一轮重新发起支付
+                log.info("[doUnifiedOrderV2][发起微信 Bar 支付第({})失败,等待下一轮重试,请求({}),响应({})]", i,
+                        JsonUtils.toJsonString(request), ex.getMessage());
+                ThreadUtil.sleep(5, TimeUnit.SECONDS);
+            }
+        }
+        throw new IllegalStateException("微信 Bar 支付,重试多次失败");
     }
 
     @Override

+ 3 - 1
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxLitePayClient.java

@@ -4,10 +4,12 @@ import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum;
 import lombok.extern.slf4j.Slf4j;
 
 /**
- * 微信支付(小程序)的 PayClient 实现类
+ * 微信支付【小程序】的 PayClient 实现类
  *
  * 由于公众号和小程序的微信支付逻辑一致,所以直接进行继承
  *
+ * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml">JSAPI 下单</>
+ *
  * @author zwy
  */
 @Slf4j

+ 4 - 3
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPayClientConfig.java

@@ -23,20 +23,21 @@ public class WxPayClientConfig implements PayClientConfig {
     /**
      * API 版本 - V2
      *
-     * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1
+     * <a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1">V2 协议说明</a>
      */
     public static final String API_VERSION_V2 = "v2";
     /**
      * API 版本 - V3
      *
-     * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml
+     * <a href="https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml">V3 协议说明</a>
      */
     public static final String API_VERSION_V3 = "v3";
 
     /**
      * 公众号或者小程序的 appid
+     *
+     * 只有公众号或小程序需要该字段
      */
-    @NotBlank(message = "APPID 不能为空", groups = {V2.class, V3.class})
     private String appId;
     /**
      * 商户号

+ 2 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxPubPayClient.java

@@ -23,6 +23,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 /**
  * 微信支付(公众号)的 PayClient 实现类
  *
+ * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml">JSAPI 下单</>
+ *
  * @author 芋道源码
  */
 @Slf4j

+ 1 - 1
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http

@@ -19,7 +19,7 @@ tenant-id: {{appTenentId}}
   "id": 202,
   "channelCode": "wx_bar",
   "channelExtras": {
-    "authCode": "132527737910208222"
+    "authCode": "132990241553789274"
   }
 }