Commit e31b40eefa84abb75b153153e2312b55f150b87a
1 parent
4014137a
mall + pay:
1. 实现 WxBarPayClient 的支付功能
Showing
7 changed files
with
63 additions
and
14 deletions
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 | @@ -169,7 +169,7 @@ public abstract class AbstractWxPayClient extends AbstractPayClient<WxPayClientC | ||
| 169 | if (Objects.equals(e.getErrCode(), "PARAM_ERROR")) { | 169 | if (Objects.equals(e.getErrCode(), "PARAM_ERROR")) { |
| 170 | throw invalidParamException(e.getErrCodeDes()); | 170 | throw invalidParamException(e.getErrCodeDes()); |
| 171 | } | 171 | } |
| 172 | - throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getReturnMsg()); | 172 | + throw exception(PayFrameworkErrorCodeConstants.ORDER_UNIFIED_ERROR, e.getErrCodeDes()); |
| 173 | } | 173 | } |
| 174 | // 情况二:状态码结果为 FAIL | 174 | // 情况二:状态码结果为 FAIL |
| 175 | if (Objects.equals(e.getReturnCode(), "FAIL")) { | 175 | if (Objects.equals(e.getReturnCode(), "FAIL")) { |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXNativePayClient.java
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxBarPayClient.java
| 1 | package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; | 1 | package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; |
| 2 | 2 | ||
| 3 | import cn.hutool.core.map.MapUtil; | 3 | import cn.hutool.core.map.MapUtil; |
| 4 | +import cn.hutool.core.thread.ThreadUtil; | ||
| 4 | import cn.hutool.core.util.StrUtil; | 5 | import cn.hutool.core.util.StrUtil; |
| 6 | +import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils; | ||
| 5 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | 7 | import cn.iocoder.yudao.framework.common.util.json.JsonUtils; |
| 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; | 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; |
| 7 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; | 9 | 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 | @@ -9,15 +11,34 @@ import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReq | ||
| 9 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; | 11 | import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; |
| 10 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; | 12 | import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; |
| 11 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; | 13 | import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderDisplayModeEnum; |
| 14 | +import cn.iocoder.yudao.framework.pay.core.enums.order.PayOrderStatusRespEnum; | ||
| 12 | import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; | 15 | import com.github.binarywang.wxpay.bean.request.WxPayMicropayRequest; |
| 13 | import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; | 16 | import com.github.binarywang.wxpay.bean.result.WxPayMicropayResult; |
| 14 | import com.github.binarywang.wxpay.constant.WxPayConstants; | 17 | import com.github.binarywang.wxpay.constant.WxPayConstants; |
| 15 | import com.github.binarywang.wxpay.exception.WxPayException; | 18 | import com.github.binarywang.wxpay.exception.WxPayException; |
| 19 | +import lombok.extern.slf4j.Slf4j; | ||
| 20 | + | ||
| 21 | +import java.time.Duration; | ||
| 22 | +import java.time.LocalDateTime; | ||
| 23 | +import java.util.concurrent.TimeUnit; | ||
| 16 | 24 | ||
| 17 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; | 25 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.invalidParamException; |
| 18 | 26 | ||
| 27 | +/** | ||
| 28 | + * 微信支付【付款码支付】的 PayClient 实现类 | ||
| 29 | + * | ||
| 30 | + * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/api/micropay.php?chapter=9_10&index=1">付款码支付</a> | ||
| 31 | + * | ||
| 32 | + * @author 芋道源码 | ||
| 33 | + */ | ||
| 34 | +@Slf4j | ||
| 19 | public class WxBarPayClient extends AbstractWxPayClient { | 35 | public class WxBarPayClient extends AbstractWxPayClient { |
| 20 | 36 | ||
| 37 | + /** | ||
| 38 | + * 微信付款码的过期时间 | ||
| 39 | + */ | ||
| 40 | + private static final Duration AUTH_CODE_EXPIRE = Duration.ofMinutes(3); | ||
| 41 | + | ||
| 21 | public WxBarPayClient(Long channelId, WxPayClientConfig config) { | 42 | public WxBarPayClient(Long channelId, WxPayClientConfig config) { |
| 22 | super(channelId, PayChannelEnum.WX_BAR.getCode(), config); | 43 | super(channelId, PayChannelEnum.WX_BAR.getCode(), config); |
| 23 | } | 44 | } |
| @@ -29,23 +50,44 @@ public class WxBarPayClient extends AbstractWxPayClient { | @@ -29,23 +50,44 @@ public class WxBarPayClient extends AbstractWxPayClient { | ||
| 29 | 50 | ||
| 30 | @Override | 51 | @Override |
| 31 | protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { | 52 | protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { |
| 53 | + // 由于付款码需要不断轮询,所以需要在较短的时间完成支付 | ||
| 54 | + LocalDateTime expireTime = LocalDateTimeUtils.addTime(AUTH_CODE_EXPIRE); | ||
| 55 | + if (expireTime.isAfter(reqDTO.getExpireTime())) { | ||
| 56 | + expireTime = reqDTO.getExpireTime(); | ||
| 57 | + } | ||
| 32 | // 构建 WxPayMicropayRequest 对象 | 58 | // 构建 WxPayMicropayRequest 对象 |
| 33 | WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder() | 59 | WxPayMicropayRequest request = WxPayMicropayRequest.newBuilder() |
| 34 | .outTradeNo(reqDTO.getMerchantOrderId()) | 60 | .outTradeNo(reqDTO.getMerchantOrderId()) |
| 35 | .body(reqDTO.getSubject()) | 61 | .body(reqDTO.getSubject()) |
| 36 | .detail(reqDTO.getBody()) | 62 | .detail(reqDTO.getBody()) |
| 37 | .totalFee(reqDTO.getAmount()) // 单位分 | 63 | .totalFee(reqDTO.getAmount()) // 单位分 |
| 38 | - .timeExpire(formatDateV2(reqDTO.getExpireTime())) | 64 | + .timeExpire(formatDateV2(expireTime)) |
| 39 | .spbillCreateIp(reqDTO.getUserIp()) | 65 | .spbillCreateIp(reqDTO.getUserIp()) |
| 40 | .authCode(getAuthCode(reqDTO)) | 66 | .authCode(getAuthCode(reqDTO)) |
| 41 | .build(); | 67 | .build(); |
| 42 | - // 执行请求 | ||
| 43 | - WxPayMicropayResult response = client.micropay(request); | ||
| 44 | - | ||
| 45 | - // 转换结果 | ||
| 46 | - // TODO 芋艿:这里后面要看看 | ||
| 47 | - return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.CUSTOM.getMode(), | ||
| 48 | - JsonUtils.toJsonString(response)); | 68 | + // 执行请求,重试直到失败(过期),或者成功 |
| 69 | + for (int i = 1; i < Byte.MAX_VALUE; i++) { | ||
| 70 | + try { | ||
| 71 | + WxPayMicropayResult response = client.micropay(request); | ||
| 72 | + // 支付成功(例如说,用户输入了密码) | ||
| 73 | + return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.BAR_CODE.getMode(), | ||
| 74 | + JsonUtils.toJsonString(response), | ||
| 75 | + PayOrderStatusRespEnum.SUCCESS.getStatus()); | ||
| 76 | + } catch (WxPayException ex) { | ||
| 77 | + // 如果不满足这 3 种任一的,则直接抛出 WxPayException 异常,不仅需处理 | ||
| 78 | + // 1. SYSTEMERROR:接口返回错误:请立即调用被扫订单结果查询API,查询当前订单状态,并根据订单的状态决定下一步的操作。 | ||
| 79 | + // 2. USERPAYING:用户支付中,需要输入密码:等待 5 秒,然后调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。 | ||
| 80 | + // 3. BANKERROR:银行系统异常:请立即调用被扫订单结果查询 API,查询当前订单的不同状态,决定下一步的操作。 | ||
| 81 | + if (!StrUtil.equalsAny(ex.getErrCode(), "SYSTEMERROR", "USERPAYING", "BANKERROR")) { | ||
| 82 | + throw ex; | ||
| 83 | + } | ||
| 84 | + // 等待 5 秒,继续下一轮重新发起支付 | ||
| 85 | + log.info("[doUnifiedOrderV2][发起微信 Bar 支付第({})失败,等待下一轮重试,请求({}),响应({})]", i, | ||
| 86 | + JsonUtils.toJsonString(request), ex.getMessage()); | ||
| 87 | + ThreadUtil.sleep(5, TimeUnit.SECONDS); | ||
| 88 | + } | ||
| 89 | + } | ||
| 90 | + throw new IllegalStateException("微信 Bar 支付,重试多次失败"); | ||
| 49 | } | 91 | } |
| 50 | 92 | ||
| 51 | @Override | 93 | @Override |
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; | @@ -4,10 +4,12 @@ import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; | ||
| 4 | import lombok.extern.slf4j.Slf4j; | 4 | import lombok.extern.slf4j.Slf4j; |
| 5 | 5 | ||
| 6 | /** | 6 | /** |
| 7 | - * 微信支付(小程序)的 PayClient 实现类 | 7 | + * 微信支付【小程序】的 PayClient 实现类 |
| 8 | * | 8 | * |
| 9 | * 由于公众号和小程序的微信支付逻辑一致,所以直接进行继承 | 9 | * 由于公众号和小程序的微信支付逻辑一致,所以直接进行继承 |
| 10 | * | 10 | * |
| 11 | + * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_5_1.shtml">JSAPI 下单</> | ||
| 12 | + * | ||
| 11 | * @author zwy | 13 | * @author zwy |
| 12 | */ | 14 | */ |
| 13 | @Slf4j | 15 | @Slf4j |
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 { | @@ -23,20 +23,21 @@ public class WxPayClientConfig implements PayClientConfig { | ||
| 23 | /** | 23 | /** |
| 24 | * API 版本 - V2 | 24 | * API 版本 - V2 |
| 25 | * | 25 | * |
| 26 | - * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1 | 26 | + * <a href="https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1">V2 协议说明</a> |
| 27 | */ | 27 | */ |
| 28 | public static final String API_VERSION_V2 = "v2"; | 28 | public static final String API_VERSION_V2 = "v2"; |
| 29 | /** | 29 | /** |
| 30 | * API 版本 - V3 | 30 | * API 版本 - V3 |
| 31 | * | 31 | * |
| 32 | - * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml | 32 | + * <a href="https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml">V3 协议说明</a> |
| 33 | */ | 33 | */ |
| 34 | public static final String API_VERSION_V3 = "v3"; | 34 | public static final String API_VERSION_V3 = "v3"; |
| 35 | 35 | ||
| 36 | /** | 36 | /** |
| 37 | * 公众号或者小程序的 appid | 37 | * 公众号或者小程序的 appid |
| 38 | + * | ||
| 39 | + * 只有公众号或小程序需要该字段 | ||
| 38 | */ | 40 | */ |
| 39 | - @NotBlank(message = "APPID 不能为空", groups = {V2.class, V3.class}) | ||
| 40 | private String appId; | 41 | private String appId; |
| 41 | /** | 42 | /** |
| 42 | * 商户号 | 43 | * 商户号 |
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 | @@ -23,6 +23,8 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU | ||
| 23 | /** | 23 | /** |
| 24 | * 微信支付(公众号)的 PayClient 实现类 | 24 | * 微信支付(公众号)的 PayClient 实现类 |
| 25 | * | 25 | * |
| 26 | + * 文档:<a href="https://pay.weixin.qq.com/wiki/doc/apiv3/apis/chapter3_1_1.shtml">JSAPI 下单</> | ||
| 27 | + * | ||
| 26 | * @author 芋道源码 | 28 | * @author 芋道源码 |
| 27 | */ | 29 | */ |
| 28 | @Slf4j | 30 | @Slf4j |
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}} | @@ -19,7 +19,7 @@ tenant-id: {{appTenentId}} | ||
| 19 | "id": 202, | 19 | "id": 202, |
| 20 | "channelCode": "wx_bar", | 20 | "channelCode": "wx_bar", |
| 21 | "channelExtras": { | 21 | "channelExtras": { |
| 22 | - "authCode": "132527737910208222" | 22 | + "authCode": "132990241553789274" |
| 23 | } | 23 | } |
| 24 | } | 24 | } |
| 25 | 25 |