From cd1384fcc3390d59653c8e09083f30fdd46f6dc1 Mon Sep 17 00:00:00 2001 From: daxpay Date: Fri, 7 Aug 2026 22:18:19 +0800 Subject: [PATCH] =?UTF-8?q?feat(transfer):=20=E8=BD=AC=E8=B4=A6=E5=9C=BA?= =?UTF-8?q?=E6=99=AF=E6=A0=87=E8=AF=86=E4=B8=89=E9=80=9A=E9=81=93=E7=BB=9F?= =?UTF-8?q?=E4=B8=80(transferScene=20=E5=90=88=E5=B9=B6=E5=9C=BA=E6=99=AF?= =?UTF-8?q?=E9=85=8D=E7=BD=AEID/=E6=94=B6=E6=AC=BE=E6=84=9F=E7=9F=A5?= =?UTF-8?q?=E5=AD=97=E6=AE=B5),=20=E5=8F=91=E8=B5=B7=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=E6=8A=9B=E5=BC=82=E5=B8=B8,=20=E6=94=AF=E4=BB=98=E5=AE=9D?= =?UTF-8?q?=E8=BD=AC=E8=B4=A6=E5=8D=95=E8=A1=A8=E5=8A=A0=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- _config/sql/table.sql | 2 ++ _config/sql/update-tables.sql | 8 ++++++++ .../transfer/TransferStrategyContext.java | 9 ++------- .../transfer/entity/AlipayTransferOrder.java | 4 ++-- .../transfer/entity/DouyinTransferOrder.java | 3 --- .../trade/transfer/param/TransferParam.java | 17 ++++------------- .../runtime/service/TransferAssistService.java | 17 +++++++---------- .../runtime/service/TransferStartService.java | 4 +++- 8 files changed, 28 insertions(+), 36 deletions(-) diff --git a/_config/sql/table.sql b/_config/sql/table.sql index d5b546fcc..0f3037685 100644 --- a/_config/sql/table.sql +++ b/_config/sql/table.sql @@ -3626,6 +3626,7 @@ CREATE TABLE "public"."pay_transfer_order_alipay" ( "payee_type" varchar(32) COLLATE "pg_catalog"."default" NOT NULL, "payee_account" varchar(100) COLLATE "pg_catalog"."default" NOT NULL, "payee_name" varchar(100) COLLATE "pg_catalog"."default", + "transfer_scene" varchar(50) COLLATE "pg_catalog"."default", "pay_fund_order_id" varchar(64) COLLATE "pg_catalog"."default", "creator" int8, "create_time" timestamptz(6), @@ -3654,6 +3655,7 @@ COMMENT ON COLUMN "public"."pay_transfer_order_alipay"."error_msg" IS '错误信 COMMENT ON COLUMN "public"."pay_transfer_order_alipay"."payee_type" IS '收款人账号类型(user_id/open_id/login_name)'; COMMENT ON COLUMN "public"."pay_transfer_order_alipay"."payee_account" IS '收款人账号'; COMMENT ON COLUMN "public"."pay_transfer_order_alipay"."payee_name" IS '收款人姓名'; +COMMENT ON COLUMN "public"."pay_transfer_order_alipay"."transfer_scene" IS '转账场景标识(支付宝=转账场景配置ID, FAIL重试时恢复场景用)'; COMMENT ON COLUMN "public"."pay_transfer_order_alipay"."pay_fund_order_id" IS '支付宝资金流水号(财务对账,区别于 out_transfer_no)'; COMMENT ON COLUMN "public"."pay_transfer_order_alipay"."creator" IS '创建人'; COMMENT ON COLUMN "public"."pay_transfer_order_alipay"."create_time" IS '创建时间'; diff --git a/_config/sql/update-tables.sql b/_config/sql/update-tables.sql index e69de29bb..87adae7cc 100644 --- a/_config/sql/update-tables.sql +++ b/_config/sql/update-tables.sql @@ -0,0 +1,8 @@ +-- ---------------------------- +-- 数据库升级脚本(增量) +-- 按时间顺序追加, 已有列/表请勿重复执行 +-- ---------------------------- + +-- 支付宝转账单新增转账场景标识列(支付宝=转账场景配置ID, FAIL重试时恢复场景用) +ALTER TABLE "public"."pay_transfer_order_alipay" ADD COLUMN "transfer_scene" varchar(50) COLLATE "pg_catalog"."default"; +COMMENT ON COLUMN "public"."pay_transfer_order_alipay"."transfer_scene" IS '转账场景标识(支付宝=转账场景配置ID, FAIL重试时恢复场景用)'; diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/strategy/transfer/TransferStrategyContext.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/strategy/transfer/TransferStrategyContext.java index af0f78b54..092752eb2 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/strategy/transfer/TransferStrategyContext.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/strategy/transfer/TransferStrategyContext.java @@ -74,7 +74,8 @@ public class TransferStrategyContext { /// 收款人微信 openid private String payeeOpenid; - /// 转账场景(微信/抖音, 抖音发起时可经本字段回写) + /// 转账场景标识(三通道通用: 支付宝=场景配置ID/抖音=场景枚举码, 由请求传入; + /// 微信=通道商户配置场景, 发起时经本字段回写) private String transferScene; /// 收款人姓名(微信金额档位校验用) @@ -89,12 +90,6 @@ public class TransferStrategyContext { /// 收款人姓名(支付宝/抖音) private String payeeName; - /// 转账场景配置ID(支付宝专用,发起时指定用哪个场景; 空则用通道商户默认) - private String transferSceneConfigId; - /// 转账场景报备信息(微信/抖音转账必填, 各场景要求不同) private List reportInfos; - - /// 用户收款感知(抖音专用, 收款人在抖音中看到的文案) - private String userRecvPerception; } diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/entity/AlipayTransferOrder.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/entity/AlipayTransferOrder.java index 2bdc69b47..8732635dc 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/entity/AlipayTransferOrder.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/entity/AlipayTransferOrder.java @@ -83,8 +83,8 @@ public class AlipayTransferOrder extends MchBaseEntity { /// 收款人姓名 private String payeeName; - /// 转账场景配置ID(支付宝专用, FAIL重试时恢复场景用) - private String transferSceneConfigId; + /// 转账场景标识(支付宝=转账场景配置ID, FAIL重试时恢复场景用) + private String transferScene; /// 转账场景报备信息(JSON序列化, FAIL重试时恢复报备用) private String reportInfos; diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/entity/DouyinTransferOrder.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/entity/DouyinTransferOrder.java index 33921d372..090836f75 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/entity/DouyinTransferOrder.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/entity/DouyinTransferOrder.java @@ -82,9 +82,6 @@ public class DouyinTransferOrder extends MchBaseEntity { /// 转账场景ID(发起转账时由前端选择的主数据枚举, 如1001-1007) private String transferScene; - /// 用户收款感知(收款人在抖音中看到的文案) - private String userRecvPerception; - /// 转账场景报备信息(JSON序列化, FAIL重试时恢复报备用) private String reportInfos; diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/param/TransferParam.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/param/TransferParam.java index d1ae8da82..f2439bddd 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/param/TransferParam.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/param/TransferParam.java @@ -62,6 +62,7 @@ public class TransferParam { /// 收款人账号类型 /// @see cn.daxpay.open.payment.trade.transfer.enums.TransferPayeeTypeEnum + /// 抖音支持 openid/phone 两种(手机号复用收款人账号字段, 子应用证书加密上送) @NotBlank(message = "收款人账号类型必填") @Size(max = 32, message = "收款人账号类型不可超过32位") @Schema(description = "收款人账号类型") @@ -92,18 +93,8 @@ public class TransferParam { @Schema(description = "转账场景报备信息") private List reportInfos; - /// 转账场景配置ID(支付宝专用,可选; 不传则用通道商户的默认转账场景配置) - @Size(max = 32, message = "转账场景配置ID不可超过32位") - @Schema(description = "转账场景配置ID(支付宝专用,不传用默认)") - private String transferSceneConfigId; - - /// 转账场景ID(抖音专用, 主数据枚举如1001-1007, 发起转账时由前端选择传入) - @Size(max = 16, message = "转账场景ID不可超过16位") - @Schema(description = "转账场景ID(抖音专用,主数据枚举)") + /// 转账场景标识(三通道通用: 支付宝=转账场景配置ID, 抖音=主数据枚举码如1001, 发起时由前端选择传入; 微信不传, 用通道商户配置场景) + @Size(max = 32, message = "转账场景不可超过32位") + @Schema(description = "转账场景标识(支付宝=场景配置ID/抖音=场景枚举码, 微信不传用配置)") private String transferScene; - - /// 用户收款感知(抖音专用, 收款人在抖音中看到的文案, 按场景枚举选项) - @Size(max = 64, message = "用户收款感知不可超过64位") - @Schema(description = "用户收款感知(抖音专用)") - private String userRecvPerception; } diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/runtime/service/TransferAssistService.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/runtime/service/TransferAssistService.java index 2642b8d34..561c5bb58 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/runtime/service/TransferAssistService.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/runtime/service/TransferAssistService.java @@ -159,14 +159,14 @@ public class TransferAssistService { .setReqTime(OffsetDateTime.now()); // 商户号独立赋值(父类 setter 返回 MchBaseEntity, 禁止链式) order.setMchNo(mchNo); - // 持久化场景配置ID与报备信息(FAIL重试时恢复) - order.setTransferSceneConfigId(param.getTransferSceneConfigId()); + // 持久化场景标识与报备信息(FAIL重试时恢复) + order.setTransferScene(param.getTransferScene()); order.setReportInfos(serializeReportInfos(param.getReportInfos())); alipayTransferOrderManager.save(order); TransferTrade trade = this.buildTrade(order, channel, transferNo); transferTradeManager.save(trade); return buildAlipayContext(order).setChannel(channel).setTrade(trade) - .setTransferSceneConfigId(param.getTransferSceneConfigId()) + .setTransferScene(param.getTransferScene()) .setReportInfos(param.getReportInfos()); } case "douyin" -> { @@ -189,15 +189,13 @@ public class TransferAssistService { .setReqTime(OffsetDateTime.now()); // 商户号独立赋值(父类 setter 返回 MchBaseEntity, 禁止链式) order.setMchNo(mchNo); - // 持久化收款感知与报备信息(FAIL重试时恢复) - order.setUserRecvPerception(param.getUserRecvPerception()); + // 持久化场景与报备信息(FAIL重试时恢复) order.setReportInfos(serializeReportInfos(param.getReportInfos())); douyinTransferOrderManager.save(order); TransferTrade trade = this.buildTrade(order, channel, transferNo); transferTradeManager.save(trade); return buildDouyinContext(order).setChannel(channel).setTrade(trade) .setTransferScene(param.getTransferScene()) - .setUserRecvPerception(param.getUserRecvPerception()) .setReportInfos(param.getReportInfos()); } default -> throw new IllegalArgumentException("未知转账通道: " + channel); @@ -483,8 +481,8 @@ public class TransferAssistService { .setPayeeType(order.getPayeeType()) .setPayeeAccount(order.getPayeeAccount()) .setPayeeName(order.getPayeeName()) - // 恢复场景配置ID与报备信息(FAIL重试时使用) - .setTransferSceneConfigId(order.getTransferSceneConfigId()) + // 恢复场景标识与报备信息(FAIL重试时使用) + .setTransferScene(order.getTransferScene()) .setReportInfos(deserializeReportInfos(order.getReportInfos())); } @@ -506,8 +504,7 @@ public class TransferAssistService { .setPayeeAccount(order.getPayeeAccount()) .setPayeeName(order.getPayeeName()) .setTransferScene(order.getTransferScene()) - // 恢复收款感知与报备信息(FAIL重试时使用) - .setUserRecvPerception(order.getUserRecvPerception()) + // 恢复报备信息(FAIL重试时使用) .setReportInfos(deserializeReportInfos(order.getReportInfos())); } diff --git a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/runtime/service/TransferStartService.java b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/runtime/service/TransferStartService.java index 95f9e7b39..f9f29af4a 100644 --- a/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/runtime/service/TransferStartService.java +++ b/daxpay-payment/daxpay-payment-core/src/main/java/cn/daxpay/open/payment/trade/transfer/runtime/service/TransferStartService.java @@ -17,6 +17,7 @@ import cn.daxpay.open.platform.common.json.util.JacksonUtil; import cn.daxpay.open.platform.common.redis.lock.LockExecutor; import cn.daxpay.open.platform.core.code.CommonCode; import cn.daxpay.open.platform.core.code.CommonErrorCode; +import cn.daxpay.open.platform.core.code.DaxPayErrorCode; import cn.daxpay.open.platform.core.exception.BizInfoException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -130,9 +131,10 @@ public class TransferStartService { this.registerDelaySync(trade.getTradeNo()); } } catch (Exception e) { - // 发起异常: 置 FAIL 并记录错误 + // 发起异常: 置 FAIL 并记录错误, 然后向上抛出, 避免接口返回"假成功"误导调用方 log.error("转账发起失败: tradeNo={}, channel={}", trade.getTradeNo(), channel, e); assistService.fail(channel, trade, e.getMessage()); + throw new BizInfoException(DaxPayErrorCode.TRADE_FAIL, "pay.error.transfer.createFailed", e.getMessage()); } }