浏览代码

增加支付回调地址的配置

YunaiV 3 年之前
父节点
当前提交
20628987c9

+ 17 - 1
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;
 
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.RandomUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayOrderCoreConvert;
 import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO;
 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
 import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitRespDTO;
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
+import cn.iocoder.yudao.framework.pay.config.PayProperties;
 import cn.iocoder.yudao.framework.pay.core.client.PayClient;
 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
 import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO;
@@ -43,6 +45,9 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
 @Slf4j
 public class PayOrderCoreServiceImpl implements PayOrderCoreService {
 
+    @Resource
+    private PayProperties payProperties;
+
     @Resource
     private PayAppCoreService payAppCoreService;
     @Resource
@@ -125,7 +130,7 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
         // 商户相关字段
         unifiedOrderReqDTO.setMerchantOrderId(order.getMerchantOrderId())
                 .setSubject(order.getSubject()).setBody(order.getBody())
-                .setNotifyUrl(app.getPayNotifyUrl());
+                .setNotifyUrl(genChannelPayNotifyUrl(reqDTO.getChannelCode()));
         // 订单相关字段
         unifiedOrderReqDTO.setAmount(order.getAmount()).setExpireTime(order.getExpireTime());
         CommonResult<?> unifiedOrderResult = client.unifiedOrder(unifiedOrderReqDTO);
@@ -137,6 +142,17 @@ public class PayOrderCoreServiceImpl implements PayOrderCoreService {
                 .setInvokeResponse(unifiedOrderResult.getData());
     }
 
+    /**
+     * 根据支付渠道的编码,生成支付渠道的回调地址
+     *
+     * @param channelCode 支付渠道的编码
+     * @return 支付渠道的回调地址
+     */
+    private String genChannelPayNotifyUrl(String channelCode) {
+        // _ 转化为 - 的原因,是因为 URL 我们统一采用中划线的原则
+        return payProperties.getPayNotifyUrl() + "/" + StrUtil.replace(channelCode, "_", "-");
+    }
+
     private String generateOrderExtensionNo() {
 //    wx
 //    2014

+ 32 - 0
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java

@@ -0,0 +1,32 @@
+package cn.iocoder.yudao.framework.pay.config;
+
+import lombok.Data;
+import org.hibernate.validator.constraints.URL;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.validation.annotation.Validated;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+import java.time.Duration;
+
+@ConfigurationProperties(prefix = "yudao.pay")
+@Validated
+@Data
+public class PayProperties {
+
+    /**
+     * 支付回调地址
+     * 注意,支付渠道统一回调到 payNotifyUrl 地址,由支付模块统一处理;然后,自己的支付模块,在回调 PayAppDO.payNotifyUrl 地址
+     */
+    @NotEmpty(message = "支付回调地址不能为空")
+    @URL(message = "支付回调地址的格式必须是 URL")
+    private String payNotifyUrl;
+    /**
+     * 退款回调地址
+     * 注意点,同 {@link #payNotifyUrl} 属性
+     */
+    @NotNull(message = "短信发送频率不能为空")
+    @URL(message = "退款回调地址的格式必须是 URL")
+    private String refundNotifyUrl;
+
+}

+ 2 - 1
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;
 
 import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
 import cn.iocoder.yudao.framework.pay.core.client.impl.PayClientFactoryImpl;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
 
@@ -10,7 +11,7 @@ import org.springframework.context.annotation.Configuration;
  *
  * @author 芋道源码
  */
-@Configuration
+@EnableConfigurationProperties(PayProperties.class)
 public class YudaoPayAutoConfiguration {
 
     @Bean

+ 7 - 1
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;
 import javax.validation.constraints.NotEmpty;
 import javax.validation.constraints.NotNull;
 import java.util.Date;
+import java.util.Map;
 
 /**
  * 统一下单 Request DTO
@@ -63,6 +64,11 @@ public class PayOrderUnifiedReqDTO {
     private Date expireTime;
 
     // ========== 拓展参数 ==========
-    // TODO 芋艿:待完善
+    /**
+     * 支付渠道的额外参数
+     *
+     * 例如说,微信公众号需要传递 openid 参数
+     */
+    private Map<String, String> channelExtras;
 
 }

+ 12 - 2
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;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.date.DateUtil;
+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.object.ObjectUtils;
@@ -94,7 +95,7 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
                 .totalFee(reqDTO.getAmount().intValue()) // 单位分
                 .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss"))
                 .spbillCreateIp(reqDTO.getUserIp())
-                .openid("ockUAwIZ-0OeMZl9ogcZ4ILrGba0") // TODO 芋艿:先随便写死
+                .openid(getOpenid(reqDTO))
                 .notifyUrl(reqDTO.getNotifyUrl())
                 .build();
         // 执行请求
@@ -109,11 +110,20 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
         request.setDescription(reqDTO.getBody());
         request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分
         request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss"));
-        request.setPayer(new WxPayUnifiedOrderV3Request.Payer().setOpenid("ockUAwIZ-0OeMZl9ogcZ4ILrGba0")); // TODO 芋艿:先随便写死
+        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;
+    }
+
 }

+ 10 - 1
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/controller/order/PayOrderController.java

@@ -32,7 +32,7 @@ public class PayOrderController {
     @PostMapping("/submit")
     @ApiOperation("提交支付订单")
 //    @PreAuthenticated // TODO 暂时不加登陆验证,前端暂时没做好
-    public CommonResult<PayOrderSubmitRespVO> submit(@RequestBody PayOrderSubmitReqVO reqVO) {
+    public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) {
         // 获得订单
         PayOrderDO payOrder = payOrderCoreService.getPayOrder(reqVO.getId());
 
@@ -47,4 +47,13 @@ public class PayOrderController {
         return success(PayOrderSubmitRespVO.builder().invokeResponse(respDTO.getInvokeResponse()).build());
     }
 
+    // ========== 支付渠道的回调 ==========
+
+    @PostMapping("/notify/wx-pub")
+    @ApiOperation("通知微信公众号的结果")
+    public String notifyWxPayOrder(@RequestBody String xmlData) {
+        System.out.println(xmlData);
+        return "success";
+    }
+
 }

+ 3 - 0
yudao-user-server/src/main/resources/application-dev.yaml

@@ -138,3 +138,6 @@ yudao:
       - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
       - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
   demo: true # 开启演示模式
+  pay:
+    pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
+    refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify

+ 3 - 0
yudao-user-server/src/main/resources/application-local.yaml

@@ -152,3 +152,6 @@ yudao:
       - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
       - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
   demo: false # 关闭演示模式
+  pay:
+    pay-notify-url: http://niubi.natapp1.cc/api/pay/order/notify
+    refund-notify-url: http://niubi.natapp1.cc/api/pay/refund/notify

+ 7 - 0
yudao-user-server/src/main/resources/static/pay.html

@@ -18,6 +18,8 @@
     let payOrderId = undefined;
     // let server = 'http://127.0.0.1:28080';
     let server = 'http://niubi.natapp1.cc';
+    // TODO openid
+    let openid = "ockUAwIZ-0OeMZl9ogcZ4ILrGba0";
     $(function() {
         // 获得 JsapiTicket
         // 参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档
@@ -73,6 +75,8 @@
         }
 
         // 提交支付
+        // 参考 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=7_7&index=6 文档
+        // 参考 https://segmentfault.com/a/1190000020704650 文档
         $.ajax({
             url: server + "/api/pay/order/submit",
             method: 'POST',
@@ -81,6 +85,9 @@
             data: JSON.stringify({
                 "id": payOrderId,
                 "channelCode": 'wx_pub',
+                "channelExtras": {
+                    "openid": openid
+                }
             }),
             success: function( result ) {
                 if (result.code !== 0) {