From b06a21f9af99968bd815c6a60dc8974098e20096 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Sun, 9 Jul 2023 00:36:40 +0800 Subject: [PATCH] =?UTF-8?q?mall=20+=20pay=EF=BC=9A=201.=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=20WxNativePayClient=20=E7=9A=84=E6=94=AF=E4=BB=98?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../client/impl/PayClientFactoryImpl.java | 2 +- .../client/impl/weixin/WXNativePayClient.java | 182 ------------------ .../client/impl/weixin/WxNativePayClient.java | 80 ++++++++ .../enums/order/PayOrderDisplayModeEnum.java | 2 +- .../app/order/AppPayOrderController.http | 11 ++ 5 files changed, 93 insertions(+), 184 deletions(-) delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXNativePayClient.java create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java index 9bf41f9d7..c21078b3f 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java +++ b/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 { case WX_LITE: return (AbstractPayClient) new WxLitePayClient(channelId, (WxPayClientConfig) config); case WX_APP: return (AbstractPayClient) new WxPubPayClient(channelId, (WxPayClientConfig) config); case WX_BAR: return (AbstractPayClient) new WxBarPayClient(channelId, (WxPayClientConfig) config); + case WX_NATIVE: return (AbstractPayClient) new WxNativePayClient(channelId, (WxPayClientConfig) config); // 支付宝支付 - case WX_NATIVE: return (AbstractPayClient) new WXNativePayClient(channelId, (WxPayClientConfig) config); case ALIPAY_WAP: return (AbstractPayClient) new AlipayWapPayClient(channelId, (AlipayPayClientConfig) config); case ALIPAY_QR: return (AbstractPayClient) new AlipayQrPayClient(channelId, (AlipayPayClientConfig) config); case ALIPAY_APP: return (AbstractPayClient) new AlipayAppPayClient(channelId, (AlipayPayClientConfig) config); diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXNativePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXNativePayClient.java deleted file mode 100644 index 11696d3e5..000000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXNativePayClient.java +++ /dev/null @@ -1,182 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.date.DateUtil; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.util.StrUtil; -import cn.iocoder.yudao.framework.common.util.io.FileUtils; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; -import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedRespDTO; -import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; -import cn.iocoder.yudao.framework.pay.core.enums.channel.PayChannelEnum; -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; -import com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; -import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; -import com.github.binarywang.wxpay.config.WxPayConfig; -import com.github.binarywang.wxpay.constant.WxPayConstants; -import com.github.binarywang.wxpay.exception.WxPayException; -import com.github.binarywang.wxpay.service.WxPayService; -import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl; -import lombok.extern.slf4j.Slf4j; - -import java.time.ZoneId; -import java.util.Date; -import java.util.Objects; - -/** - * 微信 App 支付 - * - * TODO 芋艿:待测试,可能实现也不太对; - * - * @author zwy - */ -@Slf4j -public class WXNativePayClient extends AbstractPayClient { - - private WxPayService client; - - public WXNativePayClient(Long channelId, WxPayClientConfig config) { - super(channelId, PayChannelEnum.WX_NATIVE.getCode(), config); - } - - @Override - protected void doInit() { - WxPayConfig payConfig = new WxPayConfig(); - BeanUtil.copyProperties(config, payConfig, "keyContent"); - payConfig.setTradeType(WxPayConstants.TradeType.NATIVE); // 设置使用 native 支付方式 -// if (StrUtil.isNotEmpty(config.getKeyContent())) { -// payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8)); -// } - if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) { - // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 - payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath()); - } - if (StrUtil.isNotEmpty(config.getPrivateCertContent())) { - // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决 - payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath()); - } - // 真实客户端 - this.client = new WxPayServiceImpl(); - client.setConfig(payConfig); - } - - @Override - public PayOrderUnifiedRespDTO doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) { - throw new UnsupportedOperationException(); -// // 这里原生的返回的是支付的 url 所以直接使用string接收 -// // "invokeResponse": "weixin://wxpay/bizpayurl?pr=EGYAem7zz" -// String responseV3; -// try { -// switch (config.getApiVersion()) { -// case WXPayClientConfig.API_VERSION_V2: -// responseV3 = unifiedOrderV2(reqDTO).getCodeUrl(); -// break; -// case WXPayClientConfig.API_VERSION_V3: -// responseV3 = this.unifiedOrderV3(reqDTO); -// break; -// default: -// throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); -// } -// } catch (WxPayException e) { -// log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e); -// return PayCommonResult.build(ObjectUtils.defaultIfNull(e.getErrCode(), e.getReturnCode(), "CustomErrorCode"), -// ObjectUtils.defaultIfNull(e.getErrCodeDes(), e.getCustomErrorMsg()), null, codeMapping); -// } -// return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, responseV3, codeMapping); - } - - private WxPayNativeOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { - //前端 - String tradeType = reqDTO.getChannelExtras().get("trade_type"); - // 构建 WxPayUnifiedOrderRequest 对象 - WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest - .newBuilder() - .outTradeNo(reqDTO.getMerchantOrderId()) - .body(reqDTO.getBody()) - .totalFee(reqDTO.getAmount().intValue()) // 单位分 - .timeExpire(DateUtil.format(Date.from(reqDTO.getExpireTime().atZone(ZoneId.systemDefault()).toInstant()), "yyyy-MM-dd'T'HH:mm:ssXXX")) - .spbillCreateIp(reqDTO.getUserIp()) - .notifyUrl(reqDTO.getNotifyUrl()) - .productId(tradeType) - .build(); - // 执行请求 - return client.createOrder(request); - } - - private String unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { - // 构建 WxPayUnifiedOrderRequest 对象 - WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); - request.setOutTradeNo(reqDTO.getMerchantOrderId()); - request.setDescription(reqDTO.getBody()); - request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分 - request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); - request.setNotifyUrl(reqDTO.getNotifyUrl()); - // 执行请求 - return client.createOrderV3(TradeTypeEnum.NATIVE, request); - } - - /** - * - * 微信支付回调 分v2 和v3 的处理方式 - * - * @param data 通知结果 - * @return 支付回调对象 - * @throws WxPayException 微信异常类 - */ -// @Override - public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException { - log.info("微信支付回调data数据:{}", data.getBody()); - // 微信支付 v2 回调结果处理 - switch (config.getApiVersion()) { - case WxPayClientConfig.API_VERSION_V2: - return parseOrderNotifyV2(data); - case WxPayClientConfig.API_VERSION_V3: - return parseOrderNotifyV3(data); - default: - throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion())); - } - } - - private PayOrderNotifyRespDTO parseOrderNotifyV3(PayNotifyReqDTO data) throws WxPayException { - WxPayOrderNotifyV3Result wxPayOrderNotifyV3Result = client.parseOrderNotifyV3Result(data.getBody(), null); - WxPayOrderNotifyV3Result.DecryptNotifyResult result = wxPayOrderNotifyV3Result.getResult(); - // 转换结果 - Assert.isTrue(Objects.equals(wxPayOrderNotifyV3Result.getResult().getTradeState(), "SUCCESS"), - "支付结果非 SUCCESS"); - return PayOrderNotifyRespDTO - .builder() - .orderExtensionNo(result.getOutTradeNo()) - .channelOrderNo(result.getTradeState()) - .successTime(LocalDateTimeUtil.parse(result.getSuccessTime(), "yyyy-MM-dd'T'HH:mm:ssXXX")) - .build(); - } - - private PayOrderNotifyRespDTO parseOrderNotifyV2(PayNotifyReqDTO data) throws WxPayException { - WxPayOrderNotifyResult notifyResult = client.parseOrderNotifyResult(data.getBody()); - Assert.isTrue(Objects.equals(notifyResult.getResultCode(), "SUCCESS"), "支付结果非 SUCCESS"); - // 转换结果 - return PayOrderNotifyRespDTO - .builder() - .orderExtensionNo(notifyResult.getOutTradeNo()) - .channelOrderNo(notifyResult.getTransactionId()) - .channelUserId(notifyResult.getOpenid()) - .successTime(LocalDateTimeUtil.parse(notifyResult.getTimeEnd(), "yyyyMMddHHmmss")) - .build(); - - } - - @Override - protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { - // TODO 需要实现 - throw new UnsupportedOperationException(); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java new file mode 100644 index 000000000..bec86060f --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WxNativePayClient.java @@ -0,0 +1,80 @@ +package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; + +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; +import cn.iocoder.yudao.framework.pay.core.client.dto.refund.PayRefundUnifiedReqDTO; +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 com.github.binarywang.wxpay.bean.order.WxPayNativeOrderResult; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum; +import com.github.binarywang.wxpay.constant.WxPayConstants; +import com.github.binarywang.wxpay.exception.WxPayException; +import lombok.extern.slf4j.Slf4j; + +/** + * 微信支付【Native 二维码】的 PayClient 实现类 + * + * 文档:Native 下单 + * + * @author zwy + */ +@Slf4j +public class WxNativePayClient extends AbstractWxPayClient { + + public WxNativePayClient(Long channelId, WxPayClientConfig config) { + super(channelId, PayChannelEnum.WX_NATIVE.getCode(), config); + } + + @Override + protected void doInit() { + super.doInit(WxPayConstants.TradeType.NATIVE); + } + + @Override + protected PayOrderUnifiedRespDTO doUnifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() + .outTradeNo(reqDTO.getMerchantOrderId()) + .body(reqDTO.getSubject()) + .detail(reqDTO.getBody()) + .totalFee(reqDTO.getAmount()) // 单位分 + .productId(reqDTO.getMerchantOrderId()) + .timeExpire(formatDateV2(reqDTO.getExpireTime())) + .spbillCreateIp(reqDTO.getUserIp()) + .notifyUrl(reqDTO.getNotifyUrl()) + .build(); + // 执行请求 + WxPayNativeOrderResult response = client.createOrder(request); + + // 转换结果 + return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE_URL.getMode(), + response.getCodeUrl()); + } + + @Override + protected PayOrderUnifiedRespDTO doUnifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request(); + request.setOutTradeNo(reqDTO.getMerchantOrderId()); + request.setDescription(reqDTO.getBody()); + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分 + request.setTimeExpire(formatDateV3(reqDTO.getExpireTime())); + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); + request.setNotifyUrl(reqDTO.getNotifyUrl()); + // 执行请求 + WxPayNativeOrderResult response = client.createOrderV3(TradeTypeEnum.NATIVE, request); + + // 转换结果 + return new PayOrderUnifiedRespDTO(PayOrderDisplayModeEnum.QR_CODE_URL.getMode(), + response.getCodeUrl()); + } + + @Override + protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { + return null; + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderDisplayModeEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderDisplayModeEnum.java index 7135c2d91..1e81d74c5 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/order/PayOrderDisplayModeEnum.java +++ b/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 { IFRAME("iframe"), // IFrame 内嵌链接的方式【目前暂时用不到】 FORM("form"), // HTML 表单提交 QR_CODE("qr_code"), // 二维码的文字内容 - QR_CODE_URL("qr_code_url"), // 二维码的图片链接【目前暂时用不到】 + QR_CODE_URL("qr_code_url"), // 二维码的图片链接 BAR_CODE("bar_code"), // 条形码 APP("app"), // 应用【目前暂时用不到】 CUSTOM("custom"), // 自定义:每种支付方式,做个性化处理;例如说,微信公众号支付时,调用 JSAPI 接口 diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http index 70b85d80e..8b4008455 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http +++ b/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}} "openid": "oLefc4g5GjKWHJjLjMSXB3wX0fD0" } } + +### /pay/create 提交支付订单【wx_native】 +POST {{appApi}}/pay/order/submit +Content-Type: application/json +Authorization: Bearer {{appToken}} +tenant-id: {{appTenentId}} + +{ + "id": 202, + "channelCode": "wx_native" +}