From 67d60e32f856014f6510b9ffa7c1b008ae526e41 Mon Sep 17 00:00:00 2001 From: YunaiV Date: Thu, 6 Jul 2023 00:19:05 +0800 Subject: [PATCH] mall + pay: 1、调整微信支付,修复 v2 传递的时间不正确 2、调整 mp js ticket 接口到 system 模块 --- yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImpl.java | 8 ++++---- yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java | 4 ++-- yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXLitePayClient.java | 197 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXNativePayClient.java | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPayClientConfig.java | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPubPayClient.java | 201 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java | 198 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXNativePayClient.java | 181 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java | 111 --------------------------------------------------------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java | 196 ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java | 2 +- yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayDisplayModeEnum.java | 7 ++++--- yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java | 7 ++----- yudao-module-member/yudao-module-member-biz/pom.xml | 4 ++-- yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.http | 2 -- yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.java | 37 ------------------------------------- yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java | 1 + yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java | 2 +- yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java | 2 +- yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/dataobject/merchant/PayChannelDOTest.java | 2 +- yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/mysql/merchant/PayChannelMapperIntegrationTest.java | 2 +- yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/merchant/PayChannelServiceTest.java | 2 +- yudao-module-system/yudao-module-system-biz/pom.xml | 4 ++++ yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.http | 4 ++++ yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.java | 38 ++++++++++++++++++++++++++++++++++++++ yudao-server/src/main/resources/static/pay_wx_pub.html | 7 +++++-- yudao-ui-admin/src/views/mall/promotion/seckill/seckillActivity/index.vue | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------ yudao-ui-admin/src/views/mall/promotion/seckill/seckillTime/index.vue | 25 ++++++++++--------------- 28 files changed, 822 insertions(+), 824 deletions(-) create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXLitePayClient.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 create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPayClientConfig.java create mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPubPayClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXNativePayClient.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java delete mode 100644 yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java delete mode 100644 yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.http delete mode 100644 yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.java create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.http create mode 100644 yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.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 aee06ee..9abb1b6 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 @@ -5,10 +5,10 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.*; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXLitePayClient; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXNativePayClient; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXLitePayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXNativePayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPubPayClient; import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; import lombok.extern.slf4j.Slf4j; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java index bbe96c1..471161c 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java @@ -9,8 +9,6 @@ import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.util.Set; -// TODO 芋艿:参数校验 - /** * 支付宝的 PayClientConfig 实现类 * 属性主要来自 {@link com.alipay.api.AlipayConfig} 的必要属性 @@ -111,7 +109,9 @@ public class AlipayPayClientConfig implements PayClientConfig { @Override public Set> verifyParam(Validator validator) { + // TODO 芋艿:参数校验 return validator.validate(this, MODE_PUBLIC_KEY.equals(this.getMode()) ? ModePublicKey.class : ModeCertificate.class); } + } diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXLitePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXLitePayClient.java new file mode 100644 index 0000000..65d087a --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXLitePayClient.java @@ -0,0 +1,197 @@ +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.map.MapUtil; +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.PayChannelEnum; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; +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; + +/** + * 微信小程序下支付 + * + * @author zwy + */ +@Slf4j +public class WXLitePayClient extends AbstractPayClient { + + private WxPayService client; + + public WXLitePayClient(Long channelId, WXPayClientConfig config) { + super(channelId, PayChannelEnum.WX_LITE.getCode(), config); + } + + @Override + protected void doInit() { + WxPayConfig payConfig = new WxPayConfig(); + BeanUtil.copyProperties(config, payConfig, "privateKeyContent","privateCertContent"); + payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式 +// 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(); +// WxPayMpOrderResult response; +// try { +// switch (config.getApiVersion()) { +// case WXPayClientConfig.API_VERSION_V2: +// response = this.unifiedOrderV2(reqDTO); +// break; +// case WXPayClientConfig.API_VERSION_V3: +// WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO); +// // 将 V3 的结果,统一转换成 V2。返回的字段是一致的 +// response = new WxPayMpOrderResult(); +// BeanUtil.copyProperties(responseV3, response, true); +// 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, response, codeMapping); + } + + private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() + .outTradeNo(reqDTO.getMerchantOrderId()) + .body(reqDTO.getBody()) + .totalFee(reqDTO.getAmount().intValue()) // 单位分 + .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")) // v2的时间格式 + .spbillCreateIp(reqDTO.getUserIp()) + .openid(getOpenid(reqDTO)) + .notifyUrl(reqDTO.getNotifyUrl()) + .build(); + // 执行请求 + return client.createOrder(request); + } + + private WxPayUnifiedOrderV3Result.JsapiResult 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.setTimeExpire(DateUtil.format(Date.from(reqDTO.getExpireTime().atZone(ZoneId.systemDefault()).toInstant()), "yyyy-MM-dd'T'HH:mm:ssXXX")); // v3的时间格式 + request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO))); + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); + request.setNotifyUrl(reqDTO.getNotifyUrl()); + // 执行请求 + return client.createOrderV3(TradeTypeEnum.JSAPI, request); + } + + private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) { + String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid"); + if (StrUtil.isEmpty(openid)) { + throw new IllegalArgumentException("支付请求的 openid 不能为空!"); + } + return openid; + } + + /** + * + * 微信支付回调 分 v2 和v3 的处理方式 + * + * @param data 通知结果 + * @return 支付回调对象 + * @throws WxPayException 微信异常类 + */ +// @Override + public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException { + log.info("[parseOrderNotify][微信支付回调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.getTransactionId()) + .channelUserId(result.getPayer().getOpenid()) + .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 0000000..a4f1807 --- /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,180 @@ +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.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 支付 + * + * @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/WXPayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPayClientConfig.java new file mode 100644 index 0000000..462f47a --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPayClientConfig.java @@ -0,0 +1,111 @@ +package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; + +import cn.hutool.core.io.IoUtil; +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; +import lombok.Data; + +import javax.validation.ConstraintViolation; +import javax.validation.Validator; +import javax.validation.constraints.NotBlank; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.util.Set; + +/** + * 微信支付的 PayClientConfig 实现类 + * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性 + * + * @author 芋道源码 + */ +@Data +public class WXPayClientConfig implements PayClientConfig { + + /** + * API 版本 - V2 + * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1 + */ + public static final String API_VERSION_V2 = "v2"; + /** + * API 版本 - V3 + * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml + */ + public static final String API_VERSION_V3 = "v3"; + + /** + * 公众号或者小程序的 appid + */ + @NotBlank(message = "APPID 不能为空", groups = {V2.class, V3.class}) + private String appId; + /** + * 商户号 + */ + @NotBlank(message = "商户号 不能为空", groups = {V2.class, V3.class}) + private String mchId; + /** + * API 版本 + */ + @NotBlank(message = "API 版本 不能为空", groups = {V2.class, V3.class}) + private String apiVersion; + + // ========== V2 版本的参数 ========== + + /** + * 商户密钥 + */ + @NotBlank(message = "商户密钥 不能为空", groups = V2.class) + private String mchKey; + /** + * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. + * 对应的字符串 + * + * 注意,可通过 {@link #main(String[])} 读取 + */ + /// private String keyContent; + + // ========== V3 版本的参数 ========== + /** + * apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. + * 对应的字符串 + * 注意,可通过 {@link #main(String[])} 读取 + */ + @NotBlank(message = "apiclient_key 不能为空", groups = V3.class) + private String privateKeyContent; + /** + * apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. + * 对应的字符串 + *

+ * 注意,可通过 {@link #main(String[])} 读取 + */ + @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class) + private String privateCertContent; + /** + * apiV3 密钥值 + */ + @NotBlank(message = "apiV3 密钥值 不能为空", groups = V3.class) + private String apiV3Key; + + /** + * 分组校验 v2版本 + */ + public interface V2 { + } + + /** + * 分组校验 v3版本 + */ + public interface V3 { + } + + @Override + public Set> verifyParam(Validator validator) { + return validator.validate(this, this.getApiVersion().equals(API_VERSION_V2) ? V2.class : V3.class); + } + + public static void main(String[] args) throws FileNotFoundException { + String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"; + /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_key.pem"; + /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"; + System.out.println(IoUtil.readUtf8(new FileInputStream(path))); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPubPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPubPayClient.java new file mode 100644 index 0000000..390428b --- /dev/null +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/weixin/WXPubPayClient.java @@ -0,0 +1,201 @@ +package cn.iocoder.yudao.framework.pay.core.client.impl.weixin; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.date.LocalDateTimeUtil; +import cn.hutool.core.date.TemporalAccessorUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.iocoder.yudao.framework.common.util.io.FileUtils; +import cn.iocoder.yudao.framework.common.util.json.JsonUtils; +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.PayChannelEnum; +import cn.iocoder.yudao.framework.pay.core.enums.PayDisplayModeEnum; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; +import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; +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.LocalDateTime; +import java.time.ZoneId; +import java.util.Objects; + +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; + +/** + * 微信支付(公众号)的 PayClient 实现类 + * + * @author 芋道源码 + */ +@Slf4j +public class WXPubPayClient extends AbstractPayClient { + + private WxPayService client; + + public WXPubPayClient(Long channelId, WXPayClientConfig config) { + super(channelId, PayChannelEnum.WX_PUB.getCode(), config); + } + + @Override + protected void doInit() { + WxPayConfig payConfig = new WxPayConfig(); + BeanUtil.copyProperties(config, payConfig, "keyContent"); + payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式 +// 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) { + WxPayMpOrderResult response = null; + try { + switch (config.getApiVersion()) { + case WXPayClientConfig.API_VERSION_V2: + response = this.unifiedOrderV2(reqDTO); + break; + case WXPayClientConfig.API_VERSION_V3: + WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO); + // 将 V3 的结果,统一转换成 V2。返回的字段是一致的 + response = new WxPayMpOrderResult(); + BeanUtil.copyProperties(responseV3, response, true); + 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); + System.out.println(); + } + return new PayOrderUnifiedRespDTO().setDisplayMode(PayDisplayModeEnum.CUSTOM.getMode()) + .setDisplayContent(JsonUtils.toJsonString(response)); + } + + + private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { + // 构建 WxPayUnifiedOrderRequest 对象 + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() + .outTradeNo(reqDTO.getMerchantOrderId()) + .body(reqDTO.getBody()) + .totalFee(reqDTO.getAmount()) // 单位分 + .timeExpire(formatDate(reqDTO.getExpireTime())) + .spbillCreateIp(reqDTO.getUserIp()) + .openid(getOpenid(reqDTO)) + .notifyUrl(reqDTO.getNotifyUrl()) + .build(); + // 执行请求 + return client.createOrder(request); + } + + private WxPayUnifiedOrderV3Result.JsapiResult 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())); // 单位分 + request.setTimeExpire(formatDate(reqDTO.getExpireTime())); + request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO))); + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); + request.setNotifyUrl(reqDTO.getNotifyUrl()); + // 执行请求 + return client.createOrderV3(TradeTypeEnum.JSAPI, request); + } + + private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) { + String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid"); + if (StrUtil.isEmpty(openid)) { + throw new IllegalArgumentException("支付请求的 openid 不能为空!"); + } + return openid; + } + + /** + * + * 微信支付回调 分v2 和v3 的处理方式 + * + * @param data 通知结果 + * @return 支付回调对象 + * @throws WxPayException 微信异常类 + */ +// @Override + public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException { + log.info("[parseOrderNotify][微信支付回调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) throws Throwable { + // TODO 需要实现 + throw new UnsupportedOperationException(); + } + + private static String formatDate(LocalDateTime time) { +// return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), "yyyy-MM-dd'T'HH:mm:ssXXX"); + return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), "yyyyMMddHHmmss"); + } + +} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java deleted file mode 100644 index 46ac313..0000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXLitePayClient.java +++ /dev/null @@ -1,198 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.impl.wx; - -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.map.MapUtil; -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.notify.PayRefundNotifyRespDTO; -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.PayChannelEnum; -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; -import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; -import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; -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; - -/** - * 微信小程序下支付 - * - * @author zwy - */ -@Slf4j -public class WXLitePayClient extends AbstractPayClient { - - private WxPayService client; - - public WXLitePayClient(Long channelId, WXPayClientConfig config) { - super(channelId, PayChannelEnum.WX_LITE.getCode(), config); - } - - @Override - protected void doInit() { - WxPayConfig payConfig = new WxPayConfig(); - BeanUtil.copyProperties(config, payConfig, "privateKeyContent","privateCertContent"); - payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式 -// 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(); -// WxPayMpOrderResult response; -// try { -// switch (config.getApiVersion()) { -// case WXPayClientConfig.API_VERSION_V2: -// response = this.unifiedOrderV2(reqDTO); -// break; -// case WXPayClientConfig.API_VERSION_V3: -// WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO); -// // 将 V3 的结果,统一转换成 V2。返回的字段是一致的 -// response = new WxPayMpOrderResult(); -// BeanUtil.copyProperties(responseV3, response, true); -// 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, response, codeMapping); - } - - private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { - // 构建 WxPayUnifiedOrderRequest 对象 - WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() - .outTradeNo(reqDTO.getMerchantOrderId()) - .body(reqDTO.getBody()) - .totalFee(reqDTO.getAmount().intValue()) // 单位分 - .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")) // v2的时间格式 - .spbillCreateIp(reqDTO.getUserIp()) - .openid(getOpenid(reqDTO)) - .notifyUrl(reqDTO.getNotifyUrl()) - .build(); - // 执行请求 - return client.createOrder(request); - } - - private WxPayUnifiedOrderV3Result.JsapiResult 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.setTimeExpire(DateUtil.format(Date.from(reqDTO.getExpireTime().atZone(ZoneId.systemDefault()).toInstant()), "yyyy-MM-dd'T'HH:mm:ssXXX")); // v3的时间格式 - request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO))); - request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); - request.setNotifyUrl(reqDTO.getNotifyUrl()); - // 执行请求 - return client.createOrderV3(TradeTypeEnum.JSAPI, request); - } - - private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) { - String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid"); - if (StrUtil.isEmpty(openid)) { - throw new IllegalArgumentException("支付请求的 openid 不能为空!"); - } - return openid; - } - - /** - * - * 微信支付回调 分 v2 和v3 的处理方式 - * - * @param data 通知结果 - * @return 支付回调对象 - * @throws WxPayException 微信异常类 - */ -// @Override - public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException { - log.info("[parseOrderNotify][微信支付回调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.getTransactionId()) - .channelUserId(result.getPayer().getOpenid()) - .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/wx/WXNativePayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXNativePayClient.java deleted file mode 100644 index d31cdcd..0000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXNativePayClient.java +++ /dev/null @@ -1,181 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.impl.wx; - -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.notify.PayRefundNotifyRespDTO; -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.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 支付 - * - * @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/wx/WXPayClientConfig.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java deleted file mode 100644 index 294f8e4..0000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java +++ /dev/null @@ -1,111 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.impl.wx; - -import cn.hutool.core.io.IoUtil; -import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; -import lombok.Data; - -import javax.validation.ConstraintViolation; -import javax.validation.Validator; -import javax.validation.constraints.NotBlank; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.util.Set; - -/** - * 微信支付的 PayClientConfig 实现类 - * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性 - * - * @author 芋道源码 - */ -@Data -public class WXPayClientConfig implements PayClientConfig { - - /** - * API 版本 - V2 - * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1 - */ - public static final String API_VERSION_V2 = "v2"; - /** - * API 版本 - V3 - * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml - */ - public static final String API_VERSION_V3 = "v3"; - - /** - * 公众号或者小程序的 appid - */ - @NotBlank(message = "APPID 不能为空", groups = {V2.class, V3.class}) - private String appId; - /** - * 商户号 - */ - @NotBlank(message = "商户号 不能为空", groups = {V2.class, V3.class}) - private String mchId; - /** - * API 版本 - */ - @NotBlank(message = "API 版本 不能为空", groups = {V2.class, V3.class}) - private String apiVersion; - - // ========== V2 版本的参数 ========== - - /** - * 商户密钥 - */ - @NotBlank(message = "商户密钥 不能为空", groups = V2.class) - private String mchKey; - /** - * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径. - * 对应的字符串 - * - * 注意,可通过 {@link #main(String[])} 读取 - */ - /// private String keyContent; - - // ========== V3 版本的参数 ========== - /** - * apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. - * 对应的字符串 - * 注意,可通过 {@link #main(String[])} 读取 - */ - @NotBlank(message = "apiclient_key 不能为空", groups = V3.class) - private String privateKeyContent; - /** - * apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径. - * 对应的字符串 - *

- * 注意,可通过 {@link #main(String[])} 读取 - */ - @NotBlank(message = "apiclient_cert 不能为空", groups = V3.class) - private String privateCertContent; - /** - * apiV3 密钥值 - */ - @NotBlank(message = "apiV3 密钥值 不能为空", groups = V3.class) - private String apiV3Key; - - /** - * 分组校验 v2版本 - */ - public interface V2 { - } - - /** - * 分组校验 v3版本 - */ - public interface V3 { - } - - @Override - public Set> verifyParam(Validator validator) { - return validator.validate(this, this.getApiVersion().equals(API_VERSION_V2) ? V2.class : V3.class); - } - - public static void main(String[] args) throws FileNotFoundException { - String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.p12"; - /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_key.pem"; - /// String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.pem"; - System.out.println(IoUtil.readUtf8(new FileInputStream(path))); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java deleted file mode 100644 index f464582..0000000 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java +++ /dev/null @@ -1,196 +0,0 @@ -package cn.iocoder.yudao.framework.pay.core.client.impl.wx; - -import cn.hutool.core.bean.BeanUtil; -import cn.hutool.core.date.LocalDateTimeUtil; -import cn.hutool.core.date.TemporalAccessorUtil; -import cn.hutool.core.lang.Assert; -import cn.hutool.core.map.MapUtil; -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.PayChannelEnum; -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; -import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyV3Result; -import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest; -import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request; -import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result; -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.LocalDateTime; -import java.time.ZoneId; -import java.util.Objects; - -/** - * 微信支付(公众号)的 PayClient 实现类 - * - * @author 芋道源码 - */ -@Slf4j -public class WXPubPayClient extends AbstractPayClient { - - private WxPayService client; - - public WXPubPayClient(Long channelId, WXPayClientConfig config) { - super(channelId, PayChannelEnum.WX_PUB.getCode(), config); - } - - @Override - protected void doInit() { - WxPayConfig payConfig = new WxPayConfig(); - BeanUtil.copyProperties(config, payConfig, "keyContent"); - payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式 -// 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(); -// -// WxPayMpOrderResult response; -// try { -// switch (config.getApiVersion()) { -// case WXPayClientConfig.API_VERSION_V2: -// response = this.unifiedOrderV2(reqDTO); -// break; -// case WXPayClientConfig.API_VERSION_V3: -// WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO); -// // 将 V3 的结果,统一转换成 V2。返回的字段是一致的 -// response = new WxPayMpOrderResult(); -// BeanUtil.copyProperties(responseV3, response, true); -// 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, response, codeMapping); - } - - - private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { - // 构建 WxPayUnifiedOrderRequest 对象 - WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() - .outTradeNo(reqDTO.getMerchantOrderId()) - .body(reqDTO.getBody()) - .totalFee(reqDTO.getAmount()) // 单位分 - .timeExpire(formatDate(reqDTO.getExpireTime())) - .spbillCreateIp(reqDTO.getUserIp()) - .openid(getOpenid(reqDTO)) - .notifyUrl(reqDTO.getNotifyUrl()) - .build(); - // 执行请求 - return client.createOrder(request); - } - - private WxPayUnifiedOrderV3Result.JsapiResult 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())); // 单位分 - request.setTimeExpire(formatDate(reqDTO.getExpireTime())); - request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO))); - request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp())); - request.setNotifyUrl(reqDTO.getNotifyUrl()); - // 执行请求 - return client.createOrderV3(TradeTypeEnum.JSAPI, request); - } - - private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) { - String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid"); - if (StrUtil.isEmpty(openid)) { - throw new IllegalArgumentException("支付请求的 openid 不能为空!"); - } - return openid; - } - - /** - * - * 微信支付回调 分v2 和v3 的处理方式 - * - * @param data 通知结果 - * @return 支付回调对象 - * @throws WxPayException 微信异常类 - */ -// @Override - public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyReqDTO data) throws WxPayException { - log.info("[parseOrderNotify][微信支付回调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) throws Throwable { - // TODO 需要实现 - throw new UnsupportedOperationException(); - } - - private static String formatDate(LocalDateTime time) { - return TemporalAccessorUtil.format(time.atZone(ZoneId.systemDefault()), "yyyy-MM-dd'T'HH:mm:ssXXX"); - } - -} diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java index c6528ae..d018495 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelEnum.java @@ -3,7 +3,7 @@ package cn.iocoder.yudao.framework.pay.core.enums; import cn.hutool.core.util.ArrayUtil; import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig; import lombok.AllArgsConstructor; import lombok.Getter; diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayDisplayModeEnum.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayDisplayModeEnum.java index d19bfde..9bcbf99 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayDisplayModeEnum.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayDisplayModeEnum.java @@ -13,12 +13,13 @@ import lombok.Getter; public enum PayDisplayModeEnum { URL("url"), // Redirect 跳转链接的方式 - IFRAME("iframe"), // IFrame 内嵌链接的方式 + 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"), // 应用 + APP("app"), // 应用【目前暂时用不到】 + CUSTOM("custom"), // 自定义:每种支付方式,做个性化处理;例如说,微信公众号支付时,调用 JSAPI 接口 ; /** diff --git a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java index 071f53d..4d560d8 100644 --- a/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java +++ b/yudao-framework/yudao-spring-boot-starter-biz-pay/src/test/java/cn/iocoder/yudao/framework/pay/core/client/impl/PayClientFactoryImplIntegrationTest.java @@ -2,17 +2,14 @@ package cn.iocoder.yudao.framework.pay.core.client.impl; import cn.hutool.core.io.IoUtil; import cn.hutool.core.util.RandomUtil; -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; import cn.iocoder.yudao.framework.pay.core.client.PayClient; import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayQrPayClient; import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayWapPayClient; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPubPayClient; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPubPayClient; import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; -import com.alipay.api.response.AlipayTradePrecreateResponse; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; diff --git a/yudao-module-member/yudao-module-member-biz/pom.xml b/yudao-module-member/yudao-module-member-biz/pom.xml index 5b135dd..a5eda05 100644 --- a/yudao-module-member/yudao-module-member-biz/pom.xml +++ b/yudao-module-member/yudao-module-member-biz/pom.xml @@ -41,11 +41,11 @@ cn.iocoder.boot - yudao-spring-boot-starter-biz-weixin + yudao-spring-boot-starter-biz-tenant cn.iocoder.boot - yudao-spring-boot-starter-biz-tenant + yudao-spring-boot-starter-biz-weixin diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.http b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.http deleted file mode 100644 index 60f28c6..0000000 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.http +++ /dev/null @@ -1,2 +0,0 @@ -### 请求 /login 接口 => 成功 -GET {{userServerUrl}}/wx/mp/get-jsapi-ticket diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.java deleted file mode 100644 index 7a2413e..0000000 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/controller/app/weixin/AppWxMpController.java +++ /dev/null @@ -1,37 +0,0 @@ -package cn.iocoder.yudao.module.member.controller.app.weixin; - -import cn.iocoder.yudao.framework.common.pojo.CommonResult; -import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.Operation; -import lombok.extern.slf4j.Slf4j; -import me.chanjar.weixin.common.bean.WxJsapiSignature; -import me.chanjar.weixin.common.error.WxErrorException; -import me.chanjar.weixin.mp.api.WxMpService; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -import javax.annotation.Resource; - -import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; - -@Tag(name = "微信公众号") -@RestController -@RequestMapping("/member/wx-mp") -@Validated -@Slf4j -public class AppWxMpController { - - @Resource - private WxMpService mpService; - - @PostMapping("/create-jsapi-signature") - @Operation(summary = "创建微信 JS SDK 初始化所需的签名", - description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档") - public CommonResult createJsapiSignature(@RequestParam("url") String url) throws WxErrorException { - return success(mpService.createJsapiSignature(url)); - } - -} diff --git a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java index d8c59ad..e0d23e0 100644 --- a/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java +++ b/yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/service/auth/MemberAuthServiceImpl.java @@ -125,6 +125,7 @@ public class MemberAuthServiceImpl implements MemberAuthService { @Override public AppAuthLoginRespVO weixinMiniAppLogin(AppAuthWeixinMiniAppLoginReqVO reqVO) { // 获得对应的手机号信息 + // TODO @芋艿:需要弱化微信小程序的依赖,通过 system 获取手机号 WxMaPhoneNumberInfo phoneNumberInfo; try { phoneNumberInfo = wxMaService.getUserService().getNewPhoneNoInfo(reqVO.getPhoneCode()); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java index a73d528..29d050c 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/convert/order/PayOrderConvert.java @@ -94,7 +94,7 @@ public interface PayOrderConvert { @Mapping(target = "id", ignore = true) PayOrderExtensionDO convert(PayOrderSubmitReqVO bean, String userIp); - PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO); + PayOrderUnifiedReqDTO convert2(PayOrderSubmitReqVO reqVO, String userIp); PayOrderSubmitRespVO convert(PayOrderUnifiedRespDTO bean); diff --git a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java index a653887..f8f6f54 100755 --- a/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java @@ -141,7 +141,7 @@ public class PayOrderServiceImpl implements PayOrderService { orderExtensionMapper.insert(orderExtension); // 3. 调用三方接口 - PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO) + PayOrderUnifiedReqDTO unifiedOrderReqDTO = PayOrderConvert.INSTANCE.convert2(reqVO, userIp) // 商户相关的字段 .setMerchantOrderId(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性! .setSubject(order.getSubject()).setBody(order.getBody()) diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/dataobject/merchant/PayChannelDOTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/dataobject/merchant/PayChannelDOTest.java index 2818cb1..73b4f6f 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/dataobject/merchant/PayChannelDOTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/dataobject/merchant/PayChannelDOTest.java @@ -1,7 +1,7 @@ package cn.iocoder.yudao.module.pay.dal.dataobject.merchant; import cn.iocoder.yudao.framework.common.util.json.JsonUtils; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig; import org.junit.jupiter.api.Test; public class PayChannelDOTest { diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/mysql/merchant/PayChannelMapperIntegrationTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/mysql/merchant/PayChannelMapperIntegrationTest.java index b869163..c33bcb7 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/mysql/merchant/PayChannelMapperIntegrationTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test-integration/java/cn/iocoder/yudao/module/pay/dal/mysql/merchant/PayChannelMapperIntegrationTest.java @@ -4,7 +4,7 @@ import cn.hutool.core.io.IoUtil; import cn.iocoder.yudao.module.pay.dal.dataobject.merchant.PayChannelDO; import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig; import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; import cn.iocoder.yudao.module.pay.test.BaseDbIntegrationTest; import org.junit.jupiter.api.Test; diff --git a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/merchant/PayChannelServiceTest.java b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/merchant/PayChannelServiceTest.java index 44ea6c0..50f12ba 100644 --- a/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/merchant/PayChannelServiceTest.java +++ b/yudao-module-pay/yudao-module-pay-biz/src/test/java/cn/iocoder/yudao/module/pay/service/merchant/PayChannelServiceTest.java @@ -4,7 +4,7 @@ import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; import cn.iocoder.yudao.framework.pay.core.client.impl.alipay.AlipayPayClientConfig; -import cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXPayClientConfig; +import cn.iocoder.yudao.framework.pay.core.client.impl.weixin.WXPayClientConfig; import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; import cn.iocoder.yudao.framework.test.core.ut.BaseDbUnitTest; import cn.iocoder.yudao.module.pay.controller.admin.merchant.vo.channel.PayChannelCreateReqVO; diff --git a/yudao-module-system/yudao-module-system-biz/pom.xml b/yudao-module-system/yudao-module-system-biz/pom.xml index 8980291..5abeb06 100644 --- a/yudao-module-system/yudao-module-system-biz/pom.xml +++ b/yudao-module-system/yudao-module-system-biz/pom.xml @@ -58,6 +58,10 @@ cn.iocoder.boot yudao-spring-boot-starter-biz-ip + + cn.iocoder.boot + yudao-spring-boot-starter-biz-weixin + diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.http b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.http new file mode 100644 index 0000000..6af305a --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.http @@ -0,0 +1,4 @@ +### 请求 /login 接口 => 成功 +POST {{appApi}}/system/wx-mp/create-jsapi-signature?url=http://www.iocoder.cn +Authorization: Bearer {{appToken}} +tenant-id: {{appTenentId}} diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.java new file mode 100644 index 0000000..e6e91b6 --- /dev/null +++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/controller/app/weixin/AppWxMpController.java @@ -0,0 +1,38 @@ +package cn.iocoder.yudao.module.system.controller.app.weixin; + +import cn.iocoder.yudao.framework.common.pojo.CommonResult; +import io.swagger.v3.oas.annotations.tags.Tag; +import io.swagger.v3.oas.annotations.Operation; +import lombok.extern.slf4j.Slf4j; +import me.chanjar.weixin.common.bean.WxJsapiSignature; +import me.chanjar.weixin.common.error.WxErrorException; +import me.chanjar.weixin.mp.api.WxMpService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + +import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; + +@Tag(name = "微信公众号") +@RestController +@RequestMapping("/system/wx-mp") +@Validated +@Slf4j +public class AppWxMpController { + + @Resource + private WxMpService mpService; + + // TODO @芋艿:需要额外考虑个问题;多租户下,如果每个小程序一个微信公众号,则会存在多个 appid; + @PostMapping("/create-jsapi-signature") + @Operation(summary = "创建微信 JS SDK 初始化所需的签名", + description = "参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档") + public CommonResult createJsapiSignature(@RequestParam("url") String url) throws WxErrorException { + return success(mpService.createJsapiSignature(url)); + } + +} diff --git a/yudao-server/src/main/resources/static/pay_wx_pub.html b/yudao-server/src/main/resources/static/pay_wx_pub.html index aa3471c..b41bb4b 100644 --- a/yudao-server/src/main/resources/static/pay_wx_pub.html +++ b/yudao-server/src/main/resources/static/pay_wx_pub.html @@ -16,8 +16,8 @@