Commit 7d6f205dc0178108c4bf4b5043456620c5d7ac78
1 parent
19f55417
支付退款申请,支付宝手机wap 相应实现
Showing
41 changed files
with
1515 additions
and
57 deletions
sql/pay_refund.sql
0 → 100644
| 1 | +DROP TABLE IF EXISTS `pay_refund`; | ||
| 2 | +CREATE TABLE `pay_refund` ( | ||
| 3 | + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '支付退款编号', | ||
| 4 | + `req_no` varchar(64) NOT NULL COMMENT '退款单请求号', | ||
| 5 | + `merchant_id` bigint NOT NULL COMMENT '商户编号', | ||
| 6 | + `app_id` bigint NOT NULL COMMENT '应用编号', | ||
| 7 | + `channel_id` bigint NOT NULL COMMENT '渠道编号', | ||
| 8 | + `channel_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '渠道编码', | ||
| 9 | + `order_id` bigint NOT NULL COMMENT '支付订单编号 pay_order 表id', | ||
| 10 | + `trade_no` varchar(64) NOT NULL COMMENT '交易订单号 pay_extension 表no 字段', | ||
| 11 | + `merchant_order_id` varchar(64) NOT NULL COMMENT '商户订单编号(商户系统生成)', | ||
| 12 | + `merchant_refund_no` varchar(64) NOT NULL COMMENT '商户退款订单号(商户系统生成)', | ||
| 13 | + `notify_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '异步通知商户地址', | ||
| 14 | + `notify_status` tinyint NOT NULL COMMENT '通知商户退款结果的回调状态', | ||
| 15 | + `status` tinyint NOT NULL COMMENT '退款状态', | ||
| 16 | + `type` tinyint NOT NULL COMMENT '退款类型(部分退款,全部退款)', | ||
| 17 | + `pay_amount` bigint NOT NULL COMMENT '支付金额,单位分', | ||
| 18 | + `refund_amount` bigint NOT NULL COMMENT '退款金额,单位分', | ||
| 19 | + `reason` VARCHAR(256) NOT NULL COMMENT '退款原因', | ||
| 20 | + `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户 IP', | ||
| 21 | + `channel_order_no` varchar(64) NOT NULL COMMENT '渠道订单号,pay_order 中的channel_order_no 对应', | ||
| 22 | + `channel_refund_no` varchar(64) DEFAULT NULL COMMENT '渠道退款单号,渠道返回', | ||
| 23 | + `channel_error_code` varchar(128) DEFAULT NULL COMMENT '渠道调用报错时,错误码', | ||
| 24 | + `channel_error_msg` varchar(256) DEFAULT NULL COMMENT '渠道调用报错时,错误信息', | ||
| 25 | + `channel_extras` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '支付渠道的额外参数', | ||
| 26 | + `expire_time` datetime DEFAULT NULL COMMENT '退款失效时间', | ||
| 27 | + `success_time` datetime DEFAULT NULL COMMENT '退款成功时间', | ||
| 28 | + `notify_time` datetime DEFAULT NULL COMMENT '退款通知时间', | ||
| 29 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | ||
| 30 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 31 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | ||
| 32 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 33 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | ||
| 34 | + PRIMARY KEY (`id`) USING BTREE | ||
| 35 | +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退款订单'; |
sql/ruoyi-vue-pro.sql
| @@ -953,7 +953,7 @@ CREATE TABLE `pay_app` ( | @@ -953,7 +953,7 @@ CREATE TABLE `pay_app` ( | ||
| 953 | -- Records of pay_app | 953 | -- Records of pay_app |
| 954 | -- ---------------------------- | 954 | -- ---------------------------- |
| 955 | BEGIN; | 955 | BEGIN; |
| 956 | -INSERT INTO `pay_app` VALUES (6, '芋道', 0, '我是一个公众号', 'http://127.0.0.1:28080/api/shop/order/pay-notify', 'http://127.0.0.1', 1, '', '2021-10-23 08:49:25', '', '2021-10-27 00:26:35', b'0'); | 956 | +INSERT INTO `pay_app` VALUES (6, '芋道', 0, '我是一个公众号', 'http://127.0.0.1:28080/api/shop/order/pay-notify', 'http://127.0.0.1:28080/api/shop/order/refund-notify', 1, '', '2021-10-23 08:49:25', '', '2021-10-27 00:26:35', b'0'); |
| 957 | COMMIT; | 957 | COMMIT; |
| 958 | 958 | ||
| 959 | -- ---------------------------- | 959 | -- ---------------------------- |
| @@ -1240,6 +1240,43 @@ INSERT INTO `pay_order` VALUES (120, 1, 6, 9, 'wx_pub', '1635311949168', '标题 | @@ -1240,6 +1240,43 @@ INSERT INTO `pay_order` VALUES (120, 1, 6, 9, 'wx_pub', '1635311949168', '标题 | ||
| 1240 | INSERT INTO `pay_order` VALUES (121, 1, 6, 9, 'wx_pub', '1635312124657', '标题:1635312124656', '内容:1635312124656', 'http://127.0.0.1:28080/api/shop/order/pay-notify', 0, 1, 0, 0, 10, '101.82.233.75', '2021-10-28 13:22:05', '2021-10-27 13:22:15', '2021-10-27 13:22:16', 100, 0, 0, 0, 'ockUAwIZ-0OeMZl9ogcZ4ILrGba0', '4200001174202110278060590766', NULL, '2021-10-27 13:22:05', NULL, '2021-10-27 13:22:16', b'0'); | 1240 | INSERT INTO `pay_order` VALUES (121, 1, 6, 9, 'wx_pub', '1635312124657', '标题:1635312124656', '内容:1635312124656', 'http://127.0.0.1:28080/api/shop/order/pay-notify', 0, 1, 0, 0, 10, '101.82.233.75', '2021-10-28 13:22:05', '2021-10-27 13:22:15', '2021-10-27 13:22:16', 100, 0, 0, 0, 'ockUAwIZ-0OeMZl9ogcZ4ILrGba0', '4200001174202110278060590766', NULL, '2021-10-27 13:22:05', NULL, '2021-10-27 13:22:16', b'0'); |
| 1241 | COMMIT; | 1241 | COMMIT; |
| 1242 | 1242 | ||
| 1243 | +DROP TABLE IF EXISTS `pay_refund`; | ||
| 1244 | +CREATE TABLE `pay_refund` ( | ||
| 1245 | + `id` bigint NOT NULL AUTO_INCREMENT COMMENT '支付退款编号', | ||
| 1246 | + `req_no` varchar(64) NOT NULL COMMENT '退款单请求号', | ||
| 1247 | + `merchant_id` bigint NOT NULL COMMENT '商户编号', | ||
| 1248 | + `app_id` bigint NOT NULL COMMENT '应用编号', | ||
| 1249 | + `channel_id` bigint NOT NULL COMMENT '渠道编号', | ||
| 1250 | + `channel_code` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '渠道编码', | ||
| 1251 | + `order_id` bigint NOT NULL COMMENT '支付订单编号 pay_order 表id', | ||
| 1252 | + `trade_no` varchar(64) NOT NULL COMMENT '交易订单号 pay_extension 表no 字段', | ||
| 1253 | + `merchant_order_id` varchar(64) NOT NULL COMMENT '商户订单编号(商户系统生成)', | ||
| 1254 | + `merchant_refund_no` varchar(64) NOT NULL COMMENT '商户退款订单号(商户系统生成)', | ||
| 1255 | + `notify_url` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '异步通知商户地址', | ||
| 1256 | + `notify_status` tinyint NOT NULL COMMENT '通知商户退款结果的回调状态', | ||
| 1257 | + `status` tinyint NOT NULL COMMENT '退款状态', | ||
| 1258 | + `type` tinyint NOT NULL COMMENT '退款类型(部分退款,全部退款)', | ||
| 1259 | + `pay_amount` bigint NOT NULL COMMENT '支付金额,单位分', | ||
| 1260 | + `refund_amount` bigint NOT NULL COMMENT '退款金额,单位分', | ||
| 1261 | + `reason` VARCHAR(256) NOT NULL COMMENT '退款原因', | ||
| 1262 | + `user_ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '用户 IP', | ||
| 1263 | + `channel_order_no` varchar(64) NOT NULL COMMENT '渠道订单号,pay_order 中的channel_order_no 对应', | ||
| 1264 | + `channel_refund_no` varchar(64) DEFAULT NULL COMMENT '渠道退款单号,渠道返回', | ||
| 1265 | + `channel_error_code` varchar(128) DEFAULT NULL COMMENT '渠道调用报错时,错误码', | ||
| 1266 | + `channel_error_msg` varchar(256) DEFAULT NULL COMMENT '渠道调用报错时,错误信息', | ||
| 1267 | + `channel_extras` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL COMMENT '支付渠道的额外参数', | ||
| 1268 | + `expire_time` datetime DEFAULT NULL COMMENT '退款失效时间', | ||
| 1269 | + `success_time` datetime DEFAULT NULL COMMENT '退款成功时间', | ||
| 1270 | + `notify_time` datetime DEFAULT NULL COMMENT '退款通知时间', | ||
| 1271 | + `creator` varchar(64) DEFAULT '' COMMENT '创建者', | ||
| 1272 | + `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', | ||
| 1273 | + `updater` varchar(64) DEFAULT '' COMMENT '更新者', | ||
| 1274 | + `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间', | ||
| 1275 | + `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除', | ||
| 1276 | + PRIMARY KEY (`id`) USING BTREE | ||
| 1277 | +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='退款订单'; | ||
| 1278 | + | ||
| 1279 | + | ||
| 1243 | -- ---------------------------- | 1280 | -- ---------------------------- |
| 1244 | -- Table structure for pay_order_extension | 1281 | -- Table structure for pay_order_extension |
| 1245 | -- ---------------------------- | 1282 | -- ---------------------------- |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/convert/order/PayRefundCoreConvert.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.convert.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundPostReqBO; | ||
| 6 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO; | ||
| 7 | +import org.mapstruct.Mapper; | ||
| 8 | +import org.mapstruct.factory.Mappers; | ||
| 9 | +import org.mapstruct.Mapping; | ||
| 10 | +import org.mapstruct.Mappings; | ||
| 11 | + | ||
| 12 | +@Mapper | ||
| 13 | +public interface PayRefundCoreConvert { | ||
| 14 | + | ||
| 15 | + PayRefundCoreConvert INSTANCE = Mappers.getMapper(PayRefundCoreConvert.class); | ||
| 16 | + | ||
| 17 | + PayRefundPostReqBO convert(PayRefundUnifiedRespDTO respDTO); | ||
| 18 | + | ||
| 19 | + //TODO 太多需要处理了, 暂时不用 | ||
| 20 | + @Mappings(value = { | ||
| 21 | + @Mapping(source = "amount", target = "payAmount"), | ||
| 22 | + @Mapping(source = "id", target = "orderId"), | ||
| 23 | + @Mapping(target = "status",ignore = true) | ||
| 24 | + }) | ||
| 25 | + PayRefundDO convert(PayOrderDO orderDO); | ||
| 26 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayOrderDO.java
| @@ -4,7 +4,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO | @@ -4,7 +4,7 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO | ||
| 4 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | 4 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; |
| 5 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; | 5 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; |
| 6 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderNotifyStatusEnum; | 6 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderNotifyStatusEnum; |
| 7 | -import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderRefundStatusEnum; | 7 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum; |
| 8 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum; | 8 | import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum; |
| 9 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 9 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 10 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; | 10 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; |
| @@ -135,7 +135,7 @@ public class PayOrderDO extends BaseDO { | @@ -135,7 +135,7 @@ public class PayOrderDO extends BaseDO { | ||
| 135 | /** | 135 | /** |
| 136 | * 退款状态 | 136 | * 退款状态 |
| 137 | * | 137 | * |
| 138 | - * 枚举 {@link PayOrderRefundStatusEnum} | 138 | + * 枚举 {@link PayRefundTypeEnum} |
| 139 | */ | 139 | */ |
| 140 | private Integer refundStatus; | 140 | private Integer refundStatus; |
| 141 | /** | 141 | /** |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/dataobject/order/PayRefundDO.java
| @@ -5,7 +5,9 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChann | @@ -5,7 +5,9 @@ import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChann | ||
| 5 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; | 5 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayMerchantDO; |
| 6 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; | 6 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; |
| 7 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; | 7 | import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; |
| 8 | -import lombok.Data; | 8 | +import com.baomidou.mybatisplus.annotation.TableId; |
| 9 | +import com.baomidou.mybatisplus.annotation.TableName; | ||
| 10 | +import lombok.*; | ||
| 9 | 11 | ||
| 10 | import java.util.Date; | 12 | import java.util.Date; |
| 11 | 13 | ||
| @@ -17,19 +19,34 @@ import java.util.Date; | @@ -17,19 +19,34 @@ import java.util.Date; | ||
| 17 | * | 19 | * |
| 18 | * @author 芋道源码 | 20 | * @author 芋道源码 |
| 19 | */ | 21 | */ |
| 22 | +@TableName("pay_refund") | ||
| 20 | @Data | 23 | @Data |
| 24 | +@EqualsAndHashCode(callSuper = true) | ||
| 25 | +@ToString(callSuper = true) | ||
| 26 | +@Builder | ||
| 27 | +@NoArgsConstructor | ||
| 28 | +@AllArgsConstructor | ||
| 21 | public class PayRefundDO extends BaseDO { | 29 | public class PayRefundDO extends BaseDO { |
| 22 | 30 | ||
| 23 | /** | 31 | /** |
| 24 | * 退款单编号,数据库自增 | 32 | * 退款单编号,数据库自增 |
| 25 | */ | 33 | */ |
| 34 | + @TableId | ||
| 26 | private Long id; | 35 | private Long id; |
| 27 | - /** | ||
| 28 | - * 退款单号,根据规则生成 | ||
| 29 | - * | 36 | + |
| 37 | + /** | ||
| 38 | + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no | ||
| 39 | + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no | ||
| 40 | + * 退款请求号。 | ||
| 41 | + * 标识一次退款请求,需要保证在交易号下唯一,如需部分退款,则此参数必传。 | ||
| 42 | + * 注:针对同一次退款请求,如果调用接口失败或异常了,重试时需要保证退款请求号不能变更, | ||
| 43 | + * 防止该笔交易重复退款。支付宝会保证同样的退款请求号多次请求只会退一次。 | ||
| 44 | + * 退款单请求号,根据规则生成 | ||
| 45 | + * | ||
| 30 | * 例如说,R202109181134287570000 | 46 | * 例如说,R202109181134287570000 |
| 31 | */ | 47 | */ |
| 32 | - private String no; | 48 | + private String reqNo; |
| 49 | + | ||
| 33 | /** | 50 | /** |
| 34 | * 商户编号 | 51 | * 商户编号 |
| 35 | * | 52 | * |
| @@ -61,23 +78,39 @@ public class PayRefundDO extends BaseDO { | @@ -61,23 +78,39 @@ public class PayRefundDO extends BaseDO { | ||
| 61 | */ | 78 | */ |
| 62 | private Long orderId; | 79 | private Long orderId; |
| 63 | 80 | ||
| 81 | + | ||
| 82 | + /** | ||
| 83 | + * 交易订单号,根据规则生成 | ||
| 84 | + * 调用支付渠道时,使用该字段作为对接的订单号。 | ||
| 85 | + * 1. 调用微信支付 https://api.mch.weixin.qq.com/pay/unifiedorder 时,使用该字段作为 out_trade_no | ||
| 86 | + * 2. 调用支付宝 https://opendocs.alipay.com/apis 时,使用该字段作为 out_trade_no | ||
| 87 | + * 这里对应 pay_extension 里面的 no | ||
| 88 | + * 例如说,P202110132239124200055 | ||
| 89 | + */ | ||
| 90 | + private String tradeNo; | ||
| 91 | + | ||
| 92 | + | ||
| 64 | // ========== 商户相关字段 ========== | 93 | // ========== 商户相关字段 ========== |
| 65 | /** | 94 | /** |
| 66 | - * 商户退款订单号 | ||
| 67 | - * 例如说,内部系统 A 的退款订单号。需要保证每个 PayMerchantDO 唯一 TODO 芋艿:需要在测试下 | 95 | + * 商户订单编号 |
| 96 | + */ | ||
| 97 | + private String merchantOrderId; | ||
| 98 | + /** | ||
| 99 | + * 商户退款订单号, 由商户系统产生, 由他们保证唯一,不能为空,通知商户时会传该字段。发送channel 使用 reqNo | ||
| 100 | + * 例如说,内部系统 A 的退款订单号。需要保证每个 PayMerchantDO 唯一 | ||
| 101 | + * TODO 芋艿:我理解 一个商户退款订单,可以对应多条退款记录, 因为有可能失败。但是 退款请求号 reqNo 必须唯一 | ||
| 102 | + * | ||
| 68 | */ | 103 | */ |
| 69 | private String merchantRefundNo; | 104 | private String merchantRefundNo; |
| 70 | -// /** | ||
| 71 | -// * 商户拓展参数 | ||
| 72 | -// */ | ||
| 73 | -// private String merchantExtra; | 105 | + |
| 74 | /** | 106 | /** |
| 75 | * 异步通知地址 | 107 | * 异步通知地址 |
| 76 | */ | 108 | */ |
| 77 | private String notifyUrl; | 109 | private String notifyUrl; |
| 110 | + | ||
| 78 | /** | 111 | /** |
| 79 | * 通知商户退款结果的回调状态 | 112 | * 通知商户退款结果的回调状态 |
| 80 | - * TODO 芋艿:0 未发送 1 已发送 | 113 | + * TODO 0 未发送 1 已发送 |
| 81 | */ | 114 | */ |
| 82 | private Integer notifyStatus; | 115 | private Integer notifyStatus; |
| 83 | 116 | ||
| @@ -85,44 +118,76 @@ public class PayRefundDO extends BaseDO { | @@ -85,44 +118,76 @@ public class PayRefundDO extends BaseDO { | ||
| 85 | /** | 118 | /** |
| 86 | * 退款状态 | 119 | * 退款状态 |
| 87 | * | 120 | * |
| 88 | - * TODO 芋艿:状态枚举 | ||
| 89 | */ | 121 | */ |
| 90 | private Integer status; | 122 | private Integer status; |
| 123 | + | ||
| 91 | /** | 124 | /** |
| 92 | - * 用户 IP | 125 | + * 退款类型(部分退款,全部退款) |
| 93 | */ | 126 | */ |
| 94 | - private String userIp; | 127 | + private Integer type; |
| 95 | /** | 128 | /** |
| 96 | - * 退款金额,单位:分 | 129 | + * 支付金额,单位分 |
| 97 | */ | 130 | */ |
| 98 | - private Long amount; | 131 | + private Long payAmount; |
| 132 | + /** | ||
| 133 | + * 退款金额,单位分 | ||
| 134 | + */ | ||
| 135 | + private Long refundAmount; | ||
| 136 | + | ||
| 99 | /** | 137 | /** |
| 100 | * 退款原因 | 138 | * 退款原因 |
| 101 | */ | 139 | */ |
| 102 | private String reason; | 140 | private String reason; |
| 141 | + | ||
| 142 | + | ||
| 103 | /** | 143 | /** |
| 104 | - * 订单退款成功时间 | 144 | + * 用户 IP |
| 105 | */ | 145 | */ |
| 106 | - private Date successTime; | 146 | + private String userIp; |
| 147 | + | ||
| 148 | + // ========== 渠道相关字段 ========== | ||
| 107 | /** | 149 | /** |
| 108 | - * 退款失效时间 | 150 | + * 渠道订单号,pay_order 中的channel_order_no 对应 |
| 109 | */ | 151 | */ |
| 110 | - private Date expireTime; | 152 | + private String channelOrderNo; |
| 153 | + /** | ||
| 154 | + * 渠道退款单号,渠道返回 | ||
| 155 | + */ | ||
| 156 | + private String channelRefundNo; | ||
| 157 | + | ||
| 158 | + /** | ||
| 159 | + * 调用渠道的错误码 | ||
| 160 | + */ | ||
| 161 | + private String channelErrorCode; | ||
| 162 | + | ||
| 163 | + /** | ||
| 164 | + * 调用渠道报错时,错误信息 | ||
| 165 | + */ | ||
| 166 | + private String channelErrorMsg; | ||
| 167 | + | ||
| 168 | + | ||
| 111 | /** | 169 | /** |
| 112 | * 支付渠道的额外参数 | 170 | * 支付渠道的额外参数 |
| 113 | - * | ||
| 114 | * 参见 https://www.pingxx.com/api/Refunds%20退款概述.html | 171 | * 参见 https://www.pingxx.com/api/Refunds%20退款概述.html |
| 115 | */ | 172 | */ |
| 116 | - private String channelExtra; | 173 | + private String channelExtras; |
| 174 | + | ||
| 117 | 175 | ||
| 118 | - // ========== 渠道相关字段 ========== | ||
| 119 | /** | 176 | /** |
| 120 | - * 渠道订单号 | 177 | + * TODO |
| 178 | + * 退款失效时间 | ||
| 121 | */ | 179 | */ |
| 122 | - private String channelOrderNo; | 180 | + private Date expireTime; |
| 123 | /** | 181 | /** |
| 124 | - * 渠道退款号 | 182 | + * 退款成功时间 |
| 125 | */ | 183 | */ |
| 126 | - private String channelRefundNo; | 184 | + private Date successTime; |
| 185 | + /** | ||
| 186 | + * 退款通知时间 | ||
| 187 | + */ | ||
| 188 | + private Date notifyTime; | ||
| 189 | + | ||
| 190 | + | ||
| 191 | + | ||
| 127 | 192 | ||
| 128 | } | 193 | } |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/dal/mysql/order/PayRefundMapper.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order; | ||
| 2 | + | ||
| 3 | +import java.util.*; | ||
| 4 | + | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 6 | +import cn.iocoder.yudao.framework.common.pojo.PageResult; | ||
| 7 | +import cn.iocoder.yudao.framework.mybatis.core.query.QueryWrapperX; | ||
| 8 | +import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; | ||
| 9 | + | ||
| 10 | +import org.apache.ibatis.annotations.Mapper; | ||
| 11 | + | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 退款订单 Mapper | ||
| 15 | + * | ||
| 16 | + */ | ||
| 17 | +@Mapper | ||
| 18 | +public interface PayRefundMapper extends BaseMapperX<PayRefundDO> { | ||
| 19 | + | ||
| 20 | + | ||
| 21 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/PayErrorCodeCoreConstants.java
| @@ -28,6 +28,12 @@ public interface PayErrorCodeCoreConstants { | @@ -28,6 +28,12 @@ public interface PayErrorCodeCoreConstants { | ||
| 28 | ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付"); | 28 | ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_WAITING = new ErrorCode(1007003001, "支付交易拓展单不处于待支付"); |
| 29 | ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007003002, "支付订单不处于已支付"); | 29 | ErrorCode PAY_ORDER_EXTENSION_STATUS_IS_NOT_SUCCESS = new ErrorCode(1007003002, "支付订单不处于已支付"); |
| 30 | 30 | ||
| 31 | + // ========== 支付模块(退款) 1-007-006-000 ========== | ||
| 32 | + ErrorCode PAY_REFUND_AMOUNT_EXCEED = new ErrorCode(1007006000, "退款金额超过订单可退款金额"); | ||
| 33 | + ErrorCode PAY_REFUND_ALL_REFUNDED = new ErrorCode(1007006001, "订单已经全额退款"); | ||
| 34 | + ErrorCode PAY_REFUND_CHN_ORDER_NO_IS_NULL = new ErrorCode(1007006002, "该订单的渠道订单为空"); | ||
| 35 | + ErrorCode PAY_REFUND_POST_HANDLER_NOT_FOUND = new ErrorCode(1007006002, "未找到对应的退款后置处理类"); | ||
| 36 | + | ||
| 31 | /** | 37 | /** |
| 32 | * ========== 支付商户信息 1-007-004-000 ========== | 38 | * ========== 支付商户信息 1-007-004-000 ========== |
| 33 | */ | 39 | */ |
| @@ -41,11 +47,11 @@ public interface PayErrorCodeCoreConstants { | @@ -41,11 +47,11 @@ public interface PayErrorCodeCoreConstants { | ||
| 41 | 47 | ||
| 42 | 48 | ||
| 43 | /** | 49 | /** |
| 44 | - * ========== 支付渠道 1-007-006-000 ========== | 50 | + * ========== 支付渠道 1-007-001-000 ========== |
| 45 | */ | 51 | */ |
| 46 | - ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007006000, "支付渠道不存在"); | ||
| 47 | - ErrorCode CHANNEL_KEY_READ_ERROR = new ErrorCode(1007006002, "支付渠道秘钥文件读取失败"); | 52 | + ErrorCode CHANNEL_NOT_EXISTS = new ErrorCode(1007001003, "支付渠道不存在"); |
| 53 | + ErrorCode CHANNEL_KEY_READ_ERROR = new ErrorCode(1007001004, "支付渠道秘钥文件读取失败"); | ||
| 48 | // TODO @aquan:下面这个错误码,缺了 CHANNEL 前缀。另外,错误码的分段,上面有啦,合并下进去哈 | 54 | // TODO @aquan:下面这个错误码,缺了 CHANNEL 前缀。另外,错误码的分段,上面有啦,合并下进去哈 |
| 49 | - ErrorCode EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007006003, "已存在相同的渠道"); | 55 | + ErrorCode EXIST_SAME_CHANNEL_ERROR = new ErrorCode(1007001005, "已存在相同的渠道"); |
| 50 | 56 | ||
| 51 | } | 57 | } |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayRefundStatusEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.enums.order; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Getter; | ||
| 5 | + | ||
| 6 | +@Getter | ||
| 7 | +@AllArgsConstructor | ||
| 8 | +public enum PayRefundStatusEnum { | ||
| 9 | + CREATE(0, "退款订单生成"), | ||
| 10 | + SUCCESS(1, "退款成功"), | ||
| 11 | + FAILURE(2, "退款失败"), | ||
| 12 | + PROCESSING_NOTIFY(3,"退款中, 渠道通知结果"), | ||
| 13 | + PROCESSING_QUERY(4,"退款中, 系统查询结果"), | ||
| 14 | + UNKNOWN_RETRY(5,"状态未知,需要重试"), | ||
| 15 | + UNKNOWN_QUERY(6,"状态未知,系统查询结果"), | ||
| 16 | + CLOSE(99, "退款关闭"); | ||
| 17 | + | ||
| 18 | + private final Integer status; | ||
| 19 | + private final String name; | ||
| 20 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayOrderRefundStatusEnum.java renamed to yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/enums/order/PayRefundTypeEnum.java
| @@ -11,7 +11,7 @@ import lombok.Getter; | @@ -11,7 +11,7 @@ import lombok.Getter; | ||
| 11 | */ | 11 | */ |
| 12 | @Getter | 12 | @Getter |
| 13 | @AllArgsConstructor | 13 | @AllArgsConstructor |
| 14 | -public enum PayOrderRefundStatusEnum implements IntArrayValuable { | 14 | +public enum PayRefundTypeEnum implements IntArrayValuable { |
| 15 | 15 | ||
| 16 | NO(0, "未退款"), | 16 | NO(0, "未退款"), |
| 17 | SOME(10, "部分退款"), | 17 | SOME(10, "部分退款"), |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/impl/PayNotifyCoreServiceImpl.java
| @@ -5,8 +5,10 @@ import cn.hutool.http.HttpUtil; | @@ -5,8 +5,10 @@ import cn.hutool.http.HttpUtil; | ||
| 5 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyLogDO; | 5 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyLogDO; |
| 6 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyTaskDO; | 6 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.notify.PayNotifyTaskDO; |
| 7 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | 7 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; |
| 8 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 8 | import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify.PayNotifyLogCoreMapper; | 9 | import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify.PayNotifyLogCoreMapper; |
| 9 | import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify.PayNotifyTaskCoreMapper; | 10 | import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.notify.PayNotifyTaskCoreMapper; |
| 11 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundMapper; | ||
| 10 | import cn.iocoder.yudao.coreservice.modules.pay.dal.redis.notify.PayNotifyLockCoreRedisDAO; | 12 | import cn.iocoder.yudao.coreservice.modules.pay.dal.redis.notify.PayNotifyLockCoreRedisDAO; |
| 11 | import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyStatusEnum; | 13 | import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyStatusEnum; |
| 12 | import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; | 14 | import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; |
| @@ -73,6 +75,9 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { | @@ -73,6 +75,9 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { | ||
| 73 | private PayNotifyLockCoreRedisDAO payNotifyLockCoreRedisDAO; | 75 | private PayNotifyLockCoreRedisDAO payNotifyLockCoreRedisDAO; |
| 74 | 76 | ||
| 75 | @Resource | 77 | @Resource |
| 78 | + private PayRefundMapper payRefundMapper; | ||
| 79 | + | ||
| 80 | + @Resource | ||
| 76 | @Lazy // 循环依赖(自己依赖自己),避免报错 | 81 | @Lazy // 循环依赖(自己依赖自己),避免报错 |
| 77 | private PayNotifyCoreServiceImpl self; | 82 | private PayNotifyCoreServiceImpl self; |
| 78 | 83 | ||
| @@ -89,7 +94,9 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { | @@ -89,7 +94,9 @@ public class PayNotifyCoreServiceImpl implements PayNotifyCoreService { | ||
| 89 | setMerchantOrderId(order.getMerchantOrderId()).setNotifyUrl(order.getNotifyUrl()); | 94 | setMerchantOrderId(order.getMerchantOrderId()).setNotifyUrl(order.getNotifyUrl()); |
| 90 | } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { | 95 | } else if (Objects.equals(task.getType(), PayNotifyTypeEnum.REFUND.getType())) { |
| 91 | // TODO 芋艿,需要实现下哈 | 96 | // TODO 芋艿,需要实现下哈 |
| 92 | - throw new UnsupportedOperationException("需要实现"); | 97 | + PayRefundDO refundDO = payRefundMapper.selectById(task.getDataId()); |
| 98 | + task.setMerchantId(refundDO.getMerchantId()).setAppId(refundDO.getAppId()) | ||
| 99 | + .setMerchantOrderId(refundDO.getMerchantOrderId()).setNotifyUrl(refundDO.getNotifyUrl()); | ||
| 93 | } | 100 | } |
| 94 | 101 | ||
| 95 | // 执行插入 | 102 | // 执行插入 |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/notify/vo/PayRefundOrderReqVO.java
| @@ -17,12 +17,15 @@ import javax.validation.constraints.NotNull; | @@ -17,12 +17,15 @@ import javax.validation.constraints.NotNull; | ||
| 17 | @AllArgsConstructor | 17 | @AllArgsConstructor |
| 18 | public class PayRefundOrderReqVO { | 18 | public class PayRefundOrderReqVO { |
| 19 | 19 | ||
| 20 | - @ApiModelProperty(value = "商户订单编号", required = true, example = "10") | ||
| 21 | - @NotEmpty(message = "商户订单号不能为空") | 20 | + @ApiModelProperty(value = "商户退款单编号", required = true, example = "10") |
| 21 | + @NotEmpty(message = "商户退款单编号不能为空") | ||
| 22 | private String merchantOrderId; | 22 | private String merchantOrderId; |
| 23 | 23 | ||
| 24 | @ApiModelProperty(value = "支付退款编号", required = true, example = "20") | 24 | @ApiModelProperty(value = "支付退款编号", required = true, example = "20") |
| 25 | @NotNull(message = "支付退款编号不能为空") | 25 | @NotNull(message = "支付退款编号不能为空") |
| 26 | private Long payRefundId; | 26 | private Long payRefundId; |
| 27 | 27 | ||
| 28 | + @ApiModelProperty(value = "退款状态(成功,失败)", required = true, example = "10") | ||
| 29 | + private Integer status; | ||
| 30 | + | ||
| 28 | } | 31 | } |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayOrderCoreService.java
| @@ -2,9 +2,7 @@ package cn.iocoder.yudao.coreservice.modules.pay.service.order; | @@ -2,9 +2,7 @@ package cn.iocoder.yudao.coreservice.modules.pay.service.order; | ||
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | 3 | import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; |
| 4 | 4 | ||
| 5 | -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO; | ||
| 6 | -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitReqDTO; | ||
| 7 | -import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderSubmitRespDTO; | 5 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.*; |
| 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; | 6 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; |
| 9 | 7 | ||
| 10 | import javax.validation.Valid; | 8 | import javax.validation.Valid; |
| @@ -50,4 +48,6 @@ public interface PayOrderCoreService { | @@ -50,4 +48,6 @@ public interface PayOrderCoreService { | ||
| 50 | */ | 48 | */ |
| 51 | void notifyPayOrder(Long channelId, String channelCode, PayNotifyDataDTO notifyData) throws Exception; | 49 | void notifyPayOrder(Long channelId, String channelCode, PayNotifyDataDTO notifyData) throws Exception; |
| 52 | 50 | ||
| 51 | + | ||
| 52 | + | ||
| 53 | } | 53 | } |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundAbstractChannelPostHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; | ||
| 6 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundMapper; | ||
| 7 | + | ||
| 8 | +/** | ||
| 9 | + * 支付退款订单渠道返回后 , 后置处理抽象类, 处理公用的逻辑 | ||
| 10 | + * @author jason | ||
| 11 | + */ | ||
| 12 | +public abstract class PayRefundAbstractChannelPostHandler implements PayRefundChannelPostHandler { | ||
| 13 | + | ||
| 14 | + private final PayOrderCoreMapper payOrderCoreMapper; | ||
| 15 | + private final PayRefundMapper payRefundMapper; | ||
| 16 | + | ||
| 17 | + public PayRefundAbstractChannelPostHandler(PayOrderCoreMapper payOrderCoreMapper, | ||
| 18 | + PayRefundMapper payRefundMapper){ | ||
| 19 | + this.payOrderCoreMapper = payOrderCoreMapper; | ||
| 20 | + this.payRefundMapper = payRefundMapper; | ||
| 21 | + } | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + /** | ||
| 25 | + * 更新退款单 | ||
| 26 | + * @param refundDO 需要更新的退款单信息 | ||
| 27 | + */ | ||
| 28 | + protected void updatePayRefund(PayRefundDO refundDO){ | ||
| 29 | + payRefundMapper.updateById(refundDO); | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 更新原始支付订单 | ||
| 35 | + * @param payOrderDO 支付订单信息 | ||
| 36 | + */ | ||
| 37 | + protected void updatePayOrder(PayOrderDO payOrderDO){ | ||
| 38 | + payOrderCoreMapper.updateById(payOrderDO); | ||
| 39 | + } | ||
| 40 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundChannelPostHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundPostReqBO; | ||
| 4 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 支付退款订单 ,渠道返回后 后置处理 | ||
| 8 | + * @author jason | ||
| 9 | + */ | ||
| 10 | +public interface PayRefundChannelPostHandler { | ||
| 11 | + | ||
| 12 | + /** | ||
| 13 | + * 支持的渠道返回值 | ||
| 14 | + * @return 支持的渠道返回值数组 | ||
| 15 | + */ | ||
| 16 | + PayChannelRespEnum[] supportHandleResp(); | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 根据渠道返回, 处理支付退款单 | ||
| 21 | + * @param respBO | ||
| 22 | + */ | ||
| 23 | + void handleRefundChannelResp(PayRefundPostReqBO respBO); | ||
| 24 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/PayRefundCoreService.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundReqBO; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundRespBO; | ||
| 5 | + | ||
| 6 | +/** | ||
| 7 | + * 退款单 Core Service | ||
| 8 | + * | ||
| 9 | + * @author jason | ||
| 10 | + */ | ||
| 11 | +public interface PayRefundCoreService { | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 提交退款申请 | ||
| 16 | + * @param reqDTO 退款申请信息 | ||
| 17 | + * @return 退款申请返回信息 | ||
| 18 | + */ | ||
| 19 | + PayRefundRespBO refund(PayRefundReqBO reqDTO); | ||
| 20 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/bo/PayRefundPostReqBO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.bo; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum; | ||
| 4 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 5 | +import lombok.AllArgsConstructor; | ||
| 6 | +import lombok.Builder; | ||
| 7 | +import lombok.Data; | ||
| 8 | +import lombok.NoArgsConstructor; | ||
| 9 | +import lombok.experimental.Accessors; | ||
| 10 | + | ||
| 11 | +@Data | ||
| 12 | +@Accessors(chain = true) | ||
| 13 | +@Builder | ||
| 14 | +@NoArgsConstructor | ||
| 15 | +@AllArgsConstructor | ||
| 16 | +public class PayRefundPostReqBO { | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 渠道的通用返回结果 | ||
| 21 | + */ | ||
| 22 | + private PayChannelRespEnum respEnum; | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + private PayRefundTypeEnum refundTypeEnum; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 已退款的总金额 | ||
| 30 | + */ | ||
| 31 | + private Long refundedAmount; | ||
| 32 | + | ||
| 33 | + /** | ||
| 34 | + * 本次退款金额 | ||
| 35 | + */ | ||
| 36 | + private Long refundAmount; | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 已退款次数 | ||
| 40 | + */ | ||
| 41 | + private Integer refundedTimes; | ||
| 42 | + | ||
| 43 | + | ||
| 44 | + /** | ||
| 45 | + * 订单编号 | ||
| 46 | + */ | ||
| 47 | + private Long orderId; | ||
| 48 | + | ||
| 49 | + /** | ||
| 50 | + * 退款单编号 | ||
| 51 | + */ | ||
| 52 | + private Long refundId; | ||
| 53 | + | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 渠道退款单号 | ||
| 57 | + */ | ||
| 58 | + private String channelRefundNo; | ||
| 59 | + | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no | ||
| 63 | + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no | ||
| 64 | + * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空 | ||
| 65 | + */ | ||
| 66 | + private String payTradeNo; | ||
| 67 | + | ||
| 68 | + | ||
| 69 | + /** | ||
| 70 | + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no | ||
| 71 | + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no | ||
| 72 | + * 退款请求单号 同一退款请求单号多次请求只退一笔。 | ||
| 73 | + */ | ||
| 74 | + private String refundReqNo; | ||
| 75 | + | ||
| 76 | + | ||
| 77 | + | ||
| 78 | + /** | ||
| 79 | + * 调用异常错误信息 | ||
| 80 | + */ | ||
| 81 | + private String exceptionMsg; | ||
| 82 | + | ||
| 83 | + | ||
| 84 | + /** | ||
| 85 | + * 渠道的错误码 | ||
| 86 | + */ | ||
| 87 | + private String channelErrCode; | ||
| 88 | + | ||
| 89 | + | ||
| 90 | + /** | ||
| 91 | + * 渠道的错误描述 | ||
| 92 | + */ | ||
| 93 | + private String channelErrMsg; | ||
| 94 | + | ||
| 95 | + | ||
| 96 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/bo/PayRefundReqBO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.bo; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Builder; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.NoArgsConstructor; | ||
| 7 | +import lombok.experimental.Accessors; | ||
| 8 | +/** | ||
| 9 | + * 退款申请单 Request DTO | ||
| 10 | + */ | ||
| 11 | +@Data | ||
| 12 | +@Accessors(chain = true) | ||
| 13 | +@Builder | ||
| 14 | +@NoArgsConstructor | ||
| 15 | +@AllArgsConstructor | ||
| 16 | +public class PayRefundReqBO { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 支付订单编号自增 | ||
| 20 | + */ | ||
| 21 | + private Long payOrderId; | ||
| 22 | + | ||
| 23 | + /** | ||
| 24 | + * 退款金额 | ||
| 25 | + */ | ||
| 26 | + private Long amount; | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 退款原因 | ||
| 30 | + */ | ||
| 31 | + private String reason; | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 商户退款订单号 | ||
| 36 | + */ | ||
| 37 | + private String merchantRefundNo; | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * 用户 IP | ||
| 41 | + */ | ||
| 42 | + private String userIp; | ||
| 43 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/bo/PayRefundRespBO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.bo; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Builder; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.NoArgsConstructor; | ||
| 7 | +import lombok.experimental.Accessors; | ||
| 8 | + | ||
| 9 | +/** | ||
| 10 | + * 退款申请单 Response DTO | ||
| 11 | + */ | ||
| 12 | +@Data | ||
| 13 | +@Accessors(chain = true) | ||
| 14 | +@Builder | ||
| 15 | +@NoArgsConstructor | ||
| 16 | +@AllArgsConstructor | ||
| 17 | +public class PayRefundRespBO { | ||
| 18 | + | ||
| 19 | + /** | ||
| 20 | + * 支付退款单编号, 自增 | ||
| 21 | + */ | ||
| 22 | + private Long refundId; | ||
| 23 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundChannelFailedHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; | ||
| 6 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundMapper; | ||
| 7 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; | ||
| 8 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; | ||
| 9 | +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService; | ||
| 10 | +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; | ||
| 11 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; | ||
| 12 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundPostReqBO; | ||
| 13 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 14 | +import org.springframework.stereotype.Service; | ||
| 15 | + | ||
| 16 | +import javax.annotation.Resource; | ||
| 17 | +import java.util.Optional; | ||
| 18 | + | ||
| 19 | +/** | ||
| 20 | + * 支付退款订单渠道返回失败的后置处理类 | ||
| 21 | + * {@link PayChannelRespEnum#CALL_EXCEPTION} | ||
| 22 | + * {@link PayChannelRespEnum#CAN_NOT_RETRY_FAILURE} | ||
| 23 | + */ | ||
| 24 | +@Service | ||
| 25 | +public class PayRefundChannelFailedHandler extends PayRefundAbstractChannelPostHandler { | ||
| 26 | + | ||
| 27 | + @Resource | ||
| 28 | + private PayNotifyCoreService payNotifyCoreService; | ||
| 29 | + | ||
| 30 | + public PayRefundChannelFailedHandler(PayOrderCoreMapper payOrderCoreMapper, PayRefundMapper payRefundMapper) { | ||
| 31 | + super(payOrderCoreMapper, payRefundMapper); | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + @Override | ||
| 35 | + public PayChannelRespEnum[] supportHandleResp() { | ||
| 36 | + return new PayChannelRespEnum[] {PayChannelRespEnum.CALL_EXCEPTION, PayChannelRespEnum.CAN_NOT_RETRY_FAILURE}; | ||
| 37 | + } | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + public void handleRefundChannelResp(PayRefundPostReqBO respBO) { | ||
| 42 | + //退款失败 | ||
| 43 | + //更新退款单表 | ||
| 44 | + PayRefundDO updateRefundDO = new PayRefundDO(); | ||
| 45 | + | ||
| 46 | + updateRefundDO.setId(respBO.getRefundId()) | ||
| 47 | + .setStatus(PayRefundStatusEnum.FAILURE.getStatus()) | ||
| 48 | + .setChannelErrorCode(respBO.getChannelErrCode()) | ||
| 49 | + .setChannelErrorMsg(Optional.ofNullable(respBO.getChannelErrMsg()) | ||
| 50 | + .orElse(respBO.getExceptionMsg())); | ||
| 51 | + updatePayRefund(updateRefundDO); | ||
| 52 | + PayOrderDO updateOrderDO = new PayOrderDO(); | ||
| 53 | + //更新订单表 | ||
| 54 | + updateOrderDO.setId(respBO.getOrderId()) | ||
| 55 | + .setRefundTimes(respBO.getRefundedTimes() + 1); | ||
| 56 | + updatePayOrder(updateOrderDO); | ||
| 57 | + // 立刻插入退款通知记录 | ||
| 58 | + // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 | ||
| 59 | + payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() | ||
| 60 | + .type(PayNotifyTypeEnum.REFUND.getType()).dataId(respBO.getRefundId()).build()); | ||
| 61 | + } | ||
| 62 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundChannelNotifyHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; | ||
| 6 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundMapper; | ||
| 7 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; | ||
| 8 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; | ||
| 9 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundPostReqBO; | ||
| 10 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 11 | +import org.springframework.stereotype.Service; | ||
| 12 | + | ||
| 13 | +/** | ||
| 14 | + * 支付退款订单渠道返回通知 {@link PayChannelRespEnum#PROCESSING_NOTIFY},后置处理类 | ||
| 15 | + * 支付宝退款单好像没有回调, 微信会触发回调 | ||
| 16 | + */ | ||
| 17 | +@Service | ||
| 18 | +public class PayRefundChannelNotifyHandler extends PayRefundAbstractChannelPostHandler { | ||
| 19 | + | ||
| 20 | + public PayRefundChannelNotifyHandler(PayOrderCoreMapper payOrderCoreMapper, | ||
| 21 | + PayRefundMapper payRefundMapper) { | ||
| 22 | + super(payOrderCoreMapper, payRefundMapper); | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + @Override | ||
| 26 | + public PayChannelRespEnum[] supportHandleResp() { | ||
| 27 | + return new PayChannelRespEnum[] {PayChannelRespEnum.PROCESSING_NOTIFY}; | ||
| 28 | + } | ||
| 29 | + | ||
| 30 | + @Override | ||
| 31 | + public void handleRefundChannelResp(PayRefundPostReqBO respBO) { | ||
| 32 | + PayRefundDO updateRefundDO = new PayRefundDO(); | ||
| 33 | + //更新退款单表 | ||
| 34 | + updateRefundDO.setId(respBO.getRefundId()) | ||
| 35 | + .setStatus(PayRefundStatusEnum.PROCESSING_NOTIFY.getStatus()); | ||
| 36 | + updatePayRefund(updateRefundDO); | ||
| 37 | + | ||
| 38 | + PayOrderDO updateOrderDO = new PayOrderDO(); | ||
| 39 | + //更新订单表 | ||
| 40 | + updateOrderDO.setId(respBO.getOrderId()) | ||
| 41 | + .setRefundTimes(respBO.getRefundedTimes() + 1); | ||
| 42 | + updatePayOrder(updateOrderDO); | ||
| 43 | + | ||
| 44 | + } | ||
| 45 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundChannelQueryHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; | ||
| 6 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundMapper; | ||
| 7 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; | ||
| 8 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; | ||
| 9 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundPostReqBO; | ||
| 10 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 11 | +import org.springframework.stereotype.Service; | ||
| 12 | + | ||
| 13 | +import java.util.Objects; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 支付退款订单渠道返回需调用查询接口的后置处理类 | ||
| 17 | + * {@link PayChannelRespEnum#PROCESSING_QUERY} //TODO 芋道源码 是不是微信有这样的情况 | ||
| 18 | + * {@link PayChannelRespEnum#READ_TIME_OUT_EXCEPTION} | ||
| 19 | + */ | ||
| 20 | +@Service | ||
| 21 | +public class PayRefundChannelQueryHandler extends PayRefundAbstractChannelPostHandler { | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + public PayRefundChannelQueryHandler(PayOrderCoreMapper payOrderCoreMapper, | ||
| 25 | + PayRefundMapper payRefundMapper) { | ||
| 26 | + super(payOrderCoreMapper, payRefundMapper); | ||
| 27 | + } | ||
| 28 | + | ||
| 29 | + @Override | ||
| 30 | + public PayChannelRespEnum[] supportHandleResp() { | ||
| 31 | + return new PayChannelRespEnum[]{PayChannelRespEnum.PROCESSING_QUERY, PayChannelRespEnum.READ_TIME_OUT_EXCEPTION}; | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + @Override | ||
| 35 | + public void handleRefundChannelResp(PayRefundPostReqBO respBO) { | ||
| 36 | + final PayChannelRespEnum respEnum = respBO.getRespEnum(); | ||
| 37 | + PayRefundStatusEnum refundStatus = | ||
| 38 | + Objects.equals(PayChannelRespEnum.PROCESSING_QUERY, respEnum) ? PayRefundStatusEnum.PROCESSING_QUERY | ||
| 39 | + : PayRefundStatusEnum.UNKNOWN_QUERY; | ||
| 40 | + //更新退款单表 | ||
| 41 | + PayRefundDO updateRefundDO = new PayRefundDO(); | ||
| 42 | + updateRefundDO.setId(respBO.getRefundId()) | ||
| 43 | + .setStatus(refundStatus.getStatus()); | ||
| 44 | + updatePayRefund(updateRefundDO); | ||
| 45 | + | ||
| 46 | + PayOrderDO updateOrderDO = new PayOrderDO(); | ||
| 47 | + //更新订单表 | ||
| 48 | + updateOrderDO.setId(respBO.getOrderId()) | ||
| 49 | + .setRefundTimes(respBO.getRefundedTimes() + 1); | ||
| 50 | + updatePayOrder(updateOrderDO); | ||
| 51 | + | ||
| 52 | + //TODO 发起查询任务 | ||
| 53 | + } | ||
| 54 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundChannelRetryHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; | ||
| 6 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundMapper; | ||
| 7 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; | ||
| 8 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; | ||
| 9 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundPostReqBO; | ||
| 10 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 11 | +import org.springframework.stereotype.Service; | ||
| 12 | + | ||
| 13 | +import java.util.Optional; | ||
| 14 | + | ||
| 15 | +/** | ||
| 16 | + * 支付退款订单渠道返回重试的后置处理类 | ||
| 17 | + * {@link PayChannelRespEnum#RETRY_FAILURE} | ||
| 18 | + */ | ||
| 19 | +@Service | ||
| 20 | +public class PayRefundChannelRetryHandler extends PayRefundAbstractChannelPostHandler { | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + public PayRefundChannelRetryHandler(PayOrderCoreMapper payOrderCoreMapper, | ||
| 24 | + PayRefundMapper payRefundMapper) { | ||
| 25 | + super(payOrderCoreMapper, payRefundMapper); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + @Override | ||
| 29 | + public PayChannelRespEnum[] supportHandleResp() { | ||
| 30 | + return new PayChannelRespEnum[] {PayChannelRespEnum.RETRY_FAILURE}; | ||
| 31 | + } | ||
| 32 | + | ||
| 33 | + @Override | ||
| 34 | + public void handleRefundChannelResp(PayRefundPostReqBO respBO) { | ||
| 35 | + | ||
| 36 | + PayRefundDO updateRefundDO = new PayRefundDO(); | ||
| 37 | + //更新退款单表 | ||
| 38 | + updateRefundDO.setId(respBO.getRefundId()) | ||
| 39 | + .setStatus(PayRefundStatusEnum.UNKNOWN_RETRY.getStatus()) | ||
| 40 | + .setChannelErrorCode(respBO.getChannelErrCode()) | ||
| 41 | + .setChannelErrorMsg(respBO.getChannelErrMsg()); | ||
| 42 | + updatePayRefund(updateRefundDO); | ||
| 43 | + | ||
| 44 | + PayOrderDO updateOrderDO = new PayOrderDO(); | ||
| 45 | + //更新订单表 | ||
| 46 | + updateOrderDO.setId(respBO.getOrderId()) | ||
| 47 | + .setRefundTimes(respBO.getRefundedTimes() + 1); | ||
| 48 | + updatePayOrder(updateOrderDO); | ||
| 49 | + | ||
| 50 | + //TODO 发起重试任务 | ||
| 51 | + } | ||
| 52 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundChannelSuccessHandler.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; | ||
| 6 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundMapper; | ||
| 7 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.notify.PayNotifyTypeEnum; | ||
| 8 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; | ||
| 9 | +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.PayNotifyCoreService; | ||
| 10 | +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.dto.PayNotifyTaskCreateReqDTO; | ||
| 11 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundAbstractChannelPostHandler; | ||
| 12 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundPostReqBO; | ||
| 13 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 14 | +import org.springframework.stereotype.Service; | ||
| 15 | + | ||
| 16 | +import javax.annotation.Resource; | ||
| 17 | +import java.util.Date; | ||
| 18 | +/** | ||
| 19 | + * 支付退款订单渠道返回退款成功的后置处理类 | ||
| 20 | + * {@link PayChannelRespEnum#SYNC_SUCCESS} | ||
| 21 | + */ | ||
| 22 | +@Service | ||
| 23 | +public class PayRefundChannelSuccessHandler extends PayRefundAbstractChannelPostHandler { | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + @Resource | ||
| 27 | + private PayNotifyCoreService payNotifyCoreService; | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + public PayRefundChannelSuccessHandler(PayOrderCoreMapper payOrderCoreMapper, | ||
| 31 | + PayRefundMapper payRefundMapper) { | ||
| 32 | + super(payOrderCoreMapper, payRefundMapper); | ||
| 33 | + } | ||
| 34 | + | ||
| 35 | + @Override | ||
| 36 | + public PayChannelRespEnum[] supportHandleResp() { | ||
| 37 | + return new PayChannelRespEnum[]{PayChannelRespEnum.SYNC_SUCCESS}; | ||
| 38 | + } | ||
| 39 | + | ||
| 40 | + @Override | ||
| 41 | + public void handleRefundChannelResp(PayRefundPostReqBO respBO) { | ||
| 42 | + //退款成功 | ||
| 43 | + PayRefundDO updateRefundDO = new PayRefundDO(); | ||
| 44 | + //更新退款单表 | ||
| 45 | + updateRefundDO.setId(respBO.getRefundId()) | ||
| 46 | + .setStatus(PayRefundStatusEnum.SUCCESS.getStatus()) | ||
| 47 | + .setChannelRefundNo(respBO.getChannelRefundNo()) | ||
| 48 | + .setSuccessTime(new Date()); | ||
| 49 | + updatePayRefund(updateRefundDO); | ||
| 50 | + | ||
| 51 | + PayOrderDO updateOrderDO = new PayOrderDO(); | ||
| 52 | + //更新订单表 | ||
| 53 | + updateOrderDO.setId(respBO.getOrderId()) | ||
| 54 | + .setRefundTimes(respBO.getRefundedTimes() + 1) | ||
| 55 | + .setRefundStatus(respBO.getRefundTypeEnum().getStatus()) | ||
| 56 | + .setRefundAmount(respBO.getRefundedAmount()+respBO.getRefundAmount()); | ||
| 57 | + updatePayOrder(updateOrderDO); | ||
| 58 | + | ||
| 59 | + // 立刻插入退款通知记录 | ||
| 60 | + // TODO 通知商户成功或者失败. 现在通知似乎没有实现, 只是回调 | ||
| 61 | + payNotifyCoreService.createPayNotifyTask(PayNotifyTaskCreateReqDTO.builder() | ||
| 62 | + .type(PayNotifyTypeEnum.REFUND.getType()).dataId(respBO.getRefundId()).build()); | ||
| 63 | + } | ||
| 64 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/service/order/impl/PayRefundCoreServiceImpl.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.service.order.impl; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.util.StrUtil; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.convert.order.PayRefundCoreConvert; | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayAppDO; | ||
| 6 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.merchant.PayChannelDO; | ||
| 7 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderDO; | ||
| 8 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayOrderExtensionDO; | ||
| 9 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.dataobject.order.PayRefundDO; | ||
| 10 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderCoreMapper; | ||
| 11 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayOrderExtensionCoreMapper; | ||
| 12 | +import cn.iocoder.yudao.coreservice.modules.pay.dal.mysql.order.PayRefundMapper; | ||
| 13 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderNotifyStatusEnum; | ||
| 14 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundTypeEnum; | ||
| 15 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayOrderStatusEnum; | ||
| 16 | +import cn.iocoder.yudao.coreservice.modules.pay.enums.order.PayRefundStatusEnum; | ||
| 17 | +import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayAppCoreService; | ||
| 18 | +import cn.iocoder.yudao.coreservice.modules.pay.service.merchant.PayChannelCoreService; | ||
| 19 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService; | ||
| 20 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundChannelPostHandler; | ||
| 21 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundPostReqBO; | ||
| 22 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundReqBO; | ||
| 23 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundRespBO; | ||
| 24 | +import cn.iocoder.yudao.coreservice.modules.pay.util.PaySeqUtils; | ||
| 25 | +import cn.iocoder.yudao.framework.pay.core.client.PayClient; | ||
| 26 | +import cn.iocoder.yudao.framework.pay.core.client.PayClientFactory; | ||
| 27 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO; | ||
| 28 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO; | ||
| 29 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 30 | +import lombok.extern.slf4j.Slf4j; | ||
| 31 | +import org.springframework.stereotype.Service; | ||
| 32 | +import org.springframework.transaction.annotation.Transactional; | ||
| 33 | + | ||
| 34 | +import javax.annotation.PostConstruct; | ||
| 35 | +import javax.annotation.Resource; | ||
| 36 | +import java.util.*; | ||
| 37 | + | ||
| 38 | +import static cn.iocoder.yudao.coreservice.modules.pay.enums.PayErrorCodeCoreConstants.*; | ||
| 39 | +import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; | ||
| 40 | + | ||
| 41 | +@Service | ||
| 42 | +@Slf4j | ||
| 43 | +public class PayRefundCoreServiceImpl implements PayRefundCoreService { | ||
| 44 | + | ||
| 45 | + @Resource | ||
| 46 | + private PayOrderCoreMapper payOrderCoreMapper; | ||
| 47 | + | ||
| 48 | + @Resource | ||
| 49 | + private PayRefundMapper payRefundMapper; | ||
| 50 | + | ||
| 51 | + @Resource | ||
| 52 | + private PayOrderExtensionCoreMapper payOrderExtensionCoreMapper; | ||
| 53 | + | ||
| 54 | + @Resource | ||
| 55 | + private PayAppCoreService payAppCoreService; | ||
| 56 | + | ||
| 57 | + @Resource | ||
| 58 | + private PayChannelCoreService payChannelCoreService; | ||
| 59 | + | ||
| 60 | + @Resource | ||
| 61 | + private PayClientFactory payClientFactory; | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 处理渠道返回结果的后置处理器 集合 | ||
| 65 | + */ | ||
| 66 | + @Resource | ||
| 67 | + private List<PayRefundChannelPostHandler> handlerList; | ||
| 68 | + | ||
| 69 | + | ||
| 70 | + private final EnumMap<PayChannelRespEnum, PayRefundChannelPostHandler> mapHandler = new EnumMap<>(PayChannelRespEnum.class); | ||
| 71 | + | ||
| 72 | + | ||
| 73 | + | ||
| 74 | + @PostConstruct | ||
| 75 | + public void init(){ | ||
| 76 | + | ||
| 77 | + if (Objects.nonNull(handlerList)) { | ||
| 78 | + handlerList.forEach(t->{ | ||
| 79 | + for (PayChannelRespEnum item : t.supportHandleResp()) { | ||
| 80 | + mapHandler.put(item, t); | ||
| 81 | + } | ||
| 82 | + }); | ||
| 83 | + } | ||
| 84 | + | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + | ||
| 88 | + @Override | ||
| 89 | + @Transactional(rollbackFor = Exception.class) | ||
| 90 | + public PayRefundRespBO refund(PayRefundReqBO reqBO) { | ||
| 91 | + // 获得 PayOrderDO | ||
| 92 | + PayOrderDO order = payOrderCoreMapper.selectById(reqBO.getPayOrderId()); | ||
| 93 | + // 校验订单是否存在 | ||
| 94 | + if (Objects.isNull(order) ) { | ||
| 95 | + throw exception(PAY_ORDER_NOT_FOUND); | ||
| 96 | + } | ||
| 97 | + // 校验 App | ||
| 98 | + PayAppDO app = payAppCoreService.validPayApp(order.getAppId()); | ||
| 99 | + // 校验支付渠道是否有效 | ||
| 100 | + PayChannelDO channel = payChannelCoreService.validPayChannel(order.getChannelId()); | ||
| 101 | + // 校验支付客户端是否正确初始化 | ||
| 102 | + PayClient client = payClientFactory.getPayClient(channel.getId()); | ||
| 103 | + if (client == null) { | ||
| 104 | + log.error("[refund][渠道编号({}) 找不到对应的支付客户端]", channel.getId()); | ||
| 105 | + throw exception(PAY_CHANNEL_CLIENT_NOT_FOUND); | ||
| 106 | + } | ||
| 107 | + | ||
| 108 | + //校验退款的条件 | ||
| 109 | + validatePayRefund(reqBO, order); | ||
| 110 | + | ||
| 111 | + //退款类型 | ||
| 112 | + PayRefundTypeEnum refundType = PayRefundTypeEnum.SOME; | ||
| 113 | + if (Objects.equals(reqBO.getAmount(), order.getAmount())) { | ||
| 114 | + refundType = PayRefundTypeEnum.ALL; | ||
| 115 | + } | ||
| 116 | + | ||
| 117 | + //退款单入库 退款单状态:生成, 没有和渠道产生交互 | ||
| 118 | + PayOrderExtensionDO orderExtensionDO = payOrderExtensionCoreMapper.selectById(order.getSuccessExtensionId()); | ||
| 119 | + PayRefundDO refundDO = PayRefundDO.builder().channelOrderNo(order.getChannelOrderNo()) | ||
| 120 | + .appId(order.getAppId()) | ||
| 121 | + .channelOrderNo(order.getChannelOrderNo()) | ||
| 122 | + .channelCode(order.getChannelCode()) | ||
| 123 | + .channelId(order.getChannelId()) | ||
| 124 | + .merchantId(order.getMerchantId()) | ||
| 125 | + .orderId(order.getId()) | ||
| 126 | + .merchantRefundNo(reqBO.getMerchantRefundNo()) | ||
| 127 | + .notifyUrl(app.getRefundNotifyUrl()) | ||
| 128 | + .payAmount(order.getAmount()) | ||
| 129 | + .refundAmount(reqBO.getAmount()) | ||
| 130 | + .userIp(reqBO.getUserIp()) | ||
| 131 | + .merchantOrderId(order.getMerchantOrderId()) | ||
| 132 | + .tradeNo(orderExtensionDO.getNo()) | ||
| 133 | + .status(PayRefundStatusEnum.CREATE.getStatus()) | ||
| 134 | + .reason(reqBO.getReason()) | ||
| 135 | + .notifyStatus(PayOrderNotifyStatusEnum.NO.getStatus()) | ||
| 136 | + .reqNo(PaySeqUtils.genRefundReqNo()) | ||
| 137 | + .type(refundType.getStatus()) | ||
| 138 | + .build(); | ||
| 139 | + | ||
| 140 | + payRefundMapper.insert(refundDO); | ||
| 141 | + | ||
| 142 | + PayRefundUnifiedReqDTO unifiedReqDTO = PayRefundUnifiedReqDTO.builder() | ||
| 143 | + .userIp(reqBO.getUserIp()) | ||
| 144 | + .channelOrderNo(refundDO.getChannelOrderNo()) | ||
| 145 | + .payTradeNo(refundDO.getTradeNo()) | ||
| 146 | + .refundReqNo(refundDO.getReqNo()) | ||
| 147 | + .amount(reqBO.getAmount()) | ||
| 148 | + .reason(refundDO.getReason()) | ||
| 149 | + .build(); | ||
| 150 | + | ||
| 151 | + //调用渠道进行退款 | ||
| 152 | + PayRefundUnifiedRespDTO refundUnifiedRespDTO = client.unifiedRefund(unifiedReqDTO); | ||
| 153 | + | ||
| 154 | + //根据渠道返回,获取退款后置处理,由postHandler 进行处理 | ||
| 155 | + PayRefundChannelPostHandler payRefundChannelPostHandler = mapHandler.get(refundUnifiedRespDTO.getRespEnum()); | ||
| 156 | + | ||
| 157 | + if(Objects.isNull(payRefundChannelPostHandler)){ | ||
| 158 | + throw exception(PAY_REFUND_POST_HANDLER_NOT_FOUND); | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + PayRefundPostReqBO bo = PayRefundCoreConvert.INSTANCE.convert(refundUnifiedRespDTO); | ||
| 162 | + bo.setRefundAmount(reqBO.getAmount()) | ||
| 163 | + .setRefundedAmount(order.getRefundAmount()) | ||
| 164 | + .setRefundedTimes(order.getRefundTimes()) | ||
| 165 | + .setRefundId(refundDO.getId()) | ||
| 166 | + .setOrderId(order.getId()) | ||
| 167 | + .setRefundTypeEnum(refundType); | ||
| 168 | + | ||
| 169 | + //调用退款的后置处理 | ||
| 170 | + payRefundChannelPostHandler.handleRefundChannelResp(bo); | ||
| 171 | + | ||
| 172 | + return PayRefundRespBO.builder().refundId(refundDO.getId()).build(); | ||
| 173 | + } | ||
| 174 | + | ||
| 175 | + | ||
| 176 | + /** | ||
| 177 | + * 校验是否进行退款 | ||
| 178 | + * @param reqBO 退款申请信息 | ||
| 179 | + * @param order 原始支付订单信息 | ||
| 180 | + */ | ||
| 181 | + private void validatePayRefund(PayRefundReqBO reqBO, PayOrderDO order) { | ||
| 182 | + | ||
| 183 | + // 校验状态,必须是支付状态 | ||
| 184 | + if (!PayOrderStatusEnum.SUCCESS.getStatus().equals(order.getStatus())) { | ||
| 185 | + throw exception(PAY_ORDER_STATUS_IS_NOT_SUCCESS); | ||
| 186 | + } | ||
| 187 | + //是否已经全额退款 | ||
| 188 | + if (PayRefundTypeEnum.ALL.getStatus().equals(order.getRefundStatus())) { | ||
| 189 | + throw exception(PAY_REFUND_ALL_REFUNDED); | ||
| 190 | + } | ||
| 191 | + // 校验金额 退款金额不能大于 原定的金额 | ||
| 192 | + if(reqBO.getAmount() + order.getRefundAmount() > order.getAmount()){ | ||
| 193 | + throw exception(PAY_REFUND_AMOUNT_EXCEED); | ||
| 194 | + } | ||
| 195 | + //校验渠道订单号 | ||
| 196 | + if (StrUtil.isEmpty(order.getChannelOrderNo())) { | ||
| 197 | + throw exception(PAY_REFUND_CHN_ORDER_NO_IS_NULL); | ||
| 198 | + } | ||
| 199 | + //TODO 退款的期限 退款次数的控制 | ||
| 200 | + | ||
| 201 | + | ||
| 202 | + } | ||
| 203 | +} |
yudao-core-service/src/main/java/cn/iocoder/yudao/coreservice/modules/pay/util/PaySeqUtils.java
0 → 100644
| 1 | +package cn.iocoder.yudao.coreservice.modules.pay.util; | ||
| 2 | + | ||
| 3 | +import cn.hutool.core.date.DatePattern; | ||
| 4 | +import cn.hutool.core.date.DateUtil; | ||
| 5 | +import cn.hutool.core.util.RandomUtil; | ||
| 6 | + | ||
| 7 | +import java.util.Date; | ||
| 8 | +import java.util.concurrent.atomic.AtomicLong; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 支付相关编号的生产 | ||
| 12 | + */ | ||
| 13 | +public class PaySeqUtils { | ||
| 14 | + | ||
| 15 | + private static final AtomicLong REFUND_REQ_NO_SEQ = new AtomicLong(0L); | ||
| 16 | + | ||
| 17 | + private static final AtomicLong MER_REFUND_NO_SEQ = new AtomicLong(0L); | ||
| 18 | + | ||
| 19 | + private static final AtomicLong MER_ORDER_NO_SEQ = new AtomicLong(0L); | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 生成商户退款单号,用于测试,应该由商户系统生成 | ||
| 23 | + * @return 商户退款单 | ||
| 24 | + */ | ||
| 25 | + public static String genMerchantRefundNo() { | ||
| 26 | + return String.format("%s%s%04d", "MR", | ||
| 27 | + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN), | ||
| 28 | + (int) MER_REFUND_NO_SEQ.getAndIncrement() % 10000); | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * 生成退款请求号 | ||
| 33 | + * @return 退款请求号 | ||
| 34 | + */ | ||
| 35 | + public static String genRefundReqNo() { | ||
| 36 | + return String.format("%s%s%04d", "RR", | ||
| 37 | + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN), | ||
| 38 | + (int) REFUND_REQ_NO_SEQ.getAndIncrement() % 10000); | ||
| 39 | + } | ||
| 40 | + | ||
| 41 | + /** | ||
| 42 | + * 生成商户订单编号号 用于测试,应该由商户系统生成 | ||
| 43 | + * @return 商户订单编号 | ||
| 44 | + */ | ||
| 45 | + public static String genMerchantOrderNo() { | ||
| 46 | + return String.format("%s%s%04d", "MO", | ||
| 47 | + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN), | ||
| 48 | + (int) MER_ORDER_NO_SEQ.getAndIncrement() % 10000); | ||
| 49 | + } | ||
| 50 | +} |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/PayClient.java
| 1 | package cn.iocoder.yudao.framework.pay.core.client; | 1 | package cn.iocoder.yudao.framework.pay.core.client; |
| 2 | 2 | ||
| 3 | 3 | ||
| 4 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; | ||
| 5 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO; | ||
| 6 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; | 4 | +import cn.iocoder.yudao.framework.pay.core.client.dto.*; |
| 7 | 5 | ||
| 8 | /** | 6 | /** |
| 9 | * 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能 | 7 | * 支付客户端,用于对接各支付渠道的 SDK,实现发起支付、退款等功能 |
| @@ -36,4 +34,12 @@ public interface PayClient { | @@ -36,4 +34,12 @@ public interface PayClient { | ||
| 36 | */ | 34 | */ |
| 37 | PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception; | 35 | PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception; |
| 38 | 36 | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * 调用支付渠道,进行退款 | ||
| 40 | + * @param reqDTO 统一退款请求信息 | ||
| 41 | + * @return 各支付渠道的统一返回结果 | ||
| 42 | + */ | ||
| 43 | + PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO); | ||
| 44 | + | ||
| 39 | } | 45 | } |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayNotifyDataDTO.java
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedReqDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.pay.core.client.dto; | ||
| 2 | + | ||
| 3 | +import lombok.AllArgsConstructor; | ||
| 4 | +import lombok.Builder; | ||
| 5 | +import lombok.Data; | ||
| 6 | +import lombok.NoArgsConstructor; | ||
| 7 | +import lombok.experimental.Accessors; | ||
| 8 | +import org.hibernate.validator.constraints.URL; | ||
| 9 | + | ||
| 10 | +import javax.validation.constraints.DecimalMin; | ||
| 11 | +import javax.validation.constraints.NotEmpty; | ||
| 12 | +import javax.validation.constraints.NotNull; | ||
| 13 | + | ||
| 14 | +/** | ||
| 15 | + * 统一 退款 Request DTO | ||
| 16 | + * | ||
| 17 | + * @author jason | ||
| 18 | + */ | ||
| 19 | +@Accessors(chain = true) | ||
| 20 | +@Builder | ||
| 21 | +@NoArgsConstructor | ||
| 22 | +@AllArgsConstructor | ||
| 23 | +@Data | ||
| 24 | +public class PayRefundUnifiedReqDTO { | ||
| 25 | + | ||
| 26 | + /** | ||
| 27 | + * 用户 IP | ||
| 28 | + */ | ||
| 29 | + private String userIp; | ||
| 30 | + | ||
| 31 | + /** | ||
| 32 | + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 transaction_id | ||
| 33 | + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 trade_no | ||
| 34 | + * 渠道订单号 | ||
| 35 | + */ | ||
| 36 | + private String channelOrderNo; | ||
| 37 | + | ||
| 38 | + | ||
| 39 | + /** | ||
| 40 | + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no | ||
| 41 | + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no | ||
| 42 | + * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空 | ||
| 43 | + */ | ||
| 44 | + private String payTradeNo; | ||
| 45 | + | ||
| 46 | + | ||
| 47 | + /** | ||
| 48 | + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no | ||
| 49 | + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no | ||
| 50 | + * 退款请求单号 同一退款请求单号多次请求只退一笔。 | ||
| 51 | + */ | ||
| 52 | + @NotEmpty(message = "退款请求单号") | ||
| 53 | + private String refundReqNo; | ||
| 54 | + | ||
| 55 | + | ||
| 56 | + /** | ||
| 57 | + * 退款原因 | ||
| 58 | + */ | ||
| 59 | + @NotEmpty(message = "退款原因不能为空") | ||
| 60 | + private String reason; | ||
| 61 | + | ||
| 62 | + | ||
| 63 | + /** | ||
| 64 | + * 退款金额,单位:分 | ||
| 65 | + */ | ||
| 66 | + @NotNull(message = "退款金额不能为空") | ||
| 67 | + @DecimalMin(value = "0", inclusive = false, message = "支付金额必须大于零") | ||
| 68 | + private Long amount; | ||
| 69 | + | ||
| 70 | + | ||
| 71 | + | ||
| 72 | + | ||
| 73 | + /** | ||
| 74 | + * 退款结果 notify 回调地址, 支付宝退款不需要回调地址, 微信需要 | ||
| 75 | + */ | ||
| 76 | + @URL(message = "支付结果的 notify 回调地址必须是 URL 格式") | ||
| 77 | + private String notifyUrl; | ||
| 78 | +} |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/dto/PayRefundUnifiedRespDTO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.pay.core.client.dto; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 4 | +import lombok.AllArgsConstructor; | ||
| 5 | +import lombok.Builder; | ||
| 6 | +import lombok.Data; | ||
| 7 | +import lombok.NoArgsConstructor; | ||
| 8 | +import lombok.experimental.Accessors; | ||
| 9 | + | ||
| 10 | +import javax.validation.constraints.NotEmpty; | ||
| 11 | +/** | ||
| 12 | + * 统一 退款 Response DTO | ||
| 13 | + * | ||
| 14 | + * @author jason | ||
| 15 | + */ | ||
| 16 | +@Accessors(chain = true) | ||
| 17 | +@Builder | ||
| 18 | +@NoArgsConstructor | ||
| 19 | +@AllArgsConstructor | ||
| 20 | +@Data | ||
| 21 | +public class PayRefundUnifiedRespDTO { | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + /** | ||
| 26 | + * 渠道的通用返回结果 | ||
| 27 | + */ | ||
| 28 | + private PayChannelRespEnum respEnum; | ||
| 29 | + | ||
| 30 | + | ||
| 31 | + | ||
| 32 | + /** | ||
| 33 | + * 渠道退款单号 | ||
| 34 | + */ | ||
| 35 | + private String channelRefundNo; | ||
| 36 | + | ||
| 37 | + | ||
| 38 | + /** | ||
| 39 | + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_trade_no | ||
| 40 | + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_trade_no | ||
| 41 | + * 支付交易号 {PayOrderExtensionDO no字段} 和 渠道订单号 不能同时为空 | ||
| 42 | + */ | ||
| 43 | + private String payTradeNo; | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * https://api.mch.weixin.qq.com/v3/refund/domestic/refunds 中的 out_refund_no | ||
| 48 | + * https://opendocs.alipay.com/apis alipay.trade.refund 中的 out_request_no | ||
| 49 | + * 退款请求单号 同一退款请求单号多次请求只退一笔。 | ||
| 50 | + */ | ||
| 51 | + private String refundReqNo; | ||
| 52 | + | ||
| 53 | + | ||
| 54 | + | ||
| 55 | + /** | ||
| 56 | + * 调用异常错误信息 | ||
| 57 | + */ | ||
| 58 | + private String exceptionMsg; | ||
| 59 | + | ||
| 60 | + | ||
| 61 | + /** | ||
| 62 | + * 渠道的错误码 | ||
| 63 | + */ | ||
| 64 | + private String channelErrCode; | ||
| 65 | + | ||
| 66 | + | ||
| 67 | + /** | ||
| 68 | + * 渠道的错误描述 | ||
| 69 | + */ | ||
| 70 | + private String channelErrMsg; | ||
| 71 | + | ||
| 72 | + //TODO 退款资金渠 ??? | ||
| 73 | +} |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/AbstractPayClient.java
| @@ -6,8 +6,13 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient; | @@ -6,8 +6,13 @@ import cn.iocoder.yudao.framework.pay.core.client.PayClient; | ||
| 6 | import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; | 6 | import cn.iocoder.yudao.framework.pay.core.client.PayClientConfig; |
| 7 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; | 7 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; |
| 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; | 8 | import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; |
| 9 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedReqDTO; | ||
| 10 | +import cn.iocoder.yudao.framework.pay.core.client.dto.PayRefundUnifiedRespDTO; | ||
| 11 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 9 | import lombok.extern.slf4j.Slf4j; | 12 | import lombok.extern.slf4j.Slf4j; |
| 10 | 13 | ||
| 14 | +import java.net.SocketTimeoutException; | ||
| 15 | + | ||
| 11 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | 16 | import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; |
| 12 | 17 | ||
| 13 | /** | 18 | /** |
| @@ -22,6 +27,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen | @@ -22,6 +27,7 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen | ||
| 22 | * 渠道编号 | 27 | * 渠道编号 |
| 23 | */ | 28 | */ |
| 24 | private final Long channelId; | 29 | private final Long channelId; |
| 30 | + | ||
| 25 | /** | 31 | /** |
| 26 | * 渠道编码 | 32 | * 渠道编码 |
| 27 | */ | 33 | */ |
| @@ -91,7 +97,37 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen | @@ -91,7 +97,37 @@ public abstract class AbstractPayClient<Config extends PayClientConfig> implemen | ||
| 91 | return result; | 97 | return result; |
| 92 | } | 98 | } |
| 93 | 99 | ||
| 100 | + | ||
| 101 | + | ||
| 94 | protected abstract PayCommonResult<?> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) | 102 | protected abstract PayCommonResult<?> doUnifiedOrder(PayOrderUnifiedReqDTO reqDTO) |
| 95 | throws Throwable; | 103 | throws Throwable; |
| 96 | 104 | ||
| 105 | + | ||
| 106 | + @Override | ||
| 107 | + public PayRefundUnifiedRespDTO unifiedRefund(PayRefundUnifiedReqDTO reqDTO) { | ||
| 108 | + | ||
| 109 | + PayRefundUnifiedRespDTO resp; | ||
| 110 | + try { | ||
| 111 | + resp = doUnifiedRefund(reqDTO); | ||
| 112 | + }catch (SocketTimeoutException ex){ | ||
| 113 | + //网络 read time out 异常 | ||
| 114 | + log.error("[unifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), ex); | ||
| 115 | + return PayRefundUnifiedRespDTO.builder() | ||
| 116 | + .exceptionMsg(ex.getMessage()) | ||
| 117 | + .respEnum(PayChannelRespEnum.READ_TIME_OUT_EXCEPTION) | ||
| 118 | + .build(); | ||
| 119 | + } catch (Throwable ex) { | ||
| 120 | + // 打印异常日志 | ||
| 121 | + log.error("[unifiedRefund][request({}) 发起退款失败]", toJsonString(reqDTO), ex); | ||
| 122 | + return PayRefundUnifiedRespDTO.builder() | ||
| 123 | + .exceptionMsg(ex.getMessage()) | ||
| 124 | + .respEnum(PayChannelRespEnum.CALL_EXCEPTION) | ||
| 125 | + .build(); | ||
| 126 | + } | ||
| 127 | + return resp; | ||
| 128 | + } | ||
| 129 | + | ||
| 130 | + | ||
| 131 | + protected abstract PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable; | ||
| 132 | + | ||
| 97 | } | 133 | } |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayQrPayClient.java
| @@ -3,9 +3,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; | @@ -3,9 +3,7 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; | ||
| 3 | import cn.hutool.core.bean.BeanUtil; | 3 | import cn.hutool.core.bean.BeanUtil; |
| 4 | import cn.hutool.core.date.DateUtil; | 4 | import cn.hutool.core.date.DateUtil; |
| 5 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; | 5 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; |
| 6 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; | ||
| 7 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO; | ||
| 8 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; | 6 | +import cn.iocoder.yudao.framework.pay.core.client.dto.*; |
| 9 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | 7 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; |
| 10 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; | 8 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; |
| 11 | import com.alipay.api.AlipayApiException; | 9 | import com.alipay.api.AlipayApiException; |
| @@ -71,6 +69,8 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig> | @@ -71,6 +69,8 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig> | ||
| 71 | return PayCommonResult.build(response.getCode(), response.getMsg(), response, codeMapping); | 69 | return PayCommonResult.build(response.getCode(), response.getMsg(), response, codeMapping); |
| 72 | } | 70 | } |
| 73 | 71 | ||
| 72 | + | ||
| 73 | + | ||
| 74 | @Override | 74 | @Override |
| 75 | public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception { | 75 | public PayOrderNotifyRespDTO parseOrderNotify(PayNotifyDataDTO data) throws Exception { |
| 76 | //结果转换 | 76 | //结果转换 |
| @@ -82,4 +82,10 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig> | @@ -82,4 +82,10 @@ public class AlipayQrPayClient extends AbstractPayClient<AlipayPayClientConfig> | ||
| 82 | .data(data.getBody()).build(); | 82 | .data(data.getBody()).build(); |
| 83 | 83 | ||
| 84 | } | 84 | } |
| 85 | + | ||
| 86 | + @Override | ||
| 87 | + protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { | ||
| 88 | + //TODO 需要实现 | ||
| 89 | + throw new UnsupportedOperationException(); | ||
| 90 | + } | ||
| 85 | } | 91 | } |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/alipay/AlipayWapPayClient.java
| @@ -3,28 +3,35 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; | @@ -3,28 +3,35 @@ package cn.iocoder.yudao.framework.pay.core.client.impl.alipay; | ||
| 3 | import cn.hutool.core.bean.BeanUtil; | 3 | import cn.hutool.core.bean.BeanUtil; |
| 4 | import cn.hutool.core.date.DateUtil; | 4 | import cn.hutool.core.date.DateUtil; |
| 5 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; | 5 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; |
| 6 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; | ||
| 7 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO; | ||
| 8 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; | 6 | +import cn.iocoder.yudao.framework.pay.core.client.dto.*; |
| 9 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | 7 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; |
| 10 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; | 8 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; |
| 9 | +import cn.iocoder.yudao.framework.pay.core.enums.PayChannelRespEnum; | ||
| 11 | import com.alipay.api.AlipayApiException; | 10 | import com.alipay.api.AlipayApiException; |
| 12 | import com.alipay.api.AlipayConfig; | 11 | import com.alipay.api.AlipayConfig; |
| 13 | import com.alipay.api.DefaultAlipayClient; | 12 | import com.alipay.api.DefaultAlipayClient; |
| 13 | +import com.alipay.api.domain.AlipayTradeRefundModel; | ||
| 14 | import com.alipay.api.domain.AlipayTradeWapPayModel; | 14 | import com.alipay.api.domain.AlipayTradeWapPayModel; |
| 15 | +import com.alipay.api.request.AlipayTradeRefundRequest; | ||
| 15 | import com.alipay.api.request.AlipayTradeWapPayRequest; | 16 | import com.alipay.api.request.AlipayTradeWapPayRequest; |
| 17 | +import com.alipay.api.response.AlipayTradeRefundResponse; | ||
| 16 | import com.alipay.api.response.AlipayTradeWapPayResponse; | 18 | import com.alipay.api.response.AlipayTradeWapPayResponse; |
| 17 | import lombok.SneakyThrows; | 19 | import lombok.SneakyThrows; |
| 20 | +import lombok.extern.slf4j.Slf4j; | ||
| 18 | 21 | ||
| 22 | +import java.net.SocketTimeoutException; | ||
| 19 | import java.util.Map; | 23 | import java.util.Map; |
| 20 | import java.util.Objects; | 24 | import java.util.Objects; |
| 21 | 25 | ||
| 26 | +import static cn.iocoder.yudao.framework.common.util.json.JsonUtils.toJsonString; | ||
| 27 | + | ||
| 22 | /** | 28 | /** |
| 23 | * 支付宝【手机网站】的 PayClient 实现类 | 29 | * 支付宝【手机网站】的 PayClient 实现类 |
| 24 | * 文档:https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay | 30 | * 文档:https://opendocs.alipay.com/apis/api_1/alipay.trade.wap.pay |
| 25 | * | 31 | * |
| 26 | * @author 芋道源码 | 32 | * @author 芋道源码 |
| 27 | */ | 33 | */ |
| 34 | +@Slf4j | ||
| 28 | public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig> { | 35 | public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig> { |
| 29 | 36 | ||
| 30 | private DefaultAlipayClient client; | 37 | private DefaultAlipayClient client; |
| @@ -96,4 +103,62 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig> | @@ -96,4 +103,62 @@ public class AlipayWapPayClient extends AbstractPayClient<AlipayPayClientConfig> | ||
| 96 | .data(data.getBody()).build(); | 103 | .data(data.getBody()).build(); |
| 97 | } | 104 | } |
| 98 | 105 | ||
| 106 | + | ||
| 107 | + @Override | ||
| 108 | + protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) { | ||
| 109 | + AlipayTradeRefundModel model=new AlipayTradeRefundModel(); | ||
| 110 | + model.setTradeNo(reqDTO.getChannelOrderNo()); | ||
| 111 | + model.setOutTradeNo(reqDTO.getPayTradeNo()); | ||
| 112 | + model.setOutRequestNo(reqDTO.getRefundReqNo()); | ||
| 113 | + model.setRefundAmount(calculateAmount(reqDTO.getAmount()).toString()); | ||
| 114 | + model.setRefundReason(reqDTO.getReason()); | ||
| 115 | + AlipayTradeRefundRequest refundRequest = new AlipayTradeRefundRequest(); | ||
| 116 | + refundRequest.setBizModel(model); | ||
| 117 | + PayRefundUnifiedRespDTO respDTO = new PayRefundUnifiedRespDTO(); | ||
| 118 | + try { | ||
| 119 | + AlipayTradeRefundResponse response = client.execute(refundRequest); | ||
| 120 | + log.info("[doUnifiedRefund][response({}) 发起退款 渠道返回", toJsonString(response)); | ||
| 121 | + if (response.isSuccess()) { | ||
| 122 | + //退款成功 | ||
| 123 | + //TODO 沙箱环境 返回 的tradeNo(渠道退款单号) 和 订单的tradNo 是一个值,是不是理解不对? | ||
| 124 | + respDTO.setRespEnum(PayChannelRespEnum.SYNC_SUCCESS) | ||
| 125 | + .setChannelRefundNo(response.getTradeNo()) | ||
| 126 | + .setPayTradeNo(response.getOutTradeNo()); | ||
| 127 | + }else{ | ||
| 128 | + //特殊处理 sub_code ACQ.SYSTEM_ERROR(系统错误), 需要调用重试任务 | ||
| 129 | + //沙箱环境返回的貌似是”aop.ACQ.SYSTEM_ERROR“, 用contain | ||
| 130 | + if (response.getSubCode().contains("ACQ.SYSTEM_ERROR")) { | ||
| 131 | + respDTO.setRespEnum(PayChannelRespEnum.RETRY_FAILURE) | ||
| 132 | + .setChannelErrMsg(response.getSubMsg()) | ||
| 133 | + .setChannelErrCode(response.getSubCode()); | ||
| 134 | + }else{ | ||
| 135 | + //其他当做不可以重试的错误 | ||
| 136 | + respDTO.setRespEnum(PayChannelRespEnum.CAN_NOT_RETRY_FAILURE) | ||
| 137 | + .setChannelErrCode(response.getSubCode()) | ||
| 138 | + .setChannelErrMsg(response.getSubMsg()); | ||
| 139 | + } | ||
| 140 | + } | ||
| 141 | + return respDTO; | ||
| 142 | + } catch (AlipayApiException e) { | ||
| 143 | + //TODO 记录异常日志 | ||
| 144 | + log.error("[doUnifiedRefund][request({}) 发起退款失败,网络读超时,退款状态未知]", toJsonString(reqDTO), e); | ||
| 145 | + Throwable cause = e.getCause(); | ||
| 146 | + //网络 read time out 异常, 退款状态未知 | ||
| 147 | + if (cause instanceof SocketTimeoutException) { | ||
| 148 | + respDTO.setExceptionMsg(e.getMessage()) | ||
| 149 | + .setRespEnum(PayChannelRespEnum.READ_TIME_OUT_EXCEPTION); | ||
| 150 | + }else{ | ||
| 151 | + respDTO.setExceptionMsg(e.getMessage()) | ||
| 152 | + .setChannelErrCode(e.getErrCode()) | ||
| 153 | + .setChannelErrMsg(e.getErrMsg()) | ||
| 154 | + .setRespEnum(PayChannelRespEnum.CALL_EXCEPTION); | ||
| 155 | + } | ||
| 156 | + | ||
| 157 | + return respDTO; | ||
| 158 | + | ||
| 159 | + } | ||
| 160 | + | ||
| 161 | + | ||
| 162 | + } | ||
| 163 | + | ||
| 99 | } | 164 | } |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/client/impl/wx/WXPubPayClient.java
| @@ -8,9 +8,7 @@ import cn.hutool.core.util.StrUtil; | @@ -8,9 +8,7 @@ import cn.hutool.core.util.StrUtil; | ||
| 8 | import cn.iocoder.yudao.framework.common.util.io.FileUtils; | 8 | import cn.iocoder.yudao.framework.common.util.io.FileUtils; |
| 9 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; | 9 | import cn.iocoder.yudao.framework.common.util.object.ObjectUtils; |
| 10 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; | 10 | import cn.iocoder.yudao.framework.pay.core.client.PayCommonResult; |
| 11 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayNotifyDataDTO; | ||
| 12 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderNotifyRespDTO; | ||
| 13 | -import cn.iocoder.yudao.framework.pay.core.client.dto.PayOrderUnifiedReqDTO; | 11 | +import cn.iocoder.yudao.framework.pay.core.client.dto.*; |
| 14 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; | 12 | import cn.iocoder.yudao.framework.pay.core.client.impl.AbstractPayClient; |
| 15 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; | 13 | import cn.iocoder.yudao.framework.pay.core.enums.PayChannelEnum; |
| 16 | import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; | 14 | import com.github.binarywang.wxpay.bean.notify.WxPayOrderNotifyResult; |
| @@ -92,6 +90,7 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> { | @@ -92,6 +90,7 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> { | ||
| 92 | return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping); | 90 | return PayCommonResult.build(CODE_SUCCESS, MESSAGE_SUCCESS, response, codeMapping); |
| 93 | } | 91 | } |
| 94 | 92 | ||
| 93 | + | ||
| 95 | private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { | 94 | private WxPayMpOrderResult unifiedOrderV2(PayOrderUnifiedReqDTO reqDTO) throws WxPayException { |
| 96 | // 构建 WxPayUnifiedOrderRequest 对象 | 95 | // 构建 WxPayUnifiedOrderRequest 对象 |
| 97 | WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() | 96 | WxPayUnifiedOrderRequest request = WxPayUnifiedOrderRequest.newBuilder() |
| @@ -142,4 +141,11 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> { | @@ -142,4 +141,11 @@ public class WXPubPayClient extends AbstractPayClient<WXPayClientConfig> { | ||
| 142 | .data(data.getBody()).build(); | 141 | .data(data.getBody()).build(); |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 144 | + | ||
| 145 | + @Override | ||
| 146 | + protected PayRefundUnifiedRespDTO doUnifiedRefund(PayRefundUnifiedReqDTO reqDTO) throws Throwable { | ||
| 147 | + //TODO 需要实现 | ||
| 148 | + throw new UnsupportedOperationException(); | ||
| 149 | + } | ||
| 150 | + | ||
| 145 | } | 151 | } |
yudao-framework/yudao-spring-boot-starter-biz-pay/src/main/java/cn/iocoder/yudao/framework/pay/core/enums/PayChannelRespEnum.java
0 → 100644
| 1 | +package cn.iocoder.yudao.framework.pay.core.enums; | ||
| 2 | + | ||
| 3 | +/** | ||
| 4 | + * 统一的渠道返回结果 | ||
| 5 | + * @author jason | ||
| 6 | + */ | ||
| 7 | +public enum PayChannelRespEnum { | ||
| 8 | + | ||
| 9 | + /** | ||
| 10 | + * 接口通讯正常返回, 并明确处理成功 ,不需要通过查询或者回调接口 进行下一步处理 | ||
| 11 | + */ | ||
| 12 | + SYNC_SUCCESS, | ||
| 13 | + | ||
| 14 | + /** | ||
| 15 | + * 接口通讯正常返回, 但返回错误,并且不能通过重试解决的错误, | ||
| 16 | + * 如提交失败, 业务错误(余额不足), 或者参数错误, 签名错误, 需要干预后才能处理 | ||
| 17 | + */ | ||
| 18 | + CAN_NOT_RETRY_FAILURE, | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + /** | ||
| 22 | + * 接口通讯正常返回, | ||
| 23 | + * 可以通过重试解决的错误. 如系统超时, 系统繁忙。状态未知 不能改变请求参数,如退款单请求号,重发请求 | ||
| 24 | + */ | ||
| 25 | + RETRY_FAILURE, | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + /** | ||
| 29 | + * 接口通讯正常返回,但是处理结果 需要渠道回调进行下一步处理 | ||
| 30 | + */ | ||
| 31 | + PROCESSING_NOTIFY, | ||
| 32 | + | ||
| 33 | + | ||
| 34 | + /** | ||
| 35 | + * 接口通讯正常返回, 但是处理结果,需要调用查询接口 进行查询 | ||
| 36 | + */ | ||
| 37 | + PROCESSING_QUERY, | ||
| 38 | + | ||
| 39 | + | ||
| 40 | + /** | ||
| 41 | + * 本系统调用渠道接口异常, 渠道接口请求未正常发送, 本系统不可预知的异常,较少发生, 可认为失败。 不用重试. | ||
| 42 | + */ | ||
| 43 | + CALL_EXCEPTION, | ||
| 44 | + | ||
| 45 | + | ||
| 46 | + /** | ||
| 47 | + * 本系统调用渠道接口成功, 但是未接受到请求结果,较少发生(需合理设置read time out ) 结果未知。 需要调用查询接口进行查询 | ||
| 48 | + */ | ||
| 49 | + READ_TIME_OUT_EXCEPTION; | ||
| 50 | +} |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/controller/order/PayRefundController.java
0 → 100644
| 1 | +package cn.iocoder.yudao.userserver.modules.pay.controller.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayRefundCoreService; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundReqBO; | ||
| 5 | +import cn.iocoder.yudao.coreservice.modules.pay.util.PaySeqUtils; | ||
| 6 | +import cn.iocoder.yudao.framework.common.pojo.CommonResult; | ||
| 7 | +import cn.iocoder.yudao.userserver.modules.pay.controller.order.vo.PayRefundReqVO; | ||
| 8 | +import cn.iocoder.yudao.userserver.modules.pay.controller.order.vo.PayRefundRespVO; | ||
| 9 | +import cn.iocoder.yudao.userserver.modules.pay.convert.order.PayRefundConvert; | ||
| 10 | +import io.swagger.annotations.Api; | ||
| 11 | +import io.swagger.annotations.ApiOperation; | ||
| 12 | +import lombok.extern.slf4j.Slf4j; | ||
| 13 | +import org.springframework.validation.annotation.Validated; | ||
| 14 | +import org.springframework.web.bind.annotation.PostMapping; | ||
| 15 | +import org.springframework.web.bind.annotation.RequestBody; | ||
| 16 | +import org.springframework.web.bind.annotation.RequestMapping; | ||
| 17 | +import org.springframework.web.bind.annotation.RestController; | ||
| 18 | + | ||
| 19 | +import javax.annotation.Resource; | ||
| 20 | + | ||
| 21 | +import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP; | ||
| 22 | + | ||
| 23 | +@Api(tags = "退款订单") | ||
| 24 | +@RestController | ||
| 25 | +@RequestMapping("/pay/order") | ||
| 26 | +@Validated | ||
| 27 | +@Slf4j | ||
| 28 | +public class PayRefundController { | ||
| 29 | + | ||
| 30 | + @Resource | ||
| 31 | + private PayRefundCoreService payRefundCoreService; | ||
| 32 | + | ||
| 33 | + @PostMapping("/refund") | ||
| 34 | + @ApiOperation("提交退款订单") | ||
| 35 | + public CommonResult<PayRefundRespVO> refund(@RequestBody PayRefundReqVO reqVO){ | ||
| 36 | + PayRefundReqBO reqBO = PayRefundConvert.INSTANCE.convert(reqVO); | ||
| 37 | + reqBO.setUserIp(getClientIP()); | ||
| 38 | + //TODO 测试暂时模拟生成商户退款订单 | ||
| 39 | + reqBO.setMerchantRefundNo(PaySeqUtils.genMerchantRefundNo()); | ||
| 40 | + return CommonResult.success( PayRefundConvert.INSTANCE.convert(payRefundCoreService.refund(reqBO))); | ||
| 41 | + } | ||
| 42 | +} |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/controller/order/vo/PayRefundReqVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.userserver.modules.pay.controller.order.vo; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import io.swagger.annotations.ApiModelProperty; | ||
| 5 | +import lombok.AllArgsConstructor; | ||
| 6 | +import lombok.Data; | ||
| 7 | +import lombok.NoArgsConstructor; | ||
| 8 | + | ||
| 9 | +import javax.validation.constraints.NotEmpty; | ||
| 10 | + | ||
| 11 | +@ApiModel("退款订单 Req VO") | ||
| 12 | +@Data | ||
| 13 | +@NoArgsConstructor | ||
| 14 | +@AllArgsConstructor | ||
| 15 | +public class PayRefundReqVO { | ||
| 16 | + | ||
| 17 | + @ApiModelProperty(value = "支付订单编号自增", required = true, example = "10") | ||
| 18 | + @NotEmpty(message = "支付订单编号自增") | ||
| 19 | + private Long payOrderId; | ||
| 20 | + | ||
| 21 | + @ApiModelProperty(value = "退款金额", required = true, example = "1") | ||
| 22 | + @NotEmpty(message = "退款金额") | ||
| 23 | + private Long amount; | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + @ApiModelProperty(value = "退款原因", required = true, example = "不喜欢") | ||
| 27 | + @NotEmpty(message = "退款原因") | ||
| 28 | + private String reason; | ||
| 29 | + | ||
| 30 | + @ApiModelProperty(value = "商户退款订单号", required = true, example = "MR202111180000000001") | ||
| 31 | + //TODO 测试暂时模拟生成 | ||
| 32 | + //@NotEmpty(message = "商户退款订单号") | ||
| 33 | + private String merchantRefundNo; | ||
| 34 | + | ||
| 35 | +} |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/controller/order/vo/PayRefundRespVO.java
0 → 100644
| 1 | +package cn.iocoder.yudao.userserver.modules.pay.controller.order.vo; | ||
| 2 | + | ||
| 3 | +import io.swagger.annotations.ApiModel; | ||
| 4 | +import lombok.AllArgsConstructor; | ||
| 5 | +import lombok.Builder; | ||
| 6 | +import lombok.Data; | ||
| 7 | +import lombok.NoArgsConstructor; | ||
| 8 | +import lombok.experimental.Accessors; | ||
| 9 | + | ||
| 10 | +@ApiModel("提交退款订单 Response VO") | ||
| 11 | +@Data | ||
| 12 | +@Accessors(chain = true) | ||
| 13 | +@Builder | ||
| 14 | +@NoArgsConstructor | ||
| 15 | +@AllArgsConstructor | ||
| 16 | +public class PayRefundRespVO { | ||
| 17 | + | ||
| 18 | + /** | ||
| 19 | + * 支付退款单编号, 自增 | ||
| 20 | + */ | ||
| 21 | + private Long refundId; | ||
| 22 | +} |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/convert/order/PayRefundConvert.java
0 → 100644
| 1 | +package cn.iocoder.yudao.userserver.modules.pay.convert.order; | ||
| 2 | + | ||
| 3 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundReqBO; | ||
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.service.order.bo.PayRefundRespBO; | ||
| 5 | +import cn.iocoder.yudao.userserver.modules.pay.controller.order.vo.PayRefundReqVO; | ||
| 6 | +import cn.iocoder.yudao.userserver.modules.pay.controller.order.vo.PayRefundRespVO; | ||
| 7 | +import org.mapstruct.Mapper; | ||
| 8 | +import org.mapstruct.factory.Mappers; | ||
| 9 | + | ||
| 10 | +/** | ||
| 11 | + * 支付退款 Convert | ||
| 12 | + * @author jason | ||
| 13 | + */ | ||
| 14 | +@Mapper | ||
| 15 | +public interface PayRefundConvert { | ||
| 16 | + | ||
| 17 | + PayRefundConvert INSTANCE = Mappers.getMapper(PayRefundConvert.class); | ||
| 18 | + | ||
| 19 | + PayRefundReqBO convert(PayRefundReqVO reqVO); | ||
| 20 | + | ||
| 21 | + PayRefundRespVO convert(PayRefundRespBO respBO); | ||
| 22 | +} |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/convert/package-info.java
0 → 100644
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/pay/convert/《芋道 Spring Boot 对象转换 MapStruct 入门》.md
0 → 100644
| 1 | +<http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao> |
yudao-user-server/src/main/java/cn/iocoder/yudao/userserver/modules/shop/controller/ShopOrderController.java
| 1 | package cn.iocoder.yudao.userserver.modules.shop.controller; | 1 | package cn.iocoder.yudao.userserver.modules.shop.controller; |
| 2 | 2 | ||
| 3 | import cn.iocoder.yudao.coreservice.modules.pay.service.notify.vo.PayNotifyOrderReqVO; | 3 | import cn.iocoder.yudao.coreservice.modules.pay.service.notify.vo.PayNotifyOrderReqVO; |
| 4 | +import cn.iocoder.yudao.coreservice.modules.pay.service.notify.vo.PayRefundOrderReqVO; | ||
| 4 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService; | 5 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.PayOrderCoreService; |
| 5 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO; | 6 | import cn.iocoder.yudao.coreservice.modules.pay.service.order.dto.PayOrderCreateReqDTO; |
| 7 | +import cn.iocoder.yudao.coreservice.modules.pay.util.PaySeqUtils; | ||
| 6 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; | 8 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 7 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; | 9 | import cn.iocoder.yudao.framework.common.util.date.DateUtils; |
| 8 | import cn.iocoder.yudao.userserver.modules.shop.controller.vo.ShopOrderCreateRespVO; | 10 | import cn.iocoder.yudao.userserver.modules.shop.controller.vo.ShopOrderCreateRespVO; |
| @@ -43,10 +45,10 @@ public class ShopOrderController { | @@ -43,10 +45,10 @@ public class ShopOrderController { | ||
| 43 | PayOrderCreateReqDTO reqDTO = new PayOrderCreateReqDTO(); | 45 | PayOrderCreateReqDTO reqDTO = new PayOrderCreateReqDTO(); |
| 44 | reqDTO.setAppId(6L); | 46 | reqDTO.setAppId(6L); |
| 45 | reqDTO.setUserIp(getClientIP()); | 47 | reqDTO.setUserIp(getClientIP()); |
| 46 | - reqDTO.setMerchantOrderId(String.valueOf(System.currentTimeMillis())); | 48 | + reqDTO.setMerchantOrderId(PaySeqUtils.genMerchantOrderNo()); |
| 47 | reqDTO.setSubject("标题:" + shopOrderId); | 49 | reqDTO.setSubject("标题:" + shopOrderId); |
| 48 | reqDTO.setBody("内容:" + shopOrderId); | 50 | reqDTO.setBody("内容:" + shopOrderId); |
| 49 | - reqDTO.setAmount(1); // 单位:分 | 51 | + reqDTO.setAmount(200); // 单位:分 |
| 50 | reqDTO.setExpireTime(DateUtils.addTime(Duration.ofDays(1))); | 52 | reqDTO.setExpireTime(DateUtils.addTime(Duration.ofDays(1))); |
| 51 | Long payOrderId = payOrderCoreService.createPayOrder(reqDTO); | 53 | Long payOrderId = payOrderCoreService.createPayOrder(reqDTO); |
| 52 | 54 | ||
| @@ -55,6 +57,8 @@ public class ShopOrderController { | @@ -55,6 +57,8 @@ public class ShopOrderController { | ||
| 55 | .payOrderId(payOrderId).build()); | 57 | .payOrderId(payOrderId).build()); |
| 56 | } | 58 | } |
| 57 | 59 | ||
| 60 | + | ||
| 61 | + | ||
| 58 | @PostMapping("/pay-notify") | 62 | @PostMapping("/pay-notify") |
| 59 | @ApiOperation("支付回调") | 63 | @ApiOperation("支付回调") |
| 60 | public CommonResult<Boolean> payNotify(@RequestBody @Valid PayNotifyOrderReqVO reqVO) { | 64 | public CommonResult<Boolean> payNotify(@RequestBody @Valid PayNotifyOrderReqVO reqVO) { |
| @@ -62,4 +66,11 @@ public class ShopOrderController { | @@ -62,4 +66,11 @@ public class ShopOrderController { | ||
| 62 | return success(true); | 66 | return success(true); |
| 63 | } | 67 | } |
| 64 | 68 | ||
| 69 | + @PostMapping("/refund-notify") | ||
| 70 | + @ApiOperation("退款回调") | ||
| 71 | + public CommonResult<Boolean> refundNotify(@RequestBody @Valid PayRefundOrderReqVO reqVO) { | ||
| 72 | + log.info("[refundNotify][回调成功]"); | ||
| 73 | + return success(true); | ||
| 74 | + } | ||
| 75 | + | ||
| 65 | } | 76 | } |