Commit 20eb0a2a888ad7aa654108a595a863371c4ab3ab
1 parent
23e3c4d0
mall + pay:
1、调整 returnUrl 的实现
Showing
13 changed files
with
33 additions
and
79 deletions
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/config/PayProperties.java
| ... | ... | @@ -23,13 +23,4 @@ public class PayProperties { |
| 23 | 23 | @URL(message = "回调地址的格式必须是 URL") |
| 24 | 24 | private String callbackUrl; |
| 25 | 25 | |
| 26 | - /** | |
| 27 | - * 回跳地址 | |
| 28 | - * | |
| 29 | - * 实际上,对应的 PayNotifyController 的 returnCallback 方法的 URL | |
| 30 | - */ | |
| 31 | - @URL(message = "回跳地址的格式必须是 URL") | |
| 32 | - @NotEmpty(message = "回跳地址不能为空") | |
| 33 | - private String returnUrl; | |
| 34 | - | |
| 35 | 26 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayPcPayClient.java
| 1 | 1 | package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.map.MapUtil; | |
| 4 | -import cn.hutool.core.util.StrUtil; | |
| 3 | +import cn.hutool.core.util.ObjectUtil; | |
| 5 | 4 | import cn.hutool.http.Method; |
| 6 | 5 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; |
| 7 | 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; |
| ... | ... | @@ -41,11 +40,11 @@ public class AlipayPcPayClient extends AbstractAlipayClient { |
| 41 | 40 | model.setTimeExpire(formatTime(reqDTO.getExpireTime())); |
| 42 | 41 | model.setProductCode("FAST_INSTANT_TRADE_PAY"); // 销售产品码. 目前 PC 支付场景下仅支持 FAST_INSTANT_TRADE_PAY |
| 43 | 42 | // ② 个性化的参数 |
| 44 | - // 参考 https://www.pingxx.com/api/支付渠道 extra 参数说明.html 的 alipay_pc_direct 部分 | |
| 45 | - model.setQrPayMode(MapUtil.getStr(reqDTO.getChannelExtras(), "qr_pay_mode")); | |
| 46 | - model.setQrcodeWidth(MapUtil.getLong(reqDTO.getChannelExtras(), "qr_code_width")); | |
| 47 | - // ③ 支付宝 PC 支付有多种展示模式,因此这里需要计算 | |
| 48 | - String displayMode = getDisplayMode(reqDTO.getDisplayMode(), model.getQrPayMode()); | |
| 43 | + // 如果想弄更多个性化的参数,可参考 https://www.pingxx.com/api/支付渠道 extra 参数说明.html 的 alipay_pc_direct 部分进行拓展 | |
| 44 | + model.setQrPayMode("2"); // 跳转模式 - 订单码,效果参见:https://help.pingxx.com/article/1137360/ | |
| 45 | + // ③ 支付宝 PC 支付有两种展示模式:FORM、URL | |
| 46 | + String displayMode = ObjectUtil.defaultIfNull(reqDTO.getDisplayMode(), | |
| 47 | + PayDisplayModeEnum.URL.getMode()); | |
| 49 | 48 | |
| 50 | 49 | // 1.2 构建 AlipayTradePagePayRequest 请求 |
| 51 | 50 | AlipayTradePagePayRequest request = new AlipayTradePagePayRequest(); |
| ... | ... | @@ -67,25 +66,4 @@ public class AlipayPcPayClient extends AbstractAlipayClient { |
| 67 | 66 | .setDisplayContent(response.getBody()); |
| 68 | 67 | } |
| 69 | 68 | |
| 70 | - /** | |
| 71 | - * 获得最终的支付 UI 展示模式 | |
| 72 | - * | |
| 73 | - * @param displayMode 前端传递的 UI 展示模式 | |
| 74 | - * @param qrPayMode 前端传递的二维码模式 | |
| 75 | - * @return 最终的支付 UI 展示模式 | |
| 76 | - */ | |
| 77 | - private String getDisplayMode(String displayMode, String qrPayMode) { | |
| 78 | - // 1.1 支付宝二维码的前置模式 | |
| 79 | - if (StrUtil.equalsAny(qrPayMode, "0", "1", "3", "4")) { | |
| 80 | - return PayDisplayModeEnum.IFRAME.getMode(); | |
| 81 | - } | |
| 82 | - // 1.2 支付宝二维码的跳转模式 | |
| 83 | - if (StrUtil.equals(qrPayMode, "2")) { | |
| 84 | - return PayDisplayModeEnum.URL.getMode(); | |
| 85 | - } | |
| 86 | - // 2. 前端传递了 UI 展示模式 | |
| 87 | - return displayMode != null ? displayMode : | |
| 88 | - PayDisplayModeEnum.URL.getMode(); // 模式使用 URL 跳转 | |
| 89 | - } | |
| 90 | - | |
| 91 | 69 | } | ... | ... |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
| 1 | 1 | package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.util.ObjectUtil; | |
| 4 | 3 | import cn.hutool.http.Method; |
| 5 | 4 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedReqDTO; |
| 6 | 5 | import cn.iocoder.yudao.framework.pay.core.client.dto.order.PayOrderUnifiedRespDTO; |
| ... | ... | @@ -37,9 +36,8 @@ public class AlipayWapPayClient extends AbstractAlipayClient { |
| 37 | 36 | model.setTotalAmount(formatAmount(reqDTO.getAmount())); |
| 38 | 37 | model.setProductCode("QUICK_WAP_PAY"); // 销售产品码. 目前 Wap 支付场景下仅支持 QUICK_WAP_PAY |
| 39 | 38 | // ② 个性化的参数【无】 |
| 40 | - // ③ 支付宝 Wap 支付只有一种展示,考虑到前端可能希望二维码扫描后,手机打开 | |
| 41 | - String displayMode = ObjectUtil.defaultIfNull(reqDTO.getDisplayMode(), | |
| 42 | - PayDisplayModeEnum.URL.getMode()); | |
| 39 | + // ③ 支付宝 Wap 支付只有一种展示:URL | |
| 40 | + String displayMode = PayDisplayModeEnum.URL.getMode(); | |
| 43 | 41 | |
| 44 | 42 | // 1.2 构建 AlipayTradeWapPayRequest 请求 |
| 45 | 43 | AlipayTradeWapPayRequest request = new AlipayTradeWapPayRequest(); | ... | ... |
yudao-module-mall/yudao-module-trade-biz/pom.xml
| ... | ... | @@ -93,6 +93,10 @@ |
| 93 | 93 | <groupId>cn.iocoder.boot</groupId> |
| 94 | 94 | <artifactId>yudao-spring-boot-starter-excel</artifactId> |
| 95 | 95 | </dependency> |
| 96 | + <dependency> | |
| 97 | + <groupId>cn.iocoder.boot</groupId> | |
| 98 | + <artifactId>yudao-spring-boot-starter-biz-dict</artifactId> | |
| 99 | + </dependency> | |
| 96 | 100 | |
| 97 | 101 | </dependencies> |
| 98 | 102 | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderDetailRespVO.java
| ... | ... | @@ -55,8 +55,10 @@ public class AppTradeOrderDetailRespVO { |
| 55 | 55 | @Schema(description = "付款超时时间", requiredMode = Schema.RequiredMode.REQUIRED) |
| 56 | 56 | private LocalDateTime payExpireTime; |
| 57 | 57 | |
| 58 | - @Schema(description = "支付渠道", requiredMode = Schema.RequiredMode.REQUIRED, example = "wx_lite_pay") | |
| 58 | + @Schema(description = "支付渠道", example = "wx_lite_pay") | |
| 59 | 59 | private String payChannelCode; |
| 60 | + @Schema(description = "支付渠道名", example = "微信小程序支付") | |
| 61 | + private String payChannelName; | |
| 60 | 62 | |
| 61 | 63 | @Schema(description = "商品原价(总)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1000") |
| 62 | 64 | private Integer totalPrice; | ... | ... |
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.convert.order; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | +import cn.hutool.core.util.StrUtil; | |
| 4 | 5 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 5 | 6 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 7 | +import cn.iocoder.yudao.framework.dict.core.util.DictFrameworkUtils; | |
| 6 | 8 | import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils; |
| 7 | 9 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; |
| 8 | 10 | import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO; |
| 9 | 11 | import cn.iocoder.yudao.module.pay.api.order.dto.PayOrderCreateReqDTO; |
| 12 | +import cn.iocoder.yudao.module.pay.enums.DictTypeConstants; | |
| 10 | 13 | import cn.iocoder.yudao.module.product.api.comment.dto.ProductCommentCreateReqDTO; |
| 11 | 14 | import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO; |
| 12 | 15 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuUpdateStockReqDTO; |
| ... | ... | @@ -234,6 +237,9 @@ public interface TradeOrderConvert { |
| 234 | 237 | List<ProductPropertyValueDetailRespDTO> propertyValueDetails, TradeOrderProperties tradeOrderProperties) { |
| 235 | 238 | AppTradeOrderDetailRespVO orderVO = convert3(order, orderItems); |
| 236 | 239 | orderVO.setPayExpireTime(addTime(tradeOrderProperties.getExpireTime())); |
| 240 | + if (StrUtil.isNotEmpty(order.getPayChannelCode())) { | |
| 241 | + orderVO.setPayChannelName(DictFrameworkUtils.getDictDataLabel(DictTypeConstants.CHANNEL_CODE_TYPE, order.getPayChannelCode())); | |
| 242 | + } | |
| 237 | 243 | // 处理商品属性 |
| 238 | 244 | Map<Long, ProductPropertyValueDetailRespDTO> propertyValueDetailMap = convertMap(propertyValueDetails, ProductPropertyValueDetailRespDTO::getValueId); |
| 239 | 245 | for (int i = 0; i < orderItems.size(); i++) { | ... | ... |
yudao-module-pay/yudao-module-pay-api/src/main/java/cn/iocoder/yudao/module/pay/enums/DictTypeConstants.java
| ... | ... | @@ -7,6 +7,8 @@ package cn.iocoder.yudao.module.pay.enums; |
| 7 | 7 | */ |
| 8 | 8 | public interface DictTypeConstants { |
| 9 | 9 | |
| 10 | + String CHANNEL_CODE_TYPE = "pay_channel_code_type"; // 支付-渠道名 | |
| 11 | + | |
| 10 | 12 | String ORDER_STATUS = "pay_order_status"; // 支付-订单-订单状态 |
| 11 | 13 | String ORDER_NOTIFY_STATUS = "pay_order_notify_status"; // 支付-订单-订单回调商户状态 |
| 12 | 14 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/notify/PayNotifyController.java
| ... | ... | @@ -6,8 +6,6 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; |
| 6 | 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayNotifyReqDTO; |
| 7 | 7 | import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayOrderNotifyRespDTO; |
| 8 | 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.notify.PayRefundNotifyRespDTO; |
| 9 | -import cn.iocoder.yudao.module.pay.dal.dataobject.merchant.PayChannelDO; | |
| 10 | -import cn.iocoder.yudao.module.pay.service.merchant.PayChannelService; | |
| 11 | 9 | import cn.iocoder.yudao.module.pay.service.order.PayOrderService; |
| 12 | 10 | import cn.iocoder.yudao.module.pay.service.refund.PayRefundService; |
| 13 | 11 | import io.swagger.v3.oas.annotations.Operation; |
| ... | ... | @@ -40,23 +38,6 @@ public class PayNotifyController { |
| 40 | 38 | private PayClientFactory payClientFactory; |
| 41 | 39 | |
| 42 | 40 | /** |
| 43 | - * 统一的跳转页面,支付宝跳转参数说明 | |
| 44 | - * | |
| 45 | - * <a href="https://opendocs.alipay.com/open/203/105285#前台回跳参数说明">支付宝 - 前台回跳参数说明</a> | |
| 46 | - * | |
| 47 | - * @param channelId 渠道编号 | |
| 48 | - * @return 返回跳转页面 | |
| 49 | - */ | |
| 50 | - @GetMapping(value = "/return/{channelId}") | |
| 51 | - @Operation(summary = "渠道统一的支付成功返回地址") | |
| 52 | - @Deprecated // TODO yunai:如果是 way 的情况,应该是跳转回前端地址 | |
| 53 | - public String returnCallback(@PathVariable("channelId") Long channelId, | |
| 54 | - @RequestParam Map<String, String> params) { | |
| 55 | - log.info("[returnCallback][app_id({}) 跳转]", params.get("app_id")); | |
| 56 | - return String.format("渠道[%s]支付成功", channelId); | |
| 57 | - } | |
| 58 | - | |
| 59 | - /** | |
| 60 | 41 | * 统一的渠道支付回调,支付宝的退款回调 |
| 61 | 42 | * |
| 62 | 43 | * @param channelId 渠道编号 | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/order/vo/PayOrderSubmitReqVO.java
| ... | ... | @@ -2,11 +2,10 @@ package cn.iocoder.yudao.module.pay.controller.admin.order.vo; |
| 2 | 2 | |
| 3 | 3 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 4 | import lombok.Data; |
| 5 | -import lombok.experimental.Accessors; | |
| 5 | +import org.hibernate.validator.constraints.URL; | |
| 6 | 6 | |
| 7 | 7 | import javax.validation.constraints.NotEmpty; |
| 8 | 8 | import javax.validation.constraints.NotNull; |
| 9 | -import java.awt.*; | |
| 10 | 9 | import java.util.Map; |
| 11 | 10 | |
| 12 | 11 | @Schema(description = "管理后台 - 支付订单提交 Request VO") |
| ... | ... | @@ -26,4 +25,9 @@ public class PayOrderSubmitReqVO { |
| 26 | 25 | |
| 27 | 26 | @Schema(description = "展示模式", example = "url") // 参见 {@link PayDisplayModeEnum} 枚举。如果不传递,则每个支付渠道使用默认的方式 |
| 28 | 27 | private String displayMode; |
| 28 | + | |
| 29 | + @Schema(description = "回跳地址") | |
| 30 | + @URL(message = "回跳地址的格式必须是 URL") | |
| 31 | + private String returnUrl; | |
| 32 | + | |
| 29 | 33 | } | ... | ... |
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/controller/app/order/AppPayOrderController.http
yudao-module-pay/yudao-module-pay-biz/src/main/java/cn/iocoder/yudao/module/pay/service/order/PayOrderServiceImpl.java
| ... | ... | @@ -4,7 +4,6 @@ import cn.hutool.core.date.DateUtil; |
| 4 | 4 | import cn.hutool.core.util.ObjectUtil; |
| 5 | 5 | import cn.hutool.core.util.RandomUtil; |
| 6 | 6 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 7 | -import cn.iocoder.yudao.framework.common.util.json.JsonUtils; | |
| 8 | 7 | import cn.iocoder.yudao.framework.pay.config.PayProperties; |
| 9 | 8 | import cn.iocoder.yudao.framework.pay.core.client.PayClient; |
| 10 | 9 | import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; |
| ... | ... | @@ -44,7 +43,7 @@ import java.util.Collection; |
| 44 | 43 | import java.util.List; |
| 45 | 44 | |
| 46 | 45 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 47 | -import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.*; | |
| 46 | +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | |
| 48 | 47 | |
| 49 | 48 | /** |
| 50 | 49 | * 支付订单 Service 实现类 |
| ... | ... | @@ -147,7 +146,7 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 147 | 146 | .setMerchantOrderId(orderExtension.getNo()) // 注意,此处使用的是 PayOrderExtensionDO.no 属性! |
| 148 | 147 | .setSubject(order.getSubject()).setBody(order.getBody()) |
| 149 | 148 | .setNotifyUrl(genChannelPayNotifyUrl(channel)) |
| 150 | - .setReturnUrl(genChannelReturnUrl(channel)) | |
| 149 | + .setReturnUrl(reqVO.getReturnUrl()) | |
| 151 | 150 | // 订单相关字段 |
| 152 | 151 | .setAmount(order.getAmount()).setExpireTime(order.getExpireTime()); |
| 153 | 152 | PayOrderUnifiedRespDTO unifiedOrderRespDTO = client.unifiedOrder(unifiedOrderReqDTO); |
| ... | ... | @@ -184,15 +183,6 @@ public class PayOrderServiceImpl implements PayOrderService { |
| 184 | 183 | } |
| 185 | 184 | |
| 186 | 185 | /** |
| 187 | - * 根据支付渠道的编码,生成支付渠道的返回地址 | |
| 188 | - * @param channel 支付渠道 | |
| 189 | - * @return 支付成功返回的地址。 配置地址 + "/" + channel id | |
| 190 | - */ | |
| 191 | - private String genChannelReturnUrl(PayChannelDO channel) { | |
| 192 | - return payProperties.getReturnUrl() + "/" + channel.getId(); | |
| 193 | - } | |
| 194 | - | |
| 195 | - /** | |
| 196 | 186 | * 根据支付渠道的编码,生成支付渠道的回调地址 |
| 197 | 187 | * |
| 198 | 188 | * @param channel 支付渠道 | ... | ... |
yudao-server/src/main/resources/application-dev.yaml
| ... | ... | @@ -168,7 +168,6 @@ yudao: |
| 168 | 168 | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| 169 | 169 | pay: |
| 170 | 170 | callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback |
| 171 | - return-url: http://yunai.natapp1.cc/admin-api/pay/notify/return | |
| 172 | 171 | demo: true # 开启演示模式 |
| 173 | 172 | |
| 174 | 173 | justauth: | ... | ... |
yudao-server/src/main/resources/application-local.yaml
| ... | ... | @@ -195,7 +195,6 @@ yudao: |
| 195 | 195 | - ${management.endpoints.web.base-path}/** # 不处理 Actuator 的请求 |
| 196 | 196 | pay: |
| 197 | 197 | callback-url: http://yunai.natapp1.cc/admin-api/pay/notify/callback |
| 198 | - return-url: http://yunai.natapp1.cc/admin-api/pay/notify/return | |
| 199 | 198 | access-log: # 访问日志的配置项 |
| 200 | 199 | enable: false |
| 201 | 200 | error-code: # 错误码相关配置项 | ... | ... |