mall + pay:

1、调整 returnUrl 的实现
This commit is contained in:
YunaiV 2023-07-04 23:43:16 +08:00
parent 23e3c4d0d9
commit 20eb0a2a88
13 changed files with 33 additions and 79 deletions

View File

@ -23,13 +23,4 @@ public class PayProperties {
@URL(message = "回调地址的格式必须是 URL")
private String callbackUrl;
/**
* 回跳地址
*
* 实际上对应的 PayNotifyController returnCallback 方法的 URL
*/
@URL(message = "回跳地址的格式必须是 URL")
@NotEmpty(message = "回跳地址不能为空")
private String returnUrl;
}

View File

@ -1,7 +1,6 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.Method;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
@ -41,11 +40,11 @@ public class AlipayPcPayClient extends AbstractAlipayClient {
model.setTimeExpire(formatTime(reqDTO.getExpireTime()));
model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码. 目前 PC 支付场景下仅支持 FAST_INSTANT_TRADE_PAY
// 个性化的参数
// 参考 https://www.pingxx.com/api/支付渠道 extra 参数说明.html alipay_pc_direct 部分
model.setQrPayMode(MapUtil.getStr(reqDTO.getChannelExtras(), "qr_pay_mode"));
model.setQrcodeWidth(MapUtil.getLong(reqDTO.getChannelExtras(), "qr_code_width"));
// 支付宝 PC 支付有多种展示模式因此这里需要计算
String displayMode = getDisplayMode(reqDTO.getDisplayMode(), model.getQrPayMode());
// 如果想弄更多个性化的参数参考 https://www.pingxx.com/api/支付渠道 extra 参数说明.html alipay_pc_direct 部分进行拓展
model.setQrPayMode("2"); // 跳转模式 - 订单码效果参见https://help.pingxx.com/article/1137360/
// 支付宝 PC 支付有两种展示模式FORMURL
String displayMode = ObjectUtil.defaultIfNull(reqDTO.getDisplayMode(),
PayDisplayModeEnum.URL.getMode());
// 1.2 构建 AlipayTradePagePayRequest 请求
AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
@ -67,25 +66,4 @@ public class AlipayPcPayClient extends AbstractAlipayClient {
.setDisplayContent(response.getBody());
}
/**
* 获得最终的支付 UI 展示模式
*
* @param displayMode 前端传递的 UI 展示模式
* @param qrPayMode 前端传递的二维码模式
* @return 最终的支付 UI 展示模式
*/
private String getDisplayMode(String displayMode, String qrPayMode) {
// 1.1 支付宝二维码的前置模式
if (StrUtil.equalsAny(qrPayMode, "0", "1", "3", "4")) {
return PayDisplayModeEnum.IFRAME.getMode();
}
// 1.2 支付宝二维码的跳转模式
if (StrUtil.equals(qrPayMode, "2")) {
return PayDisplayModeEnum.URL.getMode();
}
// 2. 前端传递了 UI 展示模式
return displayMode != null ? displayMode :
PayDisplayModeEnum.URL.getMode(); // 模式使用 URL 跳转
}
}

View File

@ -1,6 +1,5 @@
package cn.iocoder.yudao.framework.pay.core.client.impl.alipay;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.http.Method;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO;
import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO;
@ -37,9 +36,8 @@ public class AlipayWapPayClient extends AbstractAlipayClient {
model.setTotalAmount(formatAmount(reqDTO.getAmount()));
model.setProductCode("QUICK_WAP_PAY"); // 销售产品码. 目前 Wap 支付场景下仅支持 QUICK_WAP_PAY
// 个性化的参数
// 支付宝 Wap 支付只有一种展示考虑到前端可能希望二维码扫描后手机打开
String displayMode = ObjectUtil.defaultIfNull(reqDTO.getDisplayMode(),
PayDisplayModeEnum.URL.getMode());
// 支付宝 Wap 支付只有一种展示URL
String displayMode = PayDisplayModeEnum.URL.getMode();
// 1.2 构建 AlipayTradeWapPayRequest 请求
AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest();

View File

@ -93,6 +93,10 @@
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.boot</groupId>
<artifactId>yudao-spring-boot-starter-biz-dict</artifactId>
</dependency>
</dependencies>

View File

@ -55,8 +55,10 @@ public class AppTradeOrderDetailRespVO {
@Schema(description = "付款超时时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime payExpireTime;
@Schema(description = "支付渠道", requiredMode = Schema.RequiredMode.REQUIRED, example = "wx_lite_pay")
@Schema(description = "支付渠道", example = "wx_lite_pay")
private String payChannelCode;
@Schema(description = "支付渠道名", example = "微信小程序支付")
private String payChannelName;
@Schema(description = "商品原价(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000")
private Integer totalPrice;

View File

@ -1,12 +1,15 @@
package cn.iocoder.yudao.module.trade.convert.order;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils;
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO;
import cn.iocoder.yudao.module.pay.enums.DictTypeConstants;
import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO;
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO;
@ -234,6 +237,9 @@ public interface TradeOrderConvert {
List<ProductPropertyValueDetailRespDTO> propertyValueDetails, TradeOrderProperties tradeOrderProperties) {
AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems);
orderVO.setPayExpireTime(addTime(tradeOrderProperties.getExpireTime()));
if (StrUtil.isNotEmpty(order.getPayChannelCode())) {
orderVO.setPayChannelName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.CHANNEL_CODE_TYPE, order.getPayChannelCode()));
}
// 处理商品属性
Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId);
for (int i = 0; i < orderItems.size(); i++) {

View File

@ -7,6 +7,8 @@ package cn.iocoder.yudao.module.pay.enums;
*/
public interface DictTypeConstants {
String CHANNEL_CODE_TYPE = "pay_channel_code_type"; // 支付-渠道名
String ORDER_STATUS = "pay_order_status"; // 支付-订单-订单状态
String ORDER_NOTIFY_STATUS = "pay_order_notify_status"; // 支付-订单-订单回调商户状态

View File

@ -6,8 +6,6 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory;
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.module.pay.dal.dataobject.merchant.PayChannelDO;
import cn.iocoder.yudao.module.pay.service.merchant.PayChannelService;
import cn.iocoder.yudao.module.pay.service.order.PayOrderService;
import cn.iocoder.yudao.module.pay.service.refund.PayRefundService;
import io.swagger.v3.oas.annotations.Operation;
@ -39,23 +37,6 @@ public class PayNotifyController {
@Resource
private PayClientFactory payClientFactory;
/**
* 统一的跳转页面支付宝跳转参数说明
*
* <a href="https://opendocs.alipay.com/open/203/105285#前台回跳参数说明">支付宝 - 前台回跳参数说明</a>
*
* @param channelId 渠道编号
* @return 返回跳转页面
*/
@GetMapping(value = "/return/{channelId}")
@Operation(summary = "渠道统一的支付成功返回地址")
@Deprecated // TODO yunai如果是 way 的情况应该是跳转回前端地址
public String returnCallback(@PathVariable("channelId") Long channelId,
@RequestParam Map<String, String> params) {
log.info("[returnCallback][app_id({}) 跳转]", params.get("app_id"));
return String.format("渠道[%s]支付成功", channelId);
}
/**
* 统一的渠道支付回调支付宝的退款回调
*

View File

@ -2,11 +2,10 @@ package cn.iocoder.yudao.module.pay.controller.admin.order.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import org.hibernate.validator.constraints.URL;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotNull;
import java.awt.*;
import java.util.Map;
@Schema(description = "管理后台 - 支付订单提交 Request VO")
@ -26,4 +25,9 @@ public class PayOrderSubmitReqVO {
@Schema(description = "展示模式", example = "url") // 参见 {@link PayDisplayModeEnum} 枚举如果不传递则每个支付渠道使用默认的方式
private String displayMode;
@Schema(description = "回跳地址")
@URL(message = "回跳地址的格式必须是 URL")
private String returnUrl;
}

View File

@ -5,6 +5,6 @@ Authorization: Bearer {{appToken}}
tenant-id: {{appTenentId}}
{
"id": 125,
"channelCode": "alipay_qr"
"id": 174,
"channelCode": "alipay_pc"
}

View File

@ -4,7 +4,6 @@ import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
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;
@ -44,7 +43,7 @@ import java.util.Collection;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.*;
import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString;
/**
* 支付订单 Service 实现类
@ -147,7 +146,7 @@ public class PayOrderServiceImpl implements PayOrderService {
.setMerchantOrderId(orderExtension.getNo()) // 注意此处使用的是 PayOrderExtensionDO.no 属性
.setSubject(order.getSubject()).setBody(order.getBody())
.setNotifyUrl(genChannelPayNotifyUrl(channel))
.setReturnUrl(genChannelReturnUrl(channel))
.setReturnUrl(reqVO.getReturnUrl())
// 订单相关字段
.setAmount(order.getAmount()).setExpireTime(order.getExpireTime());
PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO);
@ -183,15 +182,6 @@ public class PayOrderServiceImpl implements PayOrderService {
return channel;
}
/**
* 根据支付渠道的编码生成支付渠道的返回地址
* @param channel 支付渠道
* @return 支付成功返回的地址 配置地址 + "/" + channel id
*/
private String genChannelReturnUrl(PayChannelDO channel) {
return payProperties.getReturnUrl() + "/" + channel.getId();
}
/**
* 根据支付渠道的编码生成支付渠道的回调地址
*

View File

@ -168,7 +168,6 @@ yudao:
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
pay:
callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback
return-url: http://yunai.natapp1.cc/admin-api/pay/notify/return
demo: true # 开启演示模式
justauth:

View File

@ -195,7 +195,6 @@ yudao:
- ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求
pay:
callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback
return-url: http://yunai.natapp1.cc/admin-api/pay/notify/return
access-log: # 访问日志的配置项
enable: false
error-code: # 错误码相关配置项