feat(channel-wechat): 微信转账申请配套(报备兜底/确认收款查询/标题与报备校验) + 错误码词条(10语)

This commit is contained in:
daxpay
2026-08-07 16:39:12 +08:00
parent be248dec17
commit 824b3f1fa6
22 changed files with 440 additions and 10 deletions

View File

@@ -1,8 +1,11 @@
package cn.daxpay.open.channel.wechat.client.req;
import cn.daxpay.open.channel.wechat.client.credential.WechatSdkCredential;
import cn.daxpay.open.payment.trade.transfer.param.TransferReportInfo;
import lombok.Data;
import java.util.List;
/// # 微信通道转账请求(发起/同步共用)
///
/// 与子应用 dax-pay-channel-one 的 `WechatTransferReq` 镜像, 字段对齐。
@@ -34,6 +37,9 @@ public class WechatTransferReq {
/// 异步通知地址(微信→平台)
private String notifyUrl;
/// 转账场景报备信息(发起时必填, 同步可空)
private List<TransferReportInfo> reportInfos;
/// 通道调用凭证
private WechatSdkCredential credential;
}

View File

@@ -0,0 +1,38 @@
package cn.daxpay.open.channel.wechat.controller.callback;
import cn.daxpay.open.channel.wechat.result.direct.WechatTransferConfirmResult;
import cn.daxpay.open.channel.wechat.service.payment.transfer.WechatTransferConfirmQueryService;
import cn.daxpay.open.platform.core.annotation.IgnoreAuth;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.constraints.NotBlank;
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/// # 微信转账确认收款(公开接口)
///
/// C 端收款人(无登录态)凭 transferNo 查询确认收款信息, 用于在微信内拉起收款确认页。
/// 与回调控制器同为 @IgnoreAuth, 不走 Sa-Token 认证。
@IgnoreAuth
@Validated
@Tag(name = "微信转账确认收款(公开)")
@RestController
@RequestMapping("/unipay/transfer/wechat")
@RequiredArgsConstructor
public class WechatTransferConfirmController {
private final WechatTransferConfirmQueryService wechatTransferConfirmQueryService;
@Operation(summary = "查询微信转账确认收款信息")
@GetMapping("/confirm-info/{transferNo}")
public Result<WechatTransferConfirmResult> getConfirmInfo(
@PathVariable @NotBlank(message = "转账单号不可为空") String transferNo) {
return Res.ok(wechatTransferConfirmQueryService.queryByTransferNo(transferNo));
}
}

View File

@@ -5,9 +5,11 @@ import cn.daxpay.open.platform.core.annotation.PermCode;
import cn.daxpay.open.platform.core.rest.Res;
import cn.daxpay.open.platform.core.rest.result.Result;
import cn.daxpay.open.channel.wechat.param.direct.WechatDirectChannelMerchantCreateParam;
import cn.daxpay.open.channel.wechat.param.direct.WechatDirectChannelMerchantUpdateParam;
import cn.daxpay.open.channel.wechat.param.direct.WechatDirectKeyConfigParam;
import cn.daxpay.open.channel.wechat.result.direct.WechatDirectChannelMerchantResult;
import cn.daxpay.open.channel.wechat.result.direct.WechatDirectKeyConfigResult;
import cn.daxpay.open.channel.wechat.result.direct.WechatTransferSceneOptionResult;
import cn.daxpay.open.channel.wechat.service.direct.WechatDirectChannelMerchantService;
import cn.daxpay.open.channel.wechat.service.direct.WechatDirectKeyConfigService;
import io.swagger.v3.oas.annotations.Operation;
@@ -21,6 +23,8 @@ import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/// # 微信直连通道商户管理
///
@PermCode(menuCode = PermCodes.Channel.Merchant.MENU)
@@ -50,6 +54,21 @@ public class WechatDirectChannelMerchantController {
return Res.ok();
}
@PermCode(code = PermCodes.Action.MANAGE)
@Operation(summary = "更新微信直连通道商户(转账场景/微信商户号)")
@PostMapping("/update")
public Result<Void> update(@RequestBody @Validated WechatDirectChannelMerchantUpdateParam param) {
wechatDirectChannelMerchantService.update(param);
return Res.ok();
}
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "查询微信转账场景选项列表")
@GetMapping("/scene-options")
public Result<List<WechatTransferSceneOptionResult>> sceneOptions() {
return Res.ok(wechatDirectChannelMerchantService.findSceneOptions());
}
@PermCode(code = PermCodes.Action.VIEW)
@Operation(summary = "根据通道商户号查询密钥配置")
@GetMapping("/find-key-config")

View File

@@ -0,0 +1,94 @@
package cn.daxpay.open.channel.wechat.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.List;
/// # 微信转账场景枚举
///
/// 对应微信商家转账(新版 fund-app/mch-transfer)的转账场景, 场景ID 由商户在微信商户平台
/// 「产品中心-商家转账」申请开通。每个场景要求不同的报备字段([reportInfoTypes]),
/// 字段值为微信协议固定的中文 [infoType], 不可更改。
///
/// 报备字段内容(infoContent)由商户在发起转账时填写, 留空用 `-` 兜底(1009 采购货款用空串)。
///
/// [reportInfoDescriptions] 与 [reportInfoTypes] 一一平行, 描述每个字段的含义和微信文档示例。
/// [userRecvPerceptionOptions] 为收款人在微信中看到的感知文案可选值, 不传时微信按场景取默认(第一个)。
@Getter
@AllArgsConstructor
public enum WechatTransferSceneEnum {
/// 现金营销
CASH_MARKETING("1000", "现金营销",
List.of("活动名称", "奖励说明"),
List.of("商户自定义内容,如「新会员有礼」", "商户自定义内容,如「注册会员抽奖一等奖」"),
List.of("现金奖励", "活动奖励")),
/// 行政补贴
ADMINISTRATIVE_SUBSIDY("1002", "行政补贴",
List.of("补贴类型"),
List.of("商户自定义内容,如「购车补贴」"),
List.of("行政补贴", "行政奖励")),
/// 保险理赔
INSURANCE_CLAIM("1004", "保险理赔",
List.of("保险产品备案编号", "保险名称", "保险操作单号"),
List.of("保险产品备案编号如「01212121212」", "保险名称,如「意外险」", "保险操作单号如「12121245442」"),
List.of("保险理赔款")),
/// 佣金报酬
COMMISSION_REWARD("1005", "佣金报酬",
List.of("岗位类型", "报酬说明"),
List.of("商户自定义内容,如「外卖员」", "商户自定义内容如「7月份配送费」"),
List.of("劳务报酬", "报销款", "企业补贴", "开工利是")),
/// 采购货款
PROCUREMENT_PAYMENT("1009", "采购货款",
List.of("采购商品名称"),
List.of("商户自定义内容,如「戴尔笔记本电脑」"),
List.of("货款")),
/// 二手回收
SECONDHAND_RECYCLING("1010", "二手回收",
List.of("回收商品名称"),
List.of("商户自定义内容,如「塑料瓶」"),
List.of("二手回收货款")),
/// 企业赔付
ENTERPRISE_COMPENSATION("1011", "企业赔付",
List.of("赔付原因"),
List.of("商户自定义内容,如「商品质量问题退款」"),
List.of("退款", "商家赔付")),
/// 公益补助
PUBLIC_WELFARE_SUBSIDY("1013", "公益补助",
List.of("公益活动名称", "公益活动备案编号"),
List.of("请填写在民政部的备案名称", "请填写在民政部的备案编号"),
List.of("公益补助金"));
/// 转账场景ID
private final String code;
/// 场景名称
private final String name;
/// 报备字段定义(微信协议固定中文 infoType, 顺序即 infoContentList 下标)
private final List<String> reportInfoTypes;
/// 报备字段说明(与 reportInfoTypes 平行, 描述字段含义和微信文档示例)
private final List<String> reportInfoDescriptions;
/// 用户收款感知可选值(收款人在微信中看到的文案, 不传时取第一个为默认)
private final List<String> userRecvPerceptionOptions;
/// 根据场景ID 查找枚举
public static WechatTransferSceneEnum findByCode(String code) {
for (WechatTransferSceneEnum scene : values()) {
if (scene.code.equals(code)) {
return scene;
}
}
return null;
}
}

View File

@@ -0,0 +1,31 @@
package cn.daxpay.open.channel.wechat.param.direct;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
import lombok.experimental.Accessors;
/// # 微信直连通道商户更新参数
///
/// 当前仅支持更新转账场景与微信商户号, 通道商户号创建后不可变。
@Data
@Accessors(chain = true)
@Schema(title = "微信直连通道商户更新参数")
public class WechatDirectChannelMerchantUpdateParam {
/// 通道商户号(定位用, 不可变)
@Schema(description = "通道商户号")
@NotBlank(message = "{validation.field.channelMerchantNo.notBlank}")
private String channelMchNo;
/// 微信直连商户号
@Schema(description = "微信直连商户号")
@Size(max = 32, message = "微信直连商户号不可超过32位")
private String wxMchId;
/// 转账场景ID(商家转账到零钱, 微信转账时必填)
/// @see cn.daxpay.open.channel.wechat.enums.WechatTransferSceneEnum
@Schema(description = "转账场景ID")
private String transferScene;
}

View File

@@ -0,0 +1,36 @@
package cn.daxpay.open.channel.wechat.result.direct;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
/// # 微信转账确认收款信息
///
/// 供 C 端收款人在微信内拉起确认收款页(`WeixinJSBridge.invoke('requestMerchantTransfer')`)使用。
/// [mchId]/[appId]/[packageInfo] 为拉起 JSAPI 必需参数, 全部由后端从订单与通道配置反查返回。
@Data
@Accessors(chain = true)
@Schema(title = "微信转账确认收款信息")
public class WechatTransferConfirmResult {
@Schema(description = "微信商户号(拉起 requestMerchantTransfer 用)")
private String mchId;
@Schema(description = "商户 AppID(拉起 requestMerchantTransfer 用)")
private String appId;
@Schema(description = "拉起确认参数 package_info")
private String packageInfo;
@Schema(description = "转账金额(分)")
private Long amount;
@Schema(description = "转账标题")
private String title;
@Schema(description = "转账状态")
private String status;
@Schema(description = "是否已终态(不可再操作)")
private boolean received;
}

View File

@@ -0,0 +1,35 @@
package cn.daxpay.open.channel.wechat.result.direct;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
/// # 微信转账场景选项结果
///
/// 供前端下拉选择与报备字段动态渲染。报备字段 [reportInfoTypes] 为微信协议固定中文
/// [infoType], 不可更改; 顺序即发起转账时 [infoContent] 的填写下标。
///
/// [reportInfoDescriptions] 与 [reportInfoTypes] 平行, 描述每个字段含义和微信文档示例。
/// [userRecvPerceptionOptions] 为收款人在微信中看到的感知文案可选值。
@Data
@Accessors(chain = true)
@Schema(title = "微信转账场景选项")
public class WechatTransferSceneOptionResult {
@Schema(description = "转账场景ID")
private String code;
@Schema(description = "场景名称")
private String name;
@Schema(description = "报备字段定义(微信协议固定中文 infoType)")
private List<String> reportInfoTypes;
@Schema(description = "报备字段说明(与 reportInfoTypes 平行, 含微信文档示例)")
private List<String> reportInfoDescriptions;
@Schema(description = "用户收款感知可选值(收款人在微信中看到的文案)")
private List<String> userRecvPerceptionOptions;
}

View File

@@ -2,8 +2,11 @@ package cn.daxpay.open.channel.wechat.service.direct;
import cn.daxpay.open.channel.wechat.dao.direct.WechatDirectChannelMerchantManager;
import cn.daxpay.open.channel.wechat.entity.direct.WechatDirectChannelMerchant;
import cn.daxpay.open.channel.wechat.enums.WechatTransferSceneEnum;
import cn.daxpay.open.channel.wechat.param.direct.WechatDirectChannelMerchantCreateParam;
import cn.daxpay.open.channel.wechat.param.direct.WechatDirectChannelMerchantUpdateParam;
import cn.daxpay.open.channel.wechat.result.direct.WechatDirectChannelMerchantResult;
import cn.daxpay.open.channel.wechat.result.direct.WechatTransferSceneOptionResult;
import cn.daxpay.open.channel.wechat.strategy.direct.merchant.WechatDirectChannelMerchantCleanupStrategy;
import cn.daxpay.open.payment.merchant.dao.channel.ChannelMerchantManager;
import cn.daxpay.open.payment.masterdata.dao.product.PayProductConfigManager;
@@ -18,6 +21,9 @@ import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
/// # 微信直连通道商户管理
///
/// 一个微信商户号(wxMchId)对应一个 channelMchNo, 商户的多个应用共享此绑定。
@@ -75,4 +81,40 @@ public class WechatDirectChannelMerchantService {
// 微信: 通道商户配置不存在
.orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist"));
}
/// 更新微信直连通道商户(转账场景/微信商户号)
@Transactional(rollbackFor = Exception.class)
public void update(WechatDirectChannelMerchantUpdateParam param) {
WechatDirectChannelMerchant entity = wechatDirectChannelMerchantManager.lambdaQuery()
.eq(WechatDirectChannelMerchant::getChannelMchNo, param.getChannelMchNo())
.oneOpt()
// 微信: 通道商户配置不存在
.orElseThrow(() -> new DataNotExistException("error.payment.channel.channelMerchantNotExist"));
// 微信商户号变更时校验同一商户下不重复
if (param.getWxMchId() != null && !param.getWxMchId().equals(entity.getWxMchId())) {
if (wechatDirectChannelMerchantManager.existsByMchNoAndWxMchId(
entity.getMchNo(), param.getWxMchId())) {
// 微信: 同一商户下该微信商户已存在
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR, "error.channel.wechat.directMchDuplicate");
}
entity.setWxMchId(param.getWxMchId());
}
// 转账场景(允许清空, 清空后发起转账会报"场景未配置")
if (param.getTransferScene() != null) {
entity.setTransferScene(param.getTransferScene());
}
wechatDirectChannelMerchantManager.updateById(entity);
}
/// 查询微信转账场景选项列表(供前端下拉与报备字段动态渲染)
public List<WechatTransferSceneOptionResult> findSceneOptions() {
return Arrays.stream(WechatTransferSceneEnum.values())
.map(scene -> new WechatTransferSceneOptionResult()
.setCode(scene.getCode())
.setName(scene.getName())
.setReportInfoTypes(scene.getReportInfoTypes())
.setReportInfoDescriptions(scene.getReportInfoDescriptions())
.setUserRecvPerceptionOptions(scene.getUserRecvPerceptionOptions()))
.toList();
}
}

View File

@@ -0,0 +1,60 @@
package cn.daxpay.open.channel.wechat.service.payment.transfer;
import cn.daxpay.open.channel.wechat.client.credential.WechatSdkCredential;
import cn.daxpay.open.channel.wechat.result.direct.WechatTransferConfirmResult;
import cn.daxpay.open.channel.wechat.service.direct.WechatDirectConfigAssembler;
import cn.daxpay.open.payment.common.context.MerchantContextLoader;
import cn.daxpay.open.payment.trade.transfer.dao.WechatTransferOrderManager;
import cn.daxpay.open.payment.trade.transfer.entity.WechatTransferOrder;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.Set;
/// # 微信转账确认收款查询服务
///
/// 供 C 端收款人(无登录态)凭 transferNo 查询确认收款信息。
/// 跨租户引导读订单([WechatTransferOrderManager#findByTransferNoNotTenant]) →
/// 装载商户上下文 → 通道凭证组装获取 wxMchId/wxAppId → 返回 packageInfo 等拉起参数。
///
/// 安全模型: 凭 transferNo 高熵不可猜防枚举, 仅返回该订单的公开收款信息。
@Slf4j
@Service
@RequiredArgsConstructor
public class WechatTransferConfirmQueryService {
/// 终态状态(不可再确认收款)
private static final Set<String> TERMINAL_STATES = Set.of("success", "close", "fail");
private final WechatTransferOrderManager wechatTransferOrderManager;
private final MerchantContextLoader merchantContextLoader;
private final WechatDirectConfigAssembler wechatDirectConfigAssembler;
/// 查询确认收款信息
///
/// @param transferNo 平台转账单号(URL 路径参数)
/// @return 确认收款信息(mchId/appId/packageInfo 等)
public WechatTransferConfirmResult queryByTransferNo(String transferNo) {
// 跨租户查订单(收款人 C 端无商户上下文)
WechatTransferOrder order = wechatTransferOrderManager.findByTransferNoNotTenant(transferNo)
.orElseThrow(() -> new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"pay.error.transfer.notFound"));
// 装载商户上下文, 后续通道配置查询走租户隔离
merchantContextLoader.initMch(order.getMchNo());
// 通道凭证组装(wxMchId + wxAppId)
WechatSdkCredential credential = wechatDirectConfigAssembler.buildConfig(
order.getMchNo(), order.getChannelMchNo(), null, null);
boolean terminal = TERMINAL_STATES.contains(order.getStatus());
return new WechatTransferConfirmResult()
.setMchId(credential.getWxMchId())
.setAppId(credential.getWxAppId())
.setPackageInfo(terminal ? null : order.getTransferBody())
.setAmount(order.getAmount())
.setTitle(order.getTitle())
.setStatus(order.getStatus())
.setReceived(terminal);
}
}

View File

@@ -4,10 +4,12 @@ import cn.daxpay.open.channel.wechat.client.WechatChannelClient;
import cn.daxpay.open.channel.wechat.client.credential.WechatSdkCredential;
import cn.daxpay.open.channel.wechat.client.req.WechatTransferReq;
import cn.daxpay.open.channel.wechat.client.resp.WechatTransferResp;
import cn.daxpay.open.channel.wechat.enums.WechatTransferSceneEnum;
import cn.daxpay.open.payment.common.result.DaxResult;
import cn.daxpay.open.payment.strategy.transfer.TransferStrategyContext;
import cn.daxpay.open.payment.trade.enums.PayFundStatusEnum;
import cn.daxpay.open.payment.trade.transfer.bo.TransferResultBo;
import cn.daxpay.open.payment.trade.transfer.param.TransferReportInfo;
import cn.daxpay.open.platform.core.code.DaxPayErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
import cn.daxpay.open.platform.system.service.config.infra.PlatformUrlConfigService;
@@ -16,6 +18,7 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -54,6 +57,7 @@ public class WechatTransferService {
req.setUserName(context.getUserName());
req.setRemark(StrUtil.sub(context.getTitle(), 0, 32));
req.setNotifyUrl(this.buildNotifyUrl(context));
req.setReportInfos(this.ensureReportInfos(context));
req.setCredential(credential);
DaxResult<WechatTransferResp> result = wechatChannelClient.transfer(req);
@@ -121,4 +125,26 @@ public class WechatTransferService {
return StrUtil.format("{}/unipay/callback/{}/{}/wechat/transfer",
base, context.getMchNo(), context.getChannelMchNo());
}
/// 确保报备信息非空
///
/// 微信 `/transfer-bills` 的 `transfer_scene_report_infos` 为必填。
/// 商户传入的 [TransferStrategyContext#getReportInfos] 非空时直接透传;
/// 为空时按场景枚举 [WechatTransferSceneEnum] 的报备字段模板构建默认值(`-`)。
private List<TransferReportInfo> ensureReportInfos(TransferStrategyContext context) {
List<TransferReportInfo> reportInfos = context.getReportInfos();
if (reportInfos != null && !reportInfos.isEmpty()) {
return reportInfos;
}
// 兜底: 按场景枚举构建默认报备信息
WechatTransferSceneEnum scene = WechatTransferSceneEnum.findByCode(context.getTransferScene());
if (scene == null) {
return reportInfos;
}
List<TransferReportInfo> defaults = new ArrayList<>();
for (String infoType : scene.getReportInfoTypes()) {
defaults.add(new TransferReportInfo().setInfoType(infoType).setInfoContent("-"));
}
return defaults;
}
}

View File

@@ -10,6 +10,7 @@ import cn.daxpay.open.payment.strategy.transfer.TransferStrategyContext;
import cn.daxpay.open.payment.trade.transfer.bo.TransferResultBo;
import cn.daxpay.open.payment.trade.transfer.enums.TransferPayeeTypeEnum;
import cn.daxpay.open.payment.trade.transfer.param.TransferParam;
import cn.daxpay.open.payment.trade.transfer.param.TransferReportInfo;
import cn.daxpay.open.platform.core.code.CommonErrorCode;
import cn.daxpay.open.platform.core.code.DaxPayErrorCode;
import cn.daxpay.open.platform.core.exception.BizInfoException;
@@ -18,12 +19,15 @@ import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/// # 微信直连转账策略
///
/// 微信商家转账到零钱(V3)的通道策略。
/// 通道差异:
/// - 仅支持 openid 收款人([TransferPayeeTypeEnum#OPENID])
/// - 金额档位姓名校验: 小于 0.3 元禁填姓名, 大于等于 2000 元必填姓名
/// - 转账备注(标题)与场景报备信息必填(微信接口 transfer_remark / transfer_scene_report_infos)
/// - transfer_scene 取自「微信转账配置」([WechatTransferConfig]), 发起应用由配置指定(公众号)
@Slf4j
@Service
@@ -53,6 +57,18 @@ public class WechatTransferStrategy extends AbsTransferStrategy {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.wechat.transferOnlyOpenid");
}
// 微信: 转账备注(标题)必填, 对应接口 transfer_remark
if (StrUtil.isBlank(param.getTitle())) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.wechat.transferRemarkRequired");
}
// 微信: 转账场景报备信息必填(接口 transfer_scene_report_infos 必填, 且 info_content 不可为空)
List<TransferReportInfo> reportInfos = param.getReportInfos();
if (reportInfos == null || reportInfos.isEmpty()
|| reportInfos.stream().anyMatch(info -> StrUtil.isBlank(info.getInfoContent()))) {
throw new BizInfoException(CommonErrorCode.VALIDATE_PARAMETERS_ERROR,
"error.channel.wechat.transferReportInfoRequired");
}
long amountFen = param.getAmount().movePointRight(2).longValue();
String payeeName = param.getPayeeName();
if (amountFen < SMALL_AMOUNT_LIMIT && StrUtil.isNotBlank(payeeName)) {

View File

@@ -3,6 +3,7 @@ package cn.daxpay.open.payment.trade.transfer.dao;
import cn.daxpay.open.platform.common.mybatisplus.impl.BaseManager;
import cn.daxpay.open.platform.common.mybatisplus.query.generator.QueryGenerator;
import cn.daxpay.open.platform.common.mybatisplus.util.MpUtil;
import cn.daxpay.open.platform.core.annotation.IgnoreTenant;
import cn.daxpay.open.platform.core.rest.param.PageParam;
import cn.daxpay.open.payment.trade.transfer.entity.WechatTransferOrder;
import cn.daxpay.open.payment.trade.transfer.param.WechatTransferOrderQuery;
@@ -24,6 +25,12 @@ public class WechatTransferOrderManager
return findByField(WechatTransferOrder::getTransferNo, transferNo);
}
/// 根据平台转账单号查询(忽略租户, 确认收款页引导读用)
@IgnoreTenant
public Optional<WechatTransferOrder> findByTransferNoNotTenant(String transferNo) {
return findByField(WechatTransferOrder::getTransferNo, transferNo);
}
/// 根据商户转账号和应用号查询(幂等查重主路径)
public Optional<WechatTransferOrder> findByBizTransferNo(String bizTransferNo, String appId) {
return lambdaQuery()

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "WeChat: Transfer initiator app not configured",
"transferAppNotExist": "WeChat: Transfer initiator app not found",
"transferAppNotBelong": "WeChat: Transfer initiator app does not belong to the current merchant",
"transferAppTypeNotOfficialAccount": "WeChat: Transfer initiator app must be an Official Account"
"transferAppTypeNotOfficialAccount": "WeChat: Transfer initiator app must be an Official Account",
"transferRemarkRequired": "WeChat: transfer remark (title) is required",
"transferReportInfoRequired": "WeChat: transfer scene report info is required, please fill in all report fields"
}

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "WeChat: Aplikasi inisiator transfer belum dikonfigurasi",
"transferAppNotExist": "WeChat: Aplikasi inisiator transfer tidak ditemukan",
"transferAppNotBelong": "WeChat: Aplikasi inisiator transfer bukan milik merchant saat ini",
"transferAppTypeNotOfficialAccount": "WeChat: Aplikasi inisiator transfer harus berjenis Official Account"
"transferAppTypeNotOfficialAccount": "WeChat: Aplikasi inisiator transfer harus berjenis Official Account",
"transferRemarkRequired": "WeChat: catatan transfer (judul) wajib diisi",
"transferReportInfoRequired": "WeChat: informasi pelaporan skema transfer wajib diisi, silakan isi semua bidang pelaporan"
}

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "WeChat: 振込发起アプリが未設定です",
"transferAppNotExist": "WeChat: 振込发起アプリが見つかりません",
"transferAppNotBelong": "WeChat: 振込发起アプリは現在のマーチャントに属していません",
"transferAppTypeNotOfficialAccount": "WeChat: 振込发起アプリは公式アカウントタイプである必要があります"
"transferAppTypeNotOfficialAccount": "WeChat: 振込发起アプリは公式アカウントタイプである必要があります",
"transferRemarkRequired": "WeChat: 振込備考(タイトル)は必須です",
"transferReportInfoRequired": "WeChat: 振込シーン報備情報は必須です。すべての報備項目を入力してください"
}

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "WeChat: 이체 발신 앱이 구성되지 않았습니다",
"transferAppNotExist": "WeChat: 이체 발신 앱을 찾을 수 없습니다",
"transferAppNotBelong": "WeChat: 이체 발신 앱이 현재 가맹점에 속하지 않습니다",
"transferAppTypeNotOfficialAccount": "WeChat: 이체 발신 앱은 공식계정 유형이어야 합니다"
"transferAppTypeNotOfficialAccount": "WeChat: 이체 발신 앱은 공식계정 유형이어야 합니다",
"transferRemarkRequired": "WeChat: 이체 메모(제목)는 필수입니다",
"transferReportInfoRequired": "WeChat: 이체 시나리오 보고 정보는 필수입니다. 모든 보고 항목을 입력해 주세요"
}

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "WeChat: Aplikasi pemula pemindahan belum dikonfigurasi",
"transferAppNotExist": "WeChat: Aplikasi pemula pemindahan tidak dijumpai",
"transferAppNotBelong": "WeChat: Aplikasi pemula pemindahan tidak milik merchant semasa",
"transferAppTypeNotOfficialAccount": "WeChat: Aplikasi pemula pemindahan mesti jenis Official Account"
"transferAppTypeNotOfficialAccount": "WeChat: Aplikasi pemula pemindahan mesti jenis Official Account",
"transferRemarkRequired": "WeChat: catatan pindah wang (tajuk) wajib diisi",
"transferReportInfoRequired": "WeChat: maklumat laporan senario pindah wang wajib diisi, sila isi semua medan laporan"
}

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "WeChat: ยังไม่ได้กำหนดค่าแอปเริ่มการโอน",
"transferAppNotExist": "WeChat: ไม่พบแอปเริ่มการโอน",
"transferAppNotBelong": "WeChat: แอปเริ่มการโอนไม่ได้เป็นของร้านค้าปัจจุบัน",
"transferAppTypeNotOfficialAccount": "WeChat: แอปเริ่มการโอนต้องเป็นประเภท Official Account"
"transferAppTypeNotOfficialAccount": "WeChat: แอปเริ่มการโอนต้องเป็นประเภท Official Account",
"transferRemarkRequired": "WeChat: หมายเหตุการโอน (หัวข้อ) จำเป็นต้องระบุ",
"transferReportInfoRequired": "WeChat: ข้อมูลรายงานสถานการณ์การโอนเงินจำเป็นต้องระบุ กรุณากรอกข้อมูลรายงานทั้งหมด"
}

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "WeChat: Ứng dụng khởi tạo chuyển khoản chưa được thiết lập",
"transferAppNotExist": "WeChat: Không tìm thấy ứng dụng khởi tạo chuyển khoản",
"transferAppNotBelong": "WeChat: Ứng dụng khởi tạo chuyển khoản không thuộc merchant hiện tại",
"transferAppTypeNotOfficialAccount": "WeChat: Ứng dụng khởi tạo chuyển khoản phải là loại Official Account"
"transferAppTypeNotOfficialAccount": "WeChat: Ứng dụng khởi tạo chuyển khoản phải là loại Official Account",
"transferRemarkRequired": "WeChat: ghi chú chuyển khoản (tiêu đề) là bắt buộc",
"transferReportInfoRequired": "WeChat: thông tin báo cáo kịch bản chuyển khoản là bắt buộc, vui lòng điền đầy đủ các trường báo cáo"
}

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "微信: 转账发起应用未配置",
"transferAppNotExist": "微信: 转账发起应用不存在",
"transferAppNotBelong": "微信: 转账发起应用不属于当前商户",
"transferAppTypeNotOfficialAccount": "微信: 转账发起应用必须是公众号类型"
"transferAppTypeNotOfficialAccount": "微信: 转账发起应用必须是公众号类型",
"transferRemarkRequired": "微信: 转账备注(标题)必填",
"transferReportInfoRequired": "微信: 转账场景报备信息必填, 请填写全部报备字段"
}

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "微信: 轉帳發起應用未設定",
"transferAppNotExist": "微信: 轉帳發起應用不存在",
"transferAppNotBelong": "微信: 轉帳發起應用不屬於目前商戶",
"transferAppTypeNotOfficialAccount": "微信: 轉帳發起應用必須是公眾號類型"
"transferAppTypeNotOfficialAccount": "微信: 轉帳發起應用必須是公眾號類型",
"transferRemarkRequired": "微信: 轉賬備註(標題)必填",
"transferReportInfoRequired": "微信: 轉賬場景報備資訊必填, 請填寫全部報備欄位"
}

View File

@@ -66,5 +66,7 @@
"transferAppNotConfigured": "微信: 轉帳發起應用未設定",
"transferAppNotExist": "微信: 轉帳發起應用不存在",
"transferAppNotBelong": "微信: 轉帳發起應用不屬於目前商戶",
"transferAppTypeNotOfficialAccount": "微信: 轉帳發起應用必須是公眾號類型"
"transferAppTypeNotOfficialAccount": "微信: 轉帳發起應用必須是公眾號類型",
"transferRemarkRequired": "微信: 轉帳備註(標題)必填",
"transferReportInfoRequired": "微信: 轉帳場景報備資訊必填, 請填寫全部報備欄位"
}