增加支付回调地址的配置

This commit is contained in:
YunaiV 2021-10-25 09:56:49 +08:00
parent 1b0aaec9ab
commit 20628987c9
9 changed files with 93 additions and 6 deletions

View File

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

View File

@ -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;
}

View File

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

View File

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

View File

@ -2,6 +2,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.wx;
import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil; import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.util.io.FileUtils; import cn.iocoder.yudao.framework.common.util.io.FileUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
@ -94,7 +95,7 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
.totalFee(reqDTO.getAmount().intValue()) // 单位分 .totalFee(reqDTO.getAmount().intValue()) // 单位分
.timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")) .timeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss"))
.spbillCreateIp(reqDTO.getUserIp()) .spbillCreateIp(reqDTO.getUserIp())
.openid("ockUAwIZ-0OeMZl9ogcZ4ILrGba0") // TODO 芋艿先随便写死 .openid(getOpenid(reqDTO))
.notifyUrl(reqDTO.getNotifyUrl()) .notifyUrl(reqDTO.getNotifyUrl())
.build(); .build();
// 执行请求 // 执行请求
@ -109,11 +110,20 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> {
request.setDescription(reqDTO.getBody()); request.setDescription(reqDTO.getBody());
request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分 request.setAmount(new WxPayUnifiedOrderV3Request.Amount().setTotal(reqDTO.getAmount().intValue())); // 单位分
request.setTimeExpire(DateUtil.format(reqDTO.getExpireTime(), "yyyyMMddHHmmss")); 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.setSceneInfo(new WxPayUnifiedOrderV3Request.SceneInfo().setPayerClientIp(reqDTO.getUserIp()));
request.setNotifyUrl(reqDTO.getNotifyUrl()); request.setNotifyUrl(reqDTO.getNotifyUrl());
// 执行请求 // 执行请求
return client.createOrderV3(TradeTypeEnum.JSAPI, request); 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;
}
} }

View File

@ -32,7 +32,7 @@ public class PayOrderController {
@PostMapping("/submit") @PostMapping("/submit")
@ApiOperation("提交支付订单") @ApiOperation("提交支付订单")
// @PreAuthenticated // TODO 暂时不加登陆验证前端暂时没做好 // @PreAuthenticated // TODO 暂时不加登陆验证前端暂时没做好
public CommonResult<PayOrderSubmitRespVO> submit(@RequestBody PayOrderSubmitReqVO reqVO) { public CommonResult<PayOrderSubmitRespVO> submitPayOrder(@RequestBody PayOrderSubmitReqVO reqVO) {
// 获得订单 // 获得订单
PayOrderDO payOrder = payOrderCoreService.getPayOrder(reqVO.getId()); PayOrderDO payOrder = payOrderCoreService.getPayOrder(reqVO.getId());
@ -47,4 +47,13 @@ public class PayOrderController {
return success(PayOrderSubmitRespVO.builder().invokeResponse(respDTO.getInvokeResponse()).build()); 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";
}
} }

View File

@ -138,3 +138,6 @@ yudao:
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
demo: true # 开启演示模式 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

View File

@ -152,3 +152,6 @@ yudao:
- ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求 - ${spring.boot.admin.context-path}/** # 不处理 Spring Boot Admin 的请求
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
demo: false # 关闭演示模式 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

View File

@ -18,6 +18,8 @@
let payOrderId = undefined; let payOrderId = undefined;
// let server = 'http://127.0.0.1:28080'; // let server = 'http://127.0.0.1:28080';
let server = 'http://niubi.natapp1.cc'; let server = 'http://niubi.natapp1.cc';
// TODO openid
let openid = "ockUAwIZ-0OeMZl9ogcZ4ILrGba0";
$(function() { $(function() {
// 获得 JsapiTicket // 获得 JsapiTicket
// 参考 https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 文档 // 参考 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({ $.ajax({
url: server + "/api/pay/order/submit", url: server + "/api/pay/order/submit",
method: 'POST', method: 'POST',
@ -81,6 +85,9 @@
data: JSON.stringify({ data: JSON.stringify({
"id": payOrderId, "id": payOrderId,
"channelCode": 'wx_pub', "channelCode": 'wx_pub',
"channelExtras": {
"openid": openid
}
}), }),
success: function( result ) { success: function( result ) {
if (result.code !== 0) { if (result.code !== 0) {