Commit 1ed6656bbb103133dba4be65ee0875c4550d6550

Authored by YunaiV
1 parent 23d8da74

完成对微信公众号支付的封装

Showing 15 changed files with 423 additions and 67 deletions
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/io/FileUtils.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.common.util.io;
  2 +
  3 +import cn.hutool.core.io.FileUtil;
  4 +import cn.hutool.core.util.IdUtil;
  5 +import lombok.SneakyThrows;
  6 +
  7 +import java.io.File;
  8 +
  9 +/**
  10 + * 文件工具类
  11 + *
  12 + * @author 芋道源码
  13 + */
  14 +public class FileUtils {
  15 +
  16 + /**
  17 + * 创建临时文件
  18 + * 该文件会在 JVM 退出时,进行删除
  19 + *
  20 + * @param data 文件内容
  21 + * @return 文件
  22 + */
  23 + @SneakyThrows
  24 + public static File createTempFile(String data) {
  25 + // 创建文件,通过 UUID 保证唯一
  26 + File file = File.createTempFile(IdUtil.simpleUUID(), null);
  27 + // 标记 JVM 退出时,自动删除
  28 + file.deleteOnExit();
  29 + // 写入内容
  30 + FileUtil.writeUtf8String(data, file);
  31 + return file;
  32 + }
  33 +
  34 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/pom.xml
@@ -11,7 +11,10 @@ @@ -11,7 +11,10 @@
11 11
12 <artifactId>yudao-spring-boot-starter-biz-pay</artifactId> 12 <artifactId>yudao-spring-boot-starter-biz-pay</artifactId>
13 <name>${artifactId}</name> 13 <name>${artifactId}</name>
14 - <description>支付拓展,基于 IJPay 简单封装,支持微信、支付宝等常见支付渠道</description> 14 + <description>支付拓展,接入国内多个支付渠道
  15 + 1. 支付宝,基于官方 SDK 接入
  16 + 2. 微信支付,基于 weixin-java-pay 接入
  17 + </description>
15 18
16 <dependencies> 19 <dependencies>
17 <dependency> 20 <dependency>
@@ -34,17 +37,36 @@ @@ -34,17 +37,36 @@
34 <artifactId>slf4j-api</artifactId> 37 <artifactId>slf4j-api</artifactId>
35 </dependency> 38 </dependency>
36 39
37 - <!-- 三方云服务相关 -->  
38 <dependency> 40 <dependency>
39 - <groupId>com.github.javen205</groupId>  
40 - <artifactId>IJPay-AliPay</artifactId>  
41 - <version>2.7.8</version> 41 + <groupId>com.fasterxml.jackson.core</groupId>
  42 + <artifactId>jackson-databind</artifactId>
42 </dependency> 43 </dependency>
43 <dependency> 44 <dependency>
44 - <groupId>com.github.javen205</groupId>  
45 - <artifactId>IJPay-WxPay</artifactId>  
46 - <version>2.7.8</version> 45 + <groupId>com.fasterxml.jackson.core</groupId>
  46 + <artifactId>jackson-core</artifactId>
47 </dependency> 47 </dependency>
48 48
  49 + <!-- 三方云服务相关 -->
  50 +<!-- <dependency>-->
  51 +<!-- <groupId>com.github.javen205</groupId>-->
  52 +<!-- <artifactId>IJPay-AliPay</artifactId>-->
  53 +<!-- <version>2.7.8</version>-->
  54 +<!-- </dependency>-->
  55 +<!-- <dependency>-->
  56 +<!-- <groupId>com.github.javen205</groupId>-->
  57 +<!-- <artifactId>IJPay-WxPay</artifactId>-->
  58 +<!-- <version>2.7.8</version>-->
  59 +<!-- </dependency>-->
  60 + <dependency>
  61 + <groupId>com.alipay.sdk</groupId>
  62 + <artifactId>alipay-sdk-java</artifactId>
  63 + <version>4.17.9.ALL</version>
  64 + </dependency>
  65 + <dependency>
  66 + <groupId>com.github.binarywang</groupId>
  67 + <artifactId>weixin-java-pay</artifactId>
  68 + <version>4.1.9.B</version>
  69 + </dependency>
  70 + <!-- TODO 芋艿:清理 -->
49 </dependencies> 71 </dependencies>
50 </project> 72 </project>
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayCodeMapping.java renamed to yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/AbstractPayCodeMapping.java
@@ -2,8 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client; @@ -2,8 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client;
2 2
3 import cn.iocoder.yudao.framework.common.exception.ErrorCode; 3 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4 import cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants; 4 import cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants;
5 -  
6 -import java.util.function.Function; 5 +import lombok.extern.slf4j.Slf4j;
7 6
8 /** 7 /**
9 * 将 API 的错误码,转换为通用的错误码 8 * 将 API 的错误码,转换为通用的错误码
@@ -13,5 +12,22 @@ import java.util.function.Function; @@ -13,5 +12,22 @@ import java.util.function.Function;
13 * 12 *
14 * @author 芋道源码 13 * @author 芋道源码
15 */ 14 */
16 -public interface PayCodeMapping extends Function<String, ErrorCode> { 15 +@Slf4j
  16 +public abstract class AbstractPayCodeMapping {
  17 +
  18 + public final ErrorCode apply(String apiCode, String apiMsg) {
  19 + if (apiCode == null) {
  20 + log.error("[apply][API 错误码为空,请排查]");
  21 + return PayFrameworkErrorCodeConstants.EXCEPTION;
  22 + }
  23 + ErrorCode errorCode = this.apply0(apiCode, apiMsg);
  24 + if (errorCode == null) {
  25 + log.error("[apply][API 错误码({}) 错误提示({}) 无法匹配]", apiCode, apiMsg);
  26 + return PayFrameworkErrorCodeConstants.PAY_UNKNOWN;
  27 + }
  28 + return errorCode;
  29 + }
  30 +
  31 + protected abstract ErrorCode apply0(String apiCode, String apiMsg);
  32 +
17 } 33 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
@@ -18,6 +18,6 @@ public interface PayClient { @@ -18,6 +18,6 @@ public interface PayClient {
18 Long getId(); 18 Long getId();
19 19
20 // TODO 缺少注释 20 // TODO 缺少注释
21 - CommonResult<Object> unifiedOrder(PayOrderUnifiedReqDTO reqDTO); 21 + CommonResult<?> unifiedOrder(PayOrderUnifiedReqDTO reqDTO);
22 22
23 } 23 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayCommonResult.java
@@ -35,16 +35,13 @@ public class PayCommonResult&lt;T&gt; extends CommonResult&lt;T&gt; { @@ -35,16 +35,13 @@ public class PayCommonResult&lt;T&gt; extends CommonResult&lt;T&gt; {
35 private PayCommonResult() { 35 private PayCommonResult() {
36 } 36 }
37 37
38 - public static <T> PayCommonResult<T> build(String apiCode, String apiMsg, T data, PayCodeMapping codeMapping) { 38 + public static <T> PayCommonResult<T> build(String apiCode, String apiMsg, T data, AbstractPayCodeMapping codeMapping) {
39 Assert.notNull(codeMapping, "参数 codeMapping 不能为空"); 39 Assert.notNull(codeMapping, "参数 codeMapping 不能为空");
40 PayCommonResult<T> result = new PayCommonResult<T>().setApiCode(apiCode).setApiMsg(apiMsg); 40 PayCommonResult<T> result = new PayCommonResult<T>().setApiCode(apiCode).setApiMsg(apiMsg);
41 result.setData(data); 41 result.setData(data);
42 // 翻译错误码 42 // 翻译错误码
43 if (codeMapping != null) { 43 if (codeMapping != null) {
44 - ErrorCode errorCode = codeMapping.apply(apiCode);  
45 - if (errorCode == null) {  
46 - errorCode = PayFrameworkErrorCodeConstants.EXCEPTION;  
47 - } 44 + ErrorCode errorCode = codeMapping.apply(apiCode, apiMsg);
48 result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg()); 45 result.setCode(errorCode.getCode()).setMsg(errorCode.getMsg());
49 } 46 }
50 return result; 47 return result;
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java
@@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.dto; @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.dto;
2 2
3 import lombok.Data; 3 import lombok.Data;
4 import org.hibernate.validator.constraints.Length; 4 import org.hibernate.validator.constraints.Length;
  5 +import org.hibernate.validator.constraints.URL;
5 6
6 import javax.validation.constraints.DecimalMin; 7 import javax.validation.constraints.DecimalMin;
7 import javax.validation.constraints.NotEmpty; 8 import javax.validation.constraints.NotEmpty;
@@ -39,6 +40,12 @@ public class PayOrderUnifiedReqDTO { @@ -39,6 +40,12 @@ public class PayOrderUnifiedReqDTO {
39 @NotEmpty(message = "商品描述信息不能为空") 40 @NotEmpty(message = "商品描述信息不能为空")
40 @Length(max = 128, message = "商品描述信息长度不能超过128") 41 @Length(max = 128, message = "商品描述信息长度不能超过128")
41 private String body; 42 private String body;
  43 + /**
  44 + * 支付结果的回调地址
  45 + */
  46 + @NotEmpty(message = "支付结果的回调地址不能为空")
  47 + @URL(message = "支付结果的回调地址必须是 URL 格式")
  48 + private String notifyUrl;
42 49
43 // ========== 订单相关字段 ========== 50 // ========== 订单相关字段 ==========
44 51
@@ -55,4 +62,7 @@ public class PayOrderUnifiedReqDTO { @@ -55,4 +62,7 @@ public class PayOrderUnifiedReqDTO {
55 @NotNull(message = "支付过期时间不能为空") 62 @NotNull(message = "支付过期时间不能为空")
56 private Date expireTime; 63 private Date expireTime;
57 64
  65 + // ========== 拓展参数 ==========
  66 + // TODO 芋艿:待完善
  67 +
58 } 68 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
@@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl; @@ -2,7 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl;
2 2
3 import cn.iocoder.yudao.framework.pay.core.client.PayClient; 3 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
4 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; 4 import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
5 -import cn.iocoder.yudao.framework.pay.core.client.PayCodeMapping; 5 +import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
6 import lombok.extern.slf4j.Slf4j; 6 import lombok.extern.slf4j.Slf4j;
7 7
8 /** 8 /**
@@ -24,7 +24,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -24,7 +24,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
24 /** 24 /**
25 * 错误码枚举类 25 * 错误码枚举类
26 */ 26 */
27 - protected PayCodeMapping codeMapping; 27 + protected AbstractPayCodeMapping codeMapping;
28 /** 28 /**
29 * 支付配置 29 * 支付配置
30 */ 30 */
@@ -34,7 +34,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen @@ -34,7 +34,7 @@ public abstract class AbstractPayClient&lt;Config extends PayClientConfig&gt; implemen
34 return amount / 100.0; 34 return amount / 100.0;
35 } 35 }
36 36
37 - public AbstractPayClient(Long channelId, String channelCode, Config config, PayCodeMapping codeMapping) { 37 + public AbstractPayClient(Long channelId, String channelCode, Config config, AbstractPayCodeMapping codeMapping) {
38 this.channelId = channelId; 38 this.channelId = channelId;
39 this.channelCode = channelCode; 39 this.channelCode = channelCode;
40 this.codeMapping = codeMapping; 40 this.codeMapping = codeMapping;
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayConfig.java renamed to yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayClientConfig.java
@@ -11,7 +11,7 @@ import lombok.Data; @@ -11,7 +11,7 @@ import lombok.Data;
11 * @author 芋道源码 11 * @author 芋道源码
12 */ 12 */
13 @Data 13 @Data
14 -public class AlipayPayConfig implements PayClientConfig { 14 +public class AlipayPayClientConfig implements PayClientConfig {
15 15
16 /** 16 /**
17 * 网关地址 - 线上 17 * 网关地址 - 线上
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPayCodeMapping.java
1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; 1 package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
2 2
3 import cn.iocoder.yudao.framework.common.exception.ErrorCode; 3 import cn.iocoder.yudao.framework.common.exception.ErrorCode;
4 -import cn.iocoder.yudao.framework.pay.core.client.PayCodeMapping; 4 +import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
5 5
6 -public class AlipayPayCodeMapping implements PayCodeMapping { 6 +/**
  7 + * 支付宝的 PayCodeMapping 实现类
  8 + *
  9 + * @author 芋道源码
  10 + */
  11 +public class AlipayPayCodeMapping extends AbstractPayCodeMapping {
7 12
8 @Override 13 @Override
9 - public ErrorCode apply(String s) { 14 + protected ErrorCode apply0(String apiCode, String apiMsg) {
10 return null; 15 return null;
11 } 16 }
12 17
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
@@ -12,6 +12,9 @@ import com.alipay.api.domain.AlipayTradePrecreateModel; @@ -12,6 +12,9 @@ import com.alipay.api.domain.AlipayTradePrecreateModel;
12 import com.alipay.api.request.AlipayTradePrecreateRequest; 12 import com.alipay.api.request.AlipayTradePrecreateRequest;
13 import com.alipay.api.response.AlipayTradePrecreateResponse; 13 import com.alipay.api.response.AlipayTradePrecreateResponse;
14 import lombok.SneakyThrows; 14 import lombok.SneakyThrows;
  15 +import lombok.extern.slf4j.Slf4j;
  16 +
  17 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
15 18
16 /** 19 /**
17 * 支付宝【扫码支付】的 PayClient 实现类 20 * 支付宝【扫码支付】的 PayClient 实现类
@@ -19,11 +22,12 @@ import lombok.SneakyThrows; @@ -19,11 +22,12 @@ import lombok.SneakyThrows;
19 * 22 *
20 * @author 芋道源码 23 * @author 芋道源码
21 */ 24 */
22 -public class AlipayQrPayClient extends AbstractPayClient<AlipayPayConfig> { 25 +@Slf4j
  26 +public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig> {
23 27
24 private DefaultAlipayClient client; 28 private DefaultAlipayClient client;
25 29
26 - public AlipayQrPayClient(Long channelId, String channelCode, AlipayPayConfig config) { 30 + public AlipayQrPayClient(Long channelId, String channelCode, AlipayPayClientConfig config) {
27 super(channelId, channelCode, config, new AlipayPayCodeMapping()); 31 super(channelId, channelCode, config, new AlipayPayCodeMapping());
28 } 32 }
29 33
@@ -32,17 +36,18 @@ public class AlipayQrPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; { @@ -32,17 +36,18 @@ public class AlipayQrPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; {
32 protected void doInit() { 36 protected void doInit() {
33 AlipayConfig alipayConfig = new AlipayConfig(); 37 AlipayConfig alipayConfig = new AlipayConfig();
34 BeanUtil.copyProperties(config, alipayConfig, false); 38 BeanUtil.copyProperties(config, alipayConfig, false);
  39 + // 真实客户端
35 this.client = new DefaultAlipayClient(alipayConfig); 40 this.client = new DefaultAlipayClient(alipayConfig);
36 } 41 }
37 42
38 @Override 43 @Override
39 - public CommonResult<Object> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) { 44 + public CommonResult<AlipayTradePrecreateResponse> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
40 // 构建 AlipayTradePrecreateModel 请求 45 // 构建 AlipayTradePrecreateModel 请求
41 AlipayTradePrecreateModel model = new AlipayTradePrecreateModel(); 46 AlipayTradePrecreateModel model = new AlipayTradePrecreateModel();
42 model.setOutTradeNo(reqDTO.getMerchantOrderId()); 47 model.setOutTradeNo(reqDTO.getMerchantOrderId());
43 model.setSubject(reqDTO.getSubject()); 48 model.setSubject(reqDTO.getSubject());
44 model.setBody(reqDTO.getBody()); 49 model.setBody(reqDTO.getBody());
45 - model.setTotalAmount(calculateAmount(reqDTO.getAmount()).toString()); 50 + model.setTotalAmount(calculateAmount(reqDTO.getAmount()).toString()); // 单位:元
46 // TODO 芋艿:clientIp + expireTime 51 // TODO 芋艿:clientIp + expireTime
47 // 构建 AlipayTradePrecreateRequest 52 // 构建 AlipayTradePrecreateRequest
48 AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest(); 53 AlipayTradePrecreateRequest request = new AlipayTradePrecreateRequest();
@@ -53,6 +58,7 @@ public class AlipayQrPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; { @@ -53,6 +58,7 @@ public class AlipayQrPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; {
53 try { 58 try {
54 response = client.execute(request); 59 response = client.execute(request);
55 } catch (AlipayApiException e) { 60 } catch (AlipayApiException e) {
  61 + log.error("[unifiedOrder][request({}) 发起支付失败]", toJsonString(reqDTO), e);
56 return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping); 62 return PayCommonResult.build(e.getErrCode(), e.getErrMsg(), null, codeMapping);
57 } 63 }
58 64
@@ -64,10 +70,10 @@ public class AlipayQrPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; { @@ -64,10 +70,10 @@ public class AlipayQrPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; {
64 } 70 }
65 71
66 public static void main(String[] args) { 72 public static void main(String[] args) {
67 - AlipayPayConfig config = new AlipayPayConfig(); 73 + AlipayPayClientConfig config = new AlipayPayClientConfig();
68 config.setAppId("2021000118634035"); 74 config.setAppId("2021000118634035");
69 - config.setServerUrl(AlipayPayConfig.SERVER_URL_SANDBOX);  
70 - config.setSignType(AlipayPayConfig.SIGN_TYPE_DEFAULT); 75 + config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX);
  76 + config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
71 config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8="); 77 config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8=");
72 config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB"); 78 config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
73 AlipayQrPayClient client = new AlipayQrPayClient(1L, "biu", config); 79 AlipayQrPayClient client = new AlipayQrPayClient(1L, "biu", config);
@@ -79,4 +85,5 @@ public class AlipayQrPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; { @@ -79,4 +85,5 @@ public class AlipayQrPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; {
79 reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis())); 85 reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
80 client.unifiedOrder(reqDTO); 86 client.unifiedOrder(reqDTO);
81 } 87 }
  88 +
82 } 89 }
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
@@ -19,11 +19,11 @@ import lombok.SneakyThrows; @@ -19,11 +19,11 @@ import lombok.SneakyThrows;
19 * 19 *
20 * @author 芋道源码 20 * @author 芋道源码
21 */ 21 */
22 -public class AlipayWapPayClient extends AbstractPayClient<AlipayPayConfig> { 22 +public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig> {
23 23
24 private DefaultAlipayClient client; 24 private DefaultAlipayClient client;
25 25
26 - public AlipayWapPayClient(Long channelId, String channelCode, AlipayPayConfig config) { 26 + public AlipayWapPayClient(Long channelId, String channelCode, AlipayPayClientConfig config) {
27 super(channelId, channelCode, config, new AlipayPayCodeMapping()); 27 super(channelId, channelCode, config, new AlipayPayCodeMapping());
28 } 28 }
29 29
@@ -36,7 +36,7 @@ public class AlipayWapPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; { @@ -36,7 +36,7 @@ public class AlipayWapPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; {
36 } 36 }
37 37
38 @Override 38 @Override
39 - public CommonResult<Object> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) { 39 + public CommonResult<AlipayTradeWapPayResponse> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
40 // 构建 AlipayTradeWapPayModel 请求 40 // 构建 AlipayTradeWapPayModel 请求
41 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel(); 41 AlipayTradeWapPayModel model = new AlipayTradeWapPayModel();
42 model.setOutTradeNo(reqDTO.getMerchantOrderId()); 42 model.setOutTradeNo(reqDTO.getMerchantOrderId());
@@ -65,10 +65,10 @@ public class AlipayWapPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; { @@ -65,10 +65,10 @@ public class AlipayWapPayClient extends AbstractPayClient&lt;AlipayPayConfig&gt; {
65 } 65 }
66 66
67 public static void main(String[] args) { 67 public static void main(String[] args) {
68 - AlipayPayConfig config = new AlipayPayConfig(); 68 + AlipayPayClientConfig config = new AlipayPayClientConfig();
69 config.setAppId("2021000118634035"); 69 config.setAppId("2021000118634035");
70 - config.setServerUrl(AlipayPayConfig.SERVER_URL_SANDBOX);  
71 - config.setSignType(AlipayPayConfig.SIGN_TYPE_DEFAULT); 70 + config.setServerUrl(AlipayPayClientConfig.SERVER_URL_SANDBOX);
  71 + config.setSignType(AlipayPayClientConfig.SIGN_TYPE_DEFAULT);
72 config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8="); 72 config.setPrivateKey("MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCHsEV1cDupwJv890x84qbppUtRIfhaKSwSVN0thCcsDCaAsGR5MZslDkO8NCT9V4r2SVXjyY7eJUZlZd1M0C8T01Tg4UOx5LUbic0O3A1uJMy6V1n9IyYwbAW3AEZhBd5bSbPgrqvmv3NeWSTQT6Anxnllf+2iDH6zyA2fPl7cYyQtbZoDJQFGqr4F+cGh2R6akzRKNoBkAeMYwoY6es2lX8sJxCVPWUmxNUoL3tScwlSpd7Bxw0q9c/X01jMwuQ0+Va358zgFiGERTE6yD01eu40OBDXOYO3z++y+TAYHlQQ2toMO63trepo88X3xV3R44/1DH+k2pAm2IF5ixiLrAgMBAAECggEAPx3SoXcseaD7rmcGcE0p4SMfbsUDdkUSmBBbtfF0GzwnqNLkWa+mgE0rWt9SmXngTQH97vByAYmLPl1s3G82ht1V7Sk7yQMe74lhFllr8eEyTjeVx3dTK1EEM4TwN+936DTXdFsr4TELJEcJJdD0KaxcCcfBLRDs2wnitEFZ9N+GoZybVmY8w0e0MI7PLObUZ2l0X4RurQnfG9ZxjXjC7PkeMVv7cGGylpNFi3BbvkRhdhLPDC2E6wqnr9e7zk+hiENivAezXrtxtwKovzCtnWJ1r0IO14Rh47H509Ic0wFnj+o5YyUL4LdmpL7yaaH6fM7zcSLFjNZPHvZCKPwYcQKBgQDQFho98QvnL8ex4v6cry4VitGpjSXm1qP3vmMQk4rTsn8iPWtcxPjqGEqOQJjdi4Mi0VZKQOLFwlH0kl95wNrD/isJ4O1yeYfX7YAXApzHqYNINzM79HemO3Yx1qLMW3okRFJ9pPRzbQ9qkTpsaegsmyX316zOBhzGRYjKbutTYwKBgQCm7phr9XdFW5Vh+XR90mVs483nrLmMiDKg7YKxSLJ8amiDjzPejCn7i95Hah08P+2MIZLIPbh2VLacczR6ltRRzN5bg5etFuqSgfkuHyxpoDmpjbe08+Q2h8JBYqcC5Nhv1AKU4iOUhVLHo/FBAQliMcGc/J3eiYTFC7EsNx382QKBgClb20doe7cttgFTXswBvaUmfFm45kmla924B7SpvrQpDD/f+VDtDZRp05fGmxuduSjYdtA3aVtpLiTwWu22OUUvZZqHDGruYOO4Hvdz23mL5b4ayqImCwoNU4bAZIc9v18p/UNf3/55NNE3oGcf/bev9rH2OjCQ4nM+Ktwhg8CFAoGACSgvbkShzUkv0ZcIf9ppu+ZnJh1AdGgINvGwaJ8vQ0nm/8h8NOoFZ4oNoGc+wU5Ubops7dUM6FjPR5e+OjdJ4E7Xp7d5O4J1TaIZlCEbo5OpdhaTDDcQvrkFu+Z4eN0qzj+YAKjDAOOrXc4tbr5q0FsgXscwtcNfaBuzFVTUrUkCgYEAwzPnMNhWG3zOWLUs2QFA2GP4Y+J8cpUYfj6pbKKzeLwyG9qBwF1NJpN8m+q9q7V9P2LY+9Lp9e1mGsGeqt5HMEA3P6vIpcqLJLqE/4PBLLRzfccTcmqb1m71+erxTRhHBRkGS+I7dZEb3olQfnS1Y1tpMBxiwYwR3LW4oXuJwj8=");
73 config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB"); 73 config.setAlipayPublicKey("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnq90KnF4dTnlzzmxpujbI05OYqi5WxAS6cL0gnZFv2gK51HExF8v/BaP7P979PhFMgWTqmOOI+Dtno5s+yD09XTY1WkshbLk6i4g2Xlr8fyW9ODnkU88RI2w9UdPhQU4cPPwBNlrsYhKkVK2OxwM3kFqjoBBY0CZoZCsSQ3LDH5WeZqPArlsS6xa2zqJBuuoKjMrdpELl3eXSjP8K54eDJCbeetCZNKWLL3DPahTPB7LZikfYmslb0QUvCgGapD0xkS7eVq70NaL1G57MWABs4tbfWgxike4Daj3EfUrzIVspQxj7w8HEj9WozJPgL88kSJSits0pqD3n5r8HSuseQIDAQAB");
74 AlipayWapPayClient client = new AlipayWapPayClient(1L, "biubiubiu", config); 74 AlipayWapPayClient client = new AlipayWapPayClient(1L, "biubiubiu", config);
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXCodeMapping.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
  2 +
  3 +import cn.iocoder.yudao.framework.common.exception.ErrorCode;
  4 +import cn.iocoder.yudao.framework.common.exception.enums.GlobalErrorCodeConstants;
  5 +import cn.iocoder.yudao.framework.pay.core.client.AbstractPayCodeMapping;
  6 +
  7 +import java.util.Objects;
  8 +
  9 +import static cn.iocoder.yudao.framework.pay.core.enums.PayFrameworkErrorCodeConstants.*;
  10 +
  11 +/**
  12 + * 微信支付 PayCodeMapping 实现类
  13 + *
  14 + * @author 芋道源码
  15 + */
  16 +public class WXCodeMapping extends AbstractPayCodeMapping {
  17 +
  18 + /**
  19 + * 错误码 - 成功
  20 + * 由于 weixin-java-pay 封装的 Result 未返回 code,所以自己定义下
  21 + */
  22 + public static final String CODE_SUCCESS = "SUCCESS";
  23 + /**
  24 + * 错误提示 - 成功
  25 + */
  26 + public static final String MESSAGE_SUCCESS = "成功";
  27 +
  28 + @Override
  29 + protected ErrorCode apply0(String apiCode, String apiMsg) {
  30 + if (Objects.equals(apiCode, CODE_SUCCESS)) {
  31 + return GlobalErrorCodeConstants.SUCCESS;
  32 + }
  33 + if (Objects.equals(apiCode, "FAIL")) {
  34 + if (Objects.equals(apiMsg, "AppID不存在,请检查后再试")) {
  35 + return PAY_CONFIG_APP_ID_ERROR;
  36 + }
  37 + if (Objects.equals(apiMsg, "签名错误,请检查后再试")
  38 + || Objects.equals(apiMsg, "签名错误")) {
  39 + return PAY_CONFIG_SIGN_ERROR;
  40 + }
  41 + }
  42 + if (Objects.equals(apiCode, "PARAM_ERROR")) {
  43 + if (Objects.equals(apiMsg, "无效的openid")) {
  44 + return PAY_OPENID_ERROR;
  45 + }
  46 + }
  47 + return null;
  48 + }
  49 +
  50 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPayClientConfig.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
  2 +
  3 +import cn.hutool.core.io.IoUtil;
  4 +import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig;
  5 +import lombok.Data;
  6 +
  7 +import java.io.FileInputStream;
  8 +import java.io.FileNotFoundException;
  9 +
  10 +// TODO 芋艿:参数校验
  11 +/**
  12 + * 微信支付的 PayClientConfig 实现类
  13 + * 属性主要来自 {@link com.github.binarywang.wxpay.config.WxPayConfig} 的必要属性
  14 + *
  15 + * @author 芋道源码
  16 + */
  17 +@Data
  18 +public class WXPayClientConfig implements PayClientConfig {
  19 +
  20 + // TODO 芋艿:V2 or V3 客户端
  21 + /**
  22 + * API 版本 - V2
  23 + *
  24 + * https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=4_1
  25 + */
  26 + public static final String API_VERSION_V2 = "v2";
  27 + /**
  28 + * API 版本 - V3
  29 + *
  30 + * https://pay.weixin.qq.com/wiki/doc/apiv3/wechatpay/wechatpay-1.shtml
  31 + */
  32 + public static final String API_VERSION_V3 = "v3";
  33 +
  34 + /**
  35 + * 公众号或者小程序的 appid
  36 + */
  37 + private String appId;
  38 + /**
  39 + * 商户号
  40 + */
  41 + private String mchId;
  42 + /**
  43 + * API 版本
  44 + */
  45 + private String apiVersion;
  46 +
  47 + // ========== V2 版本的参数 ==========
  48 +
  49 + /**
  50 + * 商户密钥
  51 + */
  52 + private String mchKey;
  53 +// /**
  54 +// * apiclient_cert.p12 证书文件的绝对路径或者以 classpath: 开头的类路径.
  55 +// * 对应的字符串
  56 +// *
  57 +// * 注意,可通过 {@link #main(String[])} 读取
  58 +// */
  59 +// private String keyContent;
  60 +
  61 + // ========== V3 版本的参数 ==========
  62 + /**
  63 + * apiclient_key.pem 证书文件的绝对路径或者以 classpath: 开头的类路径.
  64 + * 对应的字符串
  65 + *
  66 + * 注意,可通过 {@link #main(String[])} 读取
  67 + */
  68 + private String privateKeyContent;
  69 + /**
  70 + * apiclient_cert.pem 证书文件的绝对路径或者以 classpath: 开头的类路径.
  71 + * 对应的字符串
  72 + *
  73 + * 注意,可通过 {@link #main(String[])} 读取
  74 + */
  75 + private String privateCertContent;
  76 + /**
  77 + * apiV3 秘钥值
  78 + */
  79 + private String apiV3Key;
  80 +
  81 + public static void main(String[] args) throws FileNotFoundException {
  82 + String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.p12";
  83 +// String path = "/Users/yunai/Downloads/wx_pay/apiclient_key.pem";
  84 +// String path = "/Users/yunai/Downloads/wx_pay/apiclient_cert.pem";
  85 + System.out.println(IoUtil.readUtf8(new FileInputStream(path)));
  86 + }
  87 +
  88 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
  2 +
  3 +import cn.hutool.core.bean.BeanUtil;
  4 +import cn.hutool.core.date.DateUtil;
  5 +import cn.hutool.core.io.IoUtil;
  6 +import cn.hutool.core.util.StrUtil;
  7 +import cn.iocoder.yudao.framework.common.pojo.CommonResult;
  8 +import cn.iocoder.yudao.framework.common.util.io.FileUtils;
  9 +import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult;
  10 +import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
  11 +import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient;
  12 +import com.github.binarywang.wxpay.bean.order.WxPayMpOrderResult;
  13 +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderRequest;
  14 +import com.github.binarywang.wxpay.bean.request.WxPayUnifiedOrderV3Request;
  15 +import com.github.binarywang.wxpay.bean.result.WxPayUnifiedOrderV3Result;
  16 +import com.github.binarywang.wxpay.bean.result.enums.TradeTypeEnum;
  17 +import com.github.binarywang.wxpay.config.WxPayConfig;
  18 +import com.github.binarywang.wxpay.constant.WxPayConstants;
  19 +import com.github.binarywang.wxpay.exception.WxPayException;
  20 +import com.github.binarywang.wxpay.service.WxPayService;
  21 +import com.github.binarywang.wxpay.service.impl.WxPayServiceImpl;
  22 +import lombok.extern.slf4j.Slf4j;
  23 +
  24 +import java.io.FileInputStream;
  25 +import java.io.FileNotFoundException;
  26 +
  27 +import static cn.hutool.core.util.ObjectUtil.defaultIfNull;
  28 +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
  29 +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.CODE_SUCCESS;
  30 +import static cn.iocoder.yudao.framework.pay.core.client.impl.wx.WXCodeMapping.MESSAGE_SUCCESS;
  31 +
  32 +/**
  33 + * 微信支付(公众号)的 PayClient 实现类
  34 + *
  35 + * @author 芋道源码
  36 + */
  37 +@Slf4j
  38 +public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
  39 +
  40 + private WxPayService client;
  41 +
  42 + public WXPubPayClient(Long channelId, String channelCode, WXPayClientConfig config) {
  43 + super(channelId, channelCode, config, new WXCodeMapping());
  44 + }
  45 +
  46 + @Override
  47 + protected void doInit() {
  48 + WxPayConfig payConfig = new WxPayConfig();
  49 + BeanUtil.copyProperties(config, payConfig, "keyContent");
  50 + payConfig.setTradeType(WxPayConstants.TradeType.JSAPI); // 设置使用 JS API 支付方式
  51 +// if (StrUtil.isNotEmpty(config.getKeyContent())) {
  52 +// payConfig.setKeyContent(config.getKeyContent().getBytes(StandardCharsets.UTF_8));
  53 +// }
  54 + if (StrUtil.isNotEmpty(config.getPrivateKeyContent())) {
  55 + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
  56 + payConfig.setPrivateKeyPath(FileUtils.createTempFile(config.getPrivateKeyContent()).getPath());
  57 + }
  58 + if (StrUtil.isNotEmpty(config.getPrivateCertContent())) {
  59 + // weixin-pay-java 存在 BUG,无法直接设置内容,所以创建临时文件来解决
  60 + payConfig.setPrivateCertPath(FileUtils.createTempFile(config.getPrivateCertContent()).getPath());
  61 + }
  62 + // 真实客户端
  63 + this.client = new WxPayServiceImpl();
  64 + client.setConfig(payConfig);
  65 + }
  66 +
  67 + @Override
  68 + public CommonResult<WxPayMpOrderResult> unifiedOrder(PayOrderUnifiedReqDTO reqDTO) {
  69 + WxPayMpOrderResult response;
  70 + try {
  71 + switch (config.getApiVersion()) {
  72 + case WXPayClientConfig.API_VERSION_V2:
  73 + response = this.unifiedOrderV2(reqDTO);
  74 + break;
  75 + case WXPayClientConfig.API_VERSION_V3:
  76 + WxPayUnifiedOrderV3Result.JsapiResult responseV3 = this.unifiedOrderV3(reqDTO);
  77 + // 将 V3 的结果,统一转换成 V2。返回的字段是一致的
  78 + response = new WxPayMpOrderResult();
  79 + BeanUtil.copyProperties(responseV3, response, true);
  80 + break;
  81 + default:
  82 + throw new IllegalArgumentException(String.format("未知的 API 版本(%s)", config.getApiVersion()));
  83 + }
  84 + } catch (WxPayException e) {
  85 + log.error("[unifiedOrder][request({}) 发起支付失败,原因({})]", toJsonString(reqDTO), e);
  86 + return PayCommonResult.build(defaultIfNull(e.getErrCode(), e.getReturnCode()),
  87 + defaultIfNull(e.getErrCodeDes(), e.getReturnMsg()),null, codeMapping);
  88 + }
  89 + return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping);
  90 + }
  91 +
  92 + private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
  93 + // 构建 WxPayUnifiedOrderRequest 对象
  94 + WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder()
  95 + .outTradeNo(reqDTO.getMerchantOrderId())
  96 + // TODO 芋艿:貌似没 title?
  97 + .body(reqDTO.getBody())
  98 + .totalFee(reqDTO.getAmount()) // 单位分
  99 + .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss"))
  100 + .spbillCreateIp(reqDTO.getClientIp())
  101 + .openid("ockUAwIZ-0OeMZl9ogcZ4ILrGba0") // TODO 芋艿:先随便写死
  102 + .notifyUrl(reqDTO.getNotifyUrl())
  103 + .build();
  104 + // 执行请求
  105 + return client.createOrder(request);
  106 + }
  107 +
  108 + private WxPayUnifiedOrderV3Result.JsapiResult unifiedOrderV3(PayOrderUnifiedReqDTO reqDTO) throws WxPayException {
  109 + // 构建 WxPayUnifiedOrderRequest 对象
  110 + WxPayUnifiedOrderV3Request request = new WxPayUnifiedOrderV3Request();
  111 + request.setOutTradeNo(reqDTO.getMerchantOrderId());
  112 + // TODO 芋艿:貌似没 title?
  113 + request.setDescription(reqDTO.getBody());
  114 + request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount())); // 单位分
  115 + request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss"));
  116 + request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid("ockUAwIZ-0OeMZl9ogcZ4ILrGba0")); // TODO 芋艿:先随便写死
  117 + request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getClientIp()));
  118 + request.setNotifyUrl(reqDTO.getNotifyUrl());
  119 + // 执行请求
  120 + return client.createOrderV3(TradeTypeEnum.JSAPI, request);
  121 + }
  122 +
  123 + public static void main(String[] args) throws FileNotFoundException {
  124 + WXPayClientConfig config = new WXPayClientConfig();
  125 + config.setAppId("wx041349c6f39b268b");
  126 + config.setMchId("1545083881");
  127 + config.setMchKey("0alL64UDQdlCwiKZ73ib7ypaIjMns06p");
  128 + config.setApiVersion(WXPayClientConfig.API_VERSION_V3);
  129 +// config.setKeyContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_cert.p12")));
  130 + config.setPrivateKeyContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_key.pem")));
  131 + config.setPrivateCertContent(IoUtil.readUtf8(new FileInputStream("/Users/yunai/Downloads/wx_pay/apiclient_cert.pem")));
  132 + config.setApiV3Key("joerVi8y5DJ3o4ttA0o1uH47Xz1u2Ase");
  133 +
  134 + WXPubPayClient client = new WXPubPayClient(1L, "biu", config);
  135 + client.init();
  136 +
  137 + PayOrderUnifiedReqDTO reqDTO = new PayOrderUnifiedReqDTO();
  138 + reqDTO.setAmount(123);
  139 + reqDTO.setSubject("IPhone 13");
  140 + reqDTO.setBody("biubiubiu");
  141 + reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis()));
  142 + reqDTO.setClientIp("127.0.0.1");
  143 + reqDTO.setNotifyUrl("http://127.0.0.1:8080");
  144 + CommonResult<WxPayMpOrderResult> result = client.unifiedOrder(reqDTO);
  145 + System.out.println(result);
  146 + }
  147 +
  148 +}
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayFrameworkErrorCodeConstants.java
@@ -11,36 +11,15 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode; @@ -11,36 +11,15 @@ import cn.iocoder.yudao.framework.common.exception.ErrorCode;
11 */ 11 */
12 public interface PayFrameworkErrorCodeConstants { 12 public interface PayFrameworkErrorCodeConstants {
13 13
14 - ErrorCode PAY_UNKNOWN = new ErrorCode(2001000000, "未知错误,需要解析");  
15 -  
16 -// // ========== 权限 / 限流等相关 2001000100 ==========  
17 -//  
18 -// ErrorCode SMS_PERMISSION_DENY = new ErrorCode(2001000100, "没有发送短信的权限");  
19 -// // 云片:可以配置 IP 白名单,只有在白名单中才可以发送短信  
20 -// ErrorCode SMS_IP_DENY = new ErrorCode(2001000100, "IP 不允许发送短信");  
21 -//  
22 -// // 阿里云:将短信发送频率限制在正常的业务限流范围内。默认短信验证码:使用同一签名,对同一个手机号验证码,支持 1 条 / 分钟,5 条 / 小时,累计 10 条 / 天。  
23 -// ErrorCode SMS_SEND_BUSINESS_LIMIT_CONTROL = new ErrorCode(2001000102, "指定手机的发送限流");  
24 -// // 阿里云:已经达到您在控制台设置的短信日发送量限额值。在国内消息设置 > 安全设置,修改发送总量阈值。  
25 -// ErrorCode SMS_SEND_DAY_LIMIT_CONTROL = new ErrorCode(2001000103, "每天的发送限流");  
26 -//  
27 -// ErrorCode SMS_SEND_CONTENT_INVALID = new ErrorCode(2001000104, "短信内容有敏感词");  
28 -//  
29 -// // ========== 模板相关 2001000200 ==========  
30 -// ErrorCode SMS_TEMPLATE_INVALID = new ErrorCode(2001000200, "短信模板不合法"); // 包括短信模板不存在  
31 -// ErrorCode SMS_TEMPLATE_PARAM_ERROR = new ErrorCode(2001000201, "模板参数不正确");  
32 -//  
33 -// // ========== 签名相关 2001000300 ==========  
34 -// ErrorCode SMS_SIGN_INVALID = new ErrorCode(2001000300, "短信签名不可用");  
35 -//  
36 -// // ========== 账户相关 2001000400 ==========  
37 -// ErrorCode SMS_ACCOUNT_MONEY_NOT_ENOUGH = new ErrorCode(2001000400, "账户余额不足");  
38 -// ErrorCode SMS_ACCOUNT_INVALID = new ErrorCode(2001000401, "apiKey 不存在");  
39 -//  
40 -// // ========== 其它相关 2001000900 开头 ==========  
41 -// ErrorCode SMS_API_PARAM_ERROR = new ErrorCode(2001000900, "请求参数缺失");  
42 -// ErrorCode SMS_MOBILE_INVALID = new ErrorCode(2001000901, "手机格式不正确");  
43 -// ErrorCode SMS_MOBILE_BLACK = new ErrorCode(2001000902, "手机号在黑名单中"); 14 + ErrorCode PAY_UNKNOWN = new ErrorCode(2002000000, "未知错误,需要解析");
  15 +
  16 + // ========== 配置相关相关 2002000100 ==========
  17 + ErrorCode PAY_CONFIG_APP_ID_ERROR = new ErrorCode(2002000100, "支付渠道 AppId 不正确");
  18 + ErrorCode PAY_CONFIG_SIGN_ERROR = new ErrorCode(2002000100, "签名错误"); // 例如说,微信支付,配置错了 mchId 或者 mchKey
  19 +
  20 +
  21 + // ========== 其它相关 2002000900 开头 ==========
  22 + ErrorCode PAY_OPENID_ERROR = new ErrorCode(2002000900, "无效的 openid"); // 例如说,微信 openid 未授权过
44 23
45 ErrorCode EXCEPTION = new ErrorCode(2002000999, "调用异常"); 24 ErrorCode EXCEPTION = new ErrorCode(2002000999, "调用异常");
46 25