Commit 20628987c97fc7253105860f6913e18f16e7740b

Authored by YunaiV
1 parent 1b0aaec9

增加支付回调地址的配置

yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayOrderCoreServiceImpl.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl;
2 2  
3 3 import cn.hutool.core.date.DateUtil;
4 4 import cn.hutool.core.util.RandomUtil;
  5 +import cn.hutool.core.util.StrUtil;
5 6 import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayOrderCoreConvert;
6 7 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
7 8 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO;
... ... @@ -19,6 +20,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmit
19 20 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitRespDTO;
20 21 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
21 22 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
  23 +import cn.iocoder.yudao.framework.pay.config.PayProperties;
22 24 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
23 25 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
24 26 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
... ... @@ -44,6 +46,9 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
44 46 public class PayOrderCoreServiceImpl implements PayOrderCoreService {
45 47  
46 48 @Resource
  49 + private PayProperties payProperties;
  50 +
  51 + @Resource
47 52 private PayAppCoreService payAppCoreService;
48 53 @Resource
49 54 private PayChannelCoreService payChannelCoreService;
... ... @@ -125,7 +130,7 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
125 130 // 商户相关字段
126 131 unifiedOrderReqDTO.setMerchantOrderId(order.getMerchantOrderId())
127 132 .setSubject(order.getSubject()).setBody(order.getBody())
128   - .setNotifyUrl(app.getPayNotifyUrl());
  133 + .setNotifyUrl(genChannelPayNotifyUrl(reqDTO.getChannelCode()));
129 134 // 订单相关字段
130 135 unifiedOrderReqDTO.setAmount(order.getAmount()).setExpireTime(order.getExpireTime());
131 136 CommonResult<?> unifiedOrderResult = client.unifiedOrder(unifiedOrderReqDTO);
... ... @@ -137,6 +142,17 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
137 142 .setInvokeResponse(unifiedOrderResult.getData());
138 143 }
139 144  
  145 + /**
  146 + * 根据支付渠道的编码,生成支付渠道的回调地址
  147 + *
  148 + * @param channelCode 支付渠道的编码
  149 + * @return 支付渠道的回调地址
  150 + */
  151 + private String genChannelPayNotifyUrl(String channelCode) {
  152 + // _ 转化为 - 的原因,是因为 URL 我们统一采用中划线的原则
  153 + return payProperties.getPayNotifyUrl() + "/" + StrUtil.replace(channelCode, "_", "-");
  154 + }
  155 +
140 156 private String generateOrderExtensionNo() {
141 157 // wx
142 158 // 2014
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java 0 → 100644
  1 +package cn.iocoder.yudao.framework.pay.config;
  2 +
  3 +import lombok.Data;
  4 +import org.hibernate.validator.constraints.URL;
  5 +import org.springframework.boot.context.properties.ConfigurationProperties;
  6 +import org.springframework.validation.annotation.Validated;
  7 +
  8 +import javax.validation.constraints.NotEmpty;
  9 +import javax.validation.constraints.NotNull;
  10 +import java.time.Duration;
  11 +
  12 +@ConfigurationProperties(prefix = "yudao.pay")
  13 +@Validated
  14 +@Data
  15 +public class PayProperties {
  16 +
  17 + /**
  18 + * 支付回调地址
  19 + * 注意,支付渠道统一回调到 payNotifyUrl 地址,由支付模块统一处理;然后,自己的支付模块,在回调 PayAppDO.payNotifyUrl 地址
  20 + */
  21 + @NotEmpty(message = "支付回调地址不能为空")
  22 + @URL(message = "支付回调地址的格式必须是 URL")
  23 + private String payNotifyUrl;
  24 + /**
  25 + * 退款回调地址
  26 + * 注意点,同 {@link #payNotifyUrl} 属性
  27 + */
  28 + @NotNull(message = "短信发送频率不能为空")
  29 + @URL(message = "退款回调地址的格式必须是 URL")
  30 + private String refundNotifyUrl;
  31 +
  32 +}
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/YudaoPayAutoConfiguration.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.config;
2 2  
3 3 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
4 4 import cn.iocoder.yudao.framework.pay.core.client.impl.PayClientFactoryImpl;
  5 +import org.springframework.boot.context.properties.EnableConfigurationProperties;
5 6 import org.springframework.context.annotation.Bean;
6 7 import org.springframework.context.annotation.Configuration;
7 8  
... ... @@ -10,7 +11,7 @@ import org.springframework.context.annotation.Configuration;
10 11 *
11 12 * @author 芋道源码
12 13 */
13   -@Configuration
  14 +@EnableConfigurationProperties(PayProperties.class)
14 15 public class YudaoPayAutoConfiguration {
15 16  
16 17 @Bean
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayOrderUnifiedReqDTO.java
... ... @@ -8,6 +8,7 @@ import javax.validation.constraints.DecimalMin;
8 8 import javax.validation.constraints.NotEmpty;
9 9 import javax.validation.constraints.NotNull;
10 10 import java.util.Date;
  11 +import java.util.Map;
11 12  
12 13 /**
13 14 * 统一下单 Request DTO
... ... @@ -63,6 +64,11 @@ public class PayOrderUnifiedReqDTO {
63 64 private Date expireTime;
64 65  
65 66 // ========== 拓展参数 ==========
66   - // TODO 芋艿:待完善
  67 + /**
  68 + * 支付渠道的额外参数
  69 + *
  70 + * 例如说,微信公众号需要传递 openid 参数
  71 + */
  72 + private Map<String, String> channelExtras;
67 73  
68 74 }
... ...
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java
... ... @@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
2 2  
3 3 import cn.hutool.core.bean.BeanUtil;
4 4 import cn.hutool.core.date.DateUtil;
  5 +import cn.hutool.core.map.MapUtil;
5 6 import cn.hutool.core.util.StrUtil;
6 7 import cn.iocoder.yudao.framework.common.util.io.FileUtils;
7 8 import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
... ... @@ -94,7 +95,7 @@ public class WXPubPayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
94 95 .totalFee(reqDTO.getAmount().intValue()) // 单位分
95 96 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss"))
96 97 .spbillCreateIp(reqDTO.getUserIp())
97   - .openid("ockUAwIZ-0OeMZl9ogcZ4ILrGba0") // TODO 芋艿:先随便写死
  98 + .openid(getOpenid(reqDTO))
98 99 .notifyUrl(reqDTO.getNotifyUrl())
99 100 .build();
100 101 // 执行请求
... ... @@ -109,11 +110,20 @@ public class WXPubPayClient extends AbstractPayClient&lt;WXPayClientConfig&gt; {
109 110 request.setDescription(reqDTO.getBody());
110 111 request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分
111 112 request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss"));
112   - request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid("ockUAwIZ-0OeMZl9ogcZ4ILrGba0")); // TODO 芋艿:先随便写死
  113 + request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid(getOpenid(reqDTO)));
113 114 request.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
114 115 request.setNotifyUrl(reqDTO.getNotifyUrl());
115 116 // 执行请求
116 117 return client.createOrderV3(TradeTypeEnum.JSAPI, request);
117 118 }
118 119  
  120 +
  121 + private static String getOpenid(PayOrderUnifiedReqDTO reqDTO) {
  122 + String openid = MapUtil.getStr(reqDTO.getChannelExtras(), "openid");
  123 + if (StrUtil.isEmpty(openid)) {
  124 + throw new IllegalArgumentException("支付请求的 openid 不能为空!");
  125 + }
  126 + return openid;
  127 + }
  128 +
119 129 }
... ...
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/controller/order/PayOrderController.java
... ... @@ -32,7 +32,7 @@ public class PayOrderController {
32 32 @PostMapping("/submit")
33 33 @ApiOperation("提交支付订单")
34 34 // @PreAuthenticated // TODO 暂时不加登陆验证,前端暂时没做好
35   - public CommonResult<PayOrderSubmitRespVO> submit(@RequestBody PayOrderSubmitReqVO reqVO) {
  35 + public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) {
36 36 // 获得订单
37 37 PayOrderDO payOrder = payOrderCoreService.getPayOrder(reqVO.getId());
38 38  
... ... @@ -47,4 +47,13 @@ public class PayOrderController {
47 47 return success(PayOrderSubmitRespVO.builder().invokeResponse(respDTO.getInvokeResponse()).build());
48 48 }
49 49  
  50 + // ========== 支付渠道的回调 ==========
  51 +
  52 + @PostMapping("/notify/wx-pub")
  53 + @ApiOperation("通知微信公众号的结果")
  54 + public String notifyWxPayOrder(@RequestBody String xmlData) {
  55 + System.out.println(xmlData);
  56 + return "success";
  57 + }
  58 +
50 59 }
... ...
yudao-user-server/src/main/resources/application-dev.yaml
... ... @@ -138,3 +138,6 @@ yudao:
138 138 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
139 139 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
140 140 demo: true # 开启演示模式
  141 + pay:
  142 + pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
  143 + refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
... ...
yudao-user-server/src/main/resources/application-local.yaml
... ... @@ -152,3 +152,6 @@ yudao:
152 152 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
153 153 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
154 154 demo: false # 关闭演示模式
  155 + pay:
  156 + pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
  157 + refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify
... ...
yudao-user-server/src/main/resources/static/pay.html
... ... @@ -18,6 +18,8 @@
18 18 let payOrderId = undefined;
19 19 // let server = 'http://127.0.0.1:28080';
20 20 let server = 'http://niubi.natapp1.cc';
  21 + // TODO openid
  22 + let openid = "ockUAwIZ-0OeMZl9ogcZ4ILrGba0";
21 23 $(function() {
22 24 // 获得 JsapiTicket
23 25 // 参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档
... ... @@ -73,6 +75,8 @@
73 75 }
74 76  
75 77 // 提交支付
  78 + // 参考 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 文档
  79 + // 参考 https://segmentfault.com/a/1190000020704650 文档
76 80 $.ajax({
77 81 url: server + "/api/pay/order/submit",
78 82 method: 'POST',
... ... @@ -81,6 +85,9 @@
81 85 data: JSON.stringify({
82 86 "id": payOrderId,
83 87 "channelCode": 'wx_pub',
  88 + "channelExtras": {
  89 + "openid": openid
  90 + }
84 91 }),
85 92 success: function( result ) {
86 93 if (result.code !== 0) {
... ...