yeg
2026-09-15 e89a164e2b0667e613253c578ef289714901336d
fix(merchant): 进件回到渠道商直属普通商户,去掉 head 层级

平台曾经用 head_office_flag=0 + head_huifu_id=<平台汇付号> 把新商户建成自己的
下级商户,期望由此获得「微信支付配置 + 业务开通」的数据权限。真实通道上的对照
实验否掉了这个期望:

- 带 head 字段进件成功(huifu_id=6666000241506776,resp_code=90000000 审核中),
紧接着调 v2.merchant.busi.config 与 v2.merchant.busi.open,仍是
20002 数据权限认证失败;
- head/config 申请单已 apply_status=Y,不改变结果;
- 渠道商直属的普通商户 6666000241335656(该笔进件早于 head 改动,确定没有 head
字段)同样返回 20002。

所以 20002 与层级无关,取决于签名身份本身有没有那些商户的数据权限;head 层级不但
没有收益,还会按总部客群类型索要 F725/F726/F727 材料,白白增加进件失败面。进件改回
「渠道商 → 普通商户」两级,并删掉随之失去意义的 HuifuParentMerchant 与
onboarding-parent-huifu-id 配置。

商户业务开通的 use_chains_flag 原来挂在「是否建下级商户」上,现在抽成独立配置
merchant-use-chains-flag,默认 3(仅微信)——我们没实现微信特约商户进件,子商户
需要复用上级微信商户号才能发起交易,取值与改动前实际下发的完全一致。

顺带把企业/个人进件的请求组装从 create() 里拆出来(buildRequestForTest),
新增 HuifuOnboardingMerchantLevelTests 钉住「进件请求不得出现 head 字段」。
2个文件已删除
1个文件已添加
8个文件已修改
346 ■■■■■ 已修改文件
talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantSettlementService.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuEnterpriseOnboardingCommand.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuEnterpriseOnboardingGateway.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuIndividualOnboardingCommand.java 4 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuIndividualOnboardingGateway.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuMerchantBusinessOpenGateway.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuParentMerchant.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuPaymentProperties.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/resources/application.yml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuOnboardingMerchantLevelTests.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuOnboardingParentMerchantTests.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
talent-card-server/src/main/java/com/talentcard/server/business/merchant/MerchantSettlementService.java
@@ -157,8 +157,7 @@
                    privateSettlement ? "1" : "0", privateSettlement ? request.subjectName().trim() : request.merchantName().trim(), request.bankCardNumber().trim(), request.bankProvinceCode().trim(), request.bankCityCode().trim(), clean(branch.get("headBankCode")), clean(branch.get("clearingCode")), clean(branch.get("branchName")),
                    privateSettlement ? request.subjectIdNumber().trim().toUpperCase() : "", privateSettlement ? certificateValidityType(request.subjectCertificateValidityType()) : "", privateSettlement ? certificateBeginDate(request.subjectCertificateBeginDate()) : "", privateSettlement ? certificateEndDate(request.subjectCertificateEndDate()) : "", privateSettlement ? request.contactMobile().trim() : "",
                    registrationAccountPictureId, settlementCardPictureId, settlementCertFrontPictureId, settlementCertBackPictureId,
                    certificateBeginDate(request.establishedDate()), normalizeRegisteredCapital(request.companyType(), request.registeredCapital()),
                    huifuPaymentProperties.getOnboardingParentHuifuId()));
                    certificateBeginDate(request.establishedDate()), normalizeRegisteredCapital(request.companyType(), request.registeredCapital())));
            applyOnboardingResult(applicationNo, bankCardId, result.accepted(), result.huifuId(), result.applyNo(), result.tokenNo(), result.code(), result.message());
        } catch (HuifuPaymentGatewayException exception) {
            jdbc.update("update merchant_onboarding_application set reject_reason=? where application_no=? and status='pending'", "Huifu submission outcome is pending confirmation", applicationNo);
@@ -173,8 +172,7 @@
            HuifuIndividualOnboardingGateway.Result result = individualOnboardingGateway.create(new HuifuIndividualOnboardingCommand(
                    applicationNo, request.subjectName().trim(), request.subjectIdNumber().trim().toUpperCase(), certificateValidityType(request.subjectCertificateValidityType()), certificateBeginDate(request.subjectCertificateBeginDate()), certificateEndDate(request.subjectCertificateEndDate()), request.contactMobile().trim(), clean(request.registeredAddress()), request.administratorEmail().trim(),
                    mcc(principal.merchantId()), clean(request.registeredProvinceCode()), clean(request.registeredCityCode()), clean(request.registeredDistrictCode()), legalFrontPictureId, legalBackPictureId,
                    request.bankCardNumber().trim(), request.bankProvinceCode().trim(), request.bankCityCode().trim(), clean(branch.get("headBankCode")), settlementCardPictureId,
                    huifuPaymentProperties.getOnboardingParentHuifuId()));
                    request.bankCardNumber().trim(), request.bankProvinceCode().trim(), request.bankCityCode().trim(), clean(branch.get("headBankCode")), settlementCardPictureId));
            applyOnboardingResult(applicationNo, bankCardId, result.accepted(), result.huifuId(), result.applyNo(), result.tokenNo(), result.code(), result.message());
        } catch (HuifuPaymentGatewayException exception) {
            jdbc.update("update merchant_onboarding_application set reject_reason=? where application_no=? and status='pending'", "Huifu submission outcome is pending confirmation", applicationNo);
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuEnterpriseOnboardingCommand.java
@@ -44,7 +44,5 @@
        String settlementCertificateFrontPictureId,
        String settlementCertificateBackPictureId,
        String foundDate,
        String registeredCapital,
        /** 平台自己的汇付号:非空则把该商户建成平台的下级商户(head_office_flag=0 + head_huifu_id)。 */
        String parentHuifuId
        String registeredCapital
) {}
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuEnterpriseOnboardingGateway.java
@@ -25,6 +25,31 @@
    public Result create(HuifuEnterpriseOnboardingCommand command) {
        ensureConfigured();
        Map<String, Object> request = buildRequest(command);
        try {
            synchronized (SDK_LOCK) {
                initializeSdk();
                Map<String, Object> response = BasePayClient.request(request, "v2.merchant.basicdata.ent", false);
                log.info("Huifu enterprise merchant onboarding response: applicationNo={}, upperHuifuId={}, response={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), JSON.toJSONString(response));
                return Result.from(response);
            }
        } catch (BusinessException exception) {
            throw exception;
        } catch (Exception exception) {
            log.error("Huifu enterprise merchant onboarding request error: applicationNo={}, upperHuifuId={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), exception);
            throw new HuifuPaymentGatewayException("Huifu enterprise merchant onboarding request failed", outcomeUnknown(exception), exception);
        }
    }
    /**
     * 组装企业进件请求体,与发送解耦,便于直接断言商户归属
     * (见 {@code HuifuOnboardingMerchantLevelTests})。
     */
    static Map<String, Object> buildRequestForTest(HuifuPaymentProperties properties, HuifuEnterpriseOnboardingCommand command) {
        return new HuifuEnterpriseOnboardingGateway(properties).buildRequest(command);
    }
    private Map<String, Object> buildRequest(HuifuEnterpriseOnboardingCommand command) {
        Map<String, Object> card = new LinkedHashMap<>();
        card.put("branch_code", command.bankBranchCode());
        card.put("branch_name", command.bankBranchName());
@@ -53,8 +78,6 @@
        request.put("req_seq_id", command.applicationNo());
        request.put("req_date", LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE));
        request.put("upper_huifu_id", properties.getOnboardingUpperHuifuId().trim());
        // 建成平台自己的下级商户:只有挂在自己名下,平台才有权为该商户配置微信支付与业务开通。
        HuifuParentMerchant.apply(request, command.parentHuifuId());
        request.put("reg_name", command.companyName());
        request.put("short_name", command.shortName());
        request.put("receipt_name", command.shortName());
@@ -98,19 +121,7 @@
        request.put("cash_config", JSON.toJSONString(List.of(cash)));
        request.put("settle_config", JSON.toJSONString(settleConfig()));
        putIfPresent(request, "async_return_url", properties.getMerchantOnboardingNotifyUrl());
        try {
            synchronized (SDK_LOCK) {
                initializeSdk();
                Map<String, Object> response = BasePayClient.request(request, "v2.merchant.basicdata.ent", false);
                log.info("Huifu enterprise merchant onboarding response: applicationNo={}, upperHuifuId={}, response={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), JSON.toJSONString(response));
                return Result.from(response);
            }
        } catch (BusinessException exception) {
            throw exception;
        } catch (Exception exception) {
            log.error("Huifu enterprise merchant onboarding request error: applicationNo={}, upperHuifuId={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), exception);
            throw new HuifuPaymentGatewayException("Huifu enterprise merchant onboarding request failed", outcomeUnknown(exception), exception);
        }
        return request;
    }
    /**
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuIndividualOnboardingCommand.java
@@ -20,7 +20,5 @@
        String bankProvinceCode,
        String bankCityCode,
        String bankCode,
        String settlementCardFrontPictureId,
        /** 平台自己的汇付号:非空则把该商户建成平台的下级商户(head_office_flag=0 + head_huifu_id)。 */
        String parentHuifuId
        String settlementCardFrontPictureId
) {}
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuIndividualOnboardingGateway.java
@@ -25,6 +25,31 @@
    public Result create(HuifuIndividualOnboardingCommand command) {
        ensureConfigured();
        Map<String, Object> request = buildRequest(command);
        try {
            synchronized (SDK_LOCK) {
                initializeSdk();
                Map<String, Object> response = BasePayClient.request(request, "v2.merchant.basicdata.indv", false);
                log.info("Huifu individual merchant onboarding response: applicationNo={}, upperHuifuId={}, response={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), JSON.toJSONString(response));
                return Result.from(response);
            }
        } catch (BusinessException exception) {
            throw exception;
        } catch (Exception exception) {
            log.error("Huifu individual merchant onboarding request error: applicationNo={}, upperHuifuId={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), exception);
            throw new HuifuPaymentGatewayException("Huifu individual merchant onboarding request failed", outcomeUnknown(exception), exception);
        }
    }
    /**
     * 组装个人进件请求体,与发送解耦,便于直接断言商户归属
     * (见 {@code HuifuOnboardingMerchantLevelTests})。
     */
    static Map<String, Object> buildRequestForTest(HuifuPaymentProperties properties, HuifuIndividualOnboardingCommand command) {
        return new HuifuIndividualOnboardingGateway(properties).buildRequest(command);
    }
    private Map<String, Object> buildRequest(HuifuIndividualOnboardingCommand command) {
        Map<String, Object> card = new LinkedHashMap<>();
        card.put("card_type", "1");
        card.put("card_name", command.name());
@@ -49,8 +74,6 @@
        request.put("req_seq_id", command.applicationNo());
        request.put("req_date", LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE));
        request.put("upper_huifu_id", properties.getOnboardingUpperHuifuId().trim());
        // 建成平台自己的下级商户:只有挂在自己名下,平台才有权为该商户配置微信支付与业务开通。
        HuifuParentMerchant.apply(request, command.parentHuifuId());
        request.put("reg_name", command.name());
        request.put("short_name", command.name());
        request.put("mcc", command.mcc());
@@ -76,19 +99,7 @@
        // 与商户主体一致:进件即打开自动结算,核销确认后的钱由斗拱按结算周期自动划到结算卡。
        request.put("settle_config", JSON.toJSONString(HuifuEnterpriseOnboardingGateway.settleConfig()));
        putIfPresent(request, "async_return_url", properties.getMerchantOnboardingNotifyUrl());
        try {
            synchronized (SDK_LOCK) {
                initializeSdk();
                Map<String, Object> response = BasePayClient.request(request, "v2.merchant.basicdata.indv", false);
                log.info("Huifu individual merchant onboarding response: applicationNo={}, upperHuifuId={}, response={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), JSON.toJSONString(response));
                return Result.from(response);
            }
        } catch (BusinessException exception) {
            throw exception;
        } catch (Exception exception) {
            log.error("Huifu individual merchant onboarding request error: applicationNo={}, upperHuifuId={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), exception);
            throw new HuifuPaymentGatewayException("Huifu individual merchant onboarding request failed", outcomeUnknown(exception), exception);
        }
        return request;
    }
    private void ensureConfigured() {
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuMerchantBusinessOpenGateway.java
@@ -96,10 +96,9 @@
        // 核销确认后才分配给商户。forced_delay_flag 保持关闭——支付即核销的到店消费订单不该被延迟。
        request.put("delay_flag", "Y");
        request.put("forced_delay_flag", "N");
        // 子商户复用平台的微信商户号发起交易,不必为每个商户单独申请微信商户号。
        // 前提是上级(平台)已开通微信功能——平台的 wx_conf_list 三个 payScene 都是 openFlag=Y。
        // 3 = 仅微信;文档:使用上级微信、支付宝商户号需对应开通微信、支付宝功能。
        if (properties.isOnboardingUnderParentMerchant()) request.put("use_chains_flag", "3");
        // 子商户复用上级(渠道商)的微信商户号发起交易:3 = 仅微信。
        // 文档:使用上级微信、支付宝商户号需对应开通微信、支付宝功能。
        putIfPresent(request, "use_chains_flag", properties.getMerchantUseChainsFlag());
        putIfPresent(request, "async_return_url", properties.getMerchantBusinessOpenNotifyUrl());
        putIfPresent(request, "busi_async_return_url", properties.getMerchantBusinessOpenNotifyUrl());
        return request;
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuParentMerchant.java
File was deleted
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuPaymentProperties.java
@@ -18,21 +18,30 @@
    private String merchantOnboardingNotifyUrl = "";
    private String merchantWechatConfigNotifyUrl = "";
    private String merchantBusinessOpenNotifyUrl = "";
    /**
     * 进件时填在 {@code upper_huifu_id} 上的直属渠道号:新商户进件完成后归属这个渠道商。
     *
     * <p>层级只有两级——渠道商 → 商户,商户是**普通商户**,我们不再用
     * {@code head_office_flag=0} + {@code head_huifu_id} 把商户建成平台自己的下级商户。
     * 那条路实测没有任何收益:带着 head 字段进件成功后,平台对新建商户调用
     * {@code v2.merchant.busi.config} / {@code v2.merchant.busi.open} 仍然一律返回
     * {@code 20002 数据权限认证失败}(head/config 申请单已审核通过也一样);而渠道商直属的
     * 普通商户(如 {@code 6666000241335656})同样是这个回执,说明 20002 与层级无关,
     * 取决于签名身份本身有没有那些商户的数据权限。见
     * {@code .scratch/merchant-onboarding/NEXT-SESSION-PROMPT.md}。
     *
     * <p>反过来说:加 head 字段还会按总部客群类型索要 F725/F726/F727 材料,
     * 平白增加进件失败面,所以这里保持「只挂渠道商」。
     */
    private String onboardingUpperHuifuId = "";
    /**
     * 进件时把新商户挂到这个汇付号下,作为它的下级商户({@code head_office_flag=0} + {@code head_huifu_id})。
     * 商户业务开通的 {@code use_chains_flag}:是否用上级(渠道商)的微信商户号发起交易。
     *
     * <p>留空则按普通商户进件,商户归属由 {@code upper_huifu_id}(渠道商号)决定,
     * 而平台自己的身份**无权操作**这类商户——实测对它们调用商户业务开通、微信配置、
     * 商户信息查询一律返回 {@code 20002 数据权限认证失败},进件后的自动配置全部做不了。
     *
     * <p>填成平台自己的汇付号({@code merchant-id})后,商户建在平台名下,平台就能自动完成
     * 「微信小程序支付配置 + 商户业务开通」,商户端无需参与。
     *
     * <p>前置条件:该汇付号必须先通过「开通下级商户权限配置接口」
     * ({@code v2/merchant/busi/head/config})获得开通下级商户的权限。
     * <p>我们是平台自有的一个小程序服务多个商户,没有实现「微信特约商户进件」给每个商户单独
     * 申请微信商户号,所以保持 {@code 3}——仅微信、复用上级商户号。留空则不下发该字段,
     * 由商户使用自己的微信商户号。
     */
    private String onboardingParentHuifuId = "";
    private String merchantUseChainsFlag = "3";
    private String merchantWechatFeeRate = "0.60";
    private String merchantOnlineBusinessType = "H5311L";
    private String merchantOnlineMediaName = "东区人才卡";
@@ -98,13 +107,8 @@
        return merchantId;
    }
    public void setOnboardingUpperHuifuId(String onboardingUpperHuifuId) { this.onboardingUpperHuifuId = onboardingUpperHuifuId; }
    public String getOnboardingParentHuifuId() { return onboardingParentHuifuId == null ? "" : onboardingParentHuifuId.trim(); }
    public void setOnboardingParentHuifuId(String onboardingParentHuifuId) { this.onboardingParentHuifuId = onboardingParentHuifuId; }
    /**
     * 是否把新商户建成平台的下级商户。同时配好「上级商户号发起微信交易」,
     * 让子商户复用平台的微信商户号,不必逐个单独申请。
     */
    public boolean isOnboardingUnderParentMerchant() { return !getOnboardingParentHuifuId().isBlank(); }
    public String getMerchantUseChainsFlag() { return merchantUseChainsFlag == null ? "" : merchantUseChainsFlag.trim(); }
    public void setMerchantUseChainsFlag(String merchantUseChainsFlag) { this.merchantUseChainsFlag = merchantUseChainsFlag; }
    public String getMerchantWechatFeeRate() { return merchantWechatFeeRate; }
    public void setMerchantWechatFeeRate(String merchantWechatFeeRate) { this.merchantWechatFeeRate = merchantWechatFeeRate; }
    public String getMerchantOnlineBusinessType() { return merchantOnlineBusinessType; }
talent-card-server/src/main/resources/application.yml
@@ -85,11 +85,11 @@
      merchant-wechat-config-notify-url: ${HUIFU_MERCHANT_WECHAT_CONFIG_NOTIFY_URL:http://221.182.45.100:11451/api/payments/huifu/merchant-wechat-config-notify}
      merchant-business-open-notify-url: ${HUIFU_MERCHANT_BUSINESS_OPEN_NOTIFY_URL:http://221.182.45.100:11451/api/payments/huifu/merchant-business-open-notify}
      onboarding-upper-huifu-id: ${HUIFU_ONBOARDING_UPPER_HUIFU_ID:6666000220591054}
      # 把新商户建成平台自己的下级商户(head_office_flag=0 + head_huifu_id)。
      # 不建在自己名下的话,平台无权操作该商户,进件后的「微信支付配置 + 业务开通」全部
      # 会返回 20002 数据权限认证失败,商户就得自己去汇付控台配置 —— 那正是要避免的。
      # 置空则退回普通进件(归属由 upper-huifu-id 决定),仅用于排查。
      onboarding-parent-huifu-id: ${HUIFU_ONBOARDING_PARENT_HUIFU_ID:6666000226048687}
      # 进件只做「渠道商 → 普通商户」两级:不再用 head_office_flag=0 + head_huifu_id 把商户
      # 建成平台自己的下级商户。实测那条路没有收益(建完照样 20002 数据权限认证失败),
      # 还会按总部客群类型索要 F725/F726/F727 材料。
      # 商户业务开通是否复用上级(渠道商)的微信商户号:3=仅微信;留空=不下发该字段。
      merchant-use-chains-flag: ${HUIFU_MERCHANT_USE_CHAINS_FLAG:3}
      merchant-wechat-fee-rate: ${HUIFU_MERCHANT_WECHAT_FEE_RATE:0.60}
      merchant-online-business-type: ${HUIFU_MERCHANT_ONLINE_BUSINESS_TYPE:H5311L}
      merchant-online-media-name: ${HUIFU_MERCHANT_ONLINE_MEDIA_NAME:东区人才卡}
talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuOnboardingMerchantLevelTests.java
New file
@@ -0,0 +1,106 @@
package com.talentcard.server.business.payment.huifu;
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Map;
import org.junit.jupiter.api.Test;
/**
 * 进件的商户层级:只有「渠道商 → 普通商户」两级。
 *
 * <p>这里钉住的是一条**被实测否掉的旧做法**。平台曾经用
 * {@code head_office_flag=0} + {@code head_huifu_id=<平台汇付号>} 把新商户建成平台自己的下级商户,
 * 期望平台随后有权给它配微信支付与业务开通。真实通道上的对照实验推翻了它:
 *
 * <ul>
 *   <li>带 head 字段进件成功({@code huifu_id=6666000241506776},{@code resp_code=90000000 审核中}),
 *       紧接着调 {@code v2.merchant.busi.config} 与 {@code v2.merchant.busi.open},
 *       仍然 `20002 数据权限认证失败`;</li>
 *   <li>{@code head/config}(开通下级商户权限)申请单已 {@code apply_status=Y},不改变结果;</li>
 *   <li>渠道商直属的普通商户 {@code 6666000241335656}(该笔进件早于 head 改动,确定没有 head 字段)
 *       同样返回 {@code 20002}。</li>
 * </ul>
 *
 * <p>结论有两个:20002 与层级无关,取决于签名身份本身有没有那些商户的数据权限;而 head 层级
 * 不但没有收益,还会按总部客群类型索要 F725/F726/F727 材料,白白增加进件失败面。所以进件请求里
 * **不允许**再出现这两个字段。
 */
class HuifuOnboardingMerchantLevelTests {
    private static final String CHANNEL_HUIFU_ID = "6666000220591054";
    @Test
    void anIndividualOnboardingBelongsToTheChannelOnly() {
        Map<String, Object> request = HuifuIndividualOnboardingGateway.buildRequestForTest(properties(), individualCommand());
        assertThat(request).containsEntry("upper_huifu_id", CHANNEL_HUIFU_ID);
        assertThat(request).doesNotContainKeys("head_office_flag", "head_huifu_id");
    }
    @Test
    void anEnterpriseOnboardingBelongsToTheChannelOnly() {
        Map<String, Object> request = HuifuEnterpriseOnboardingGateway.buildRequestForTest(properties(), enterpriseCommand());
        assertThat(request).containsEntry("upper_huifu_id", CHANNEL_HUIFU_ID);
        assertThat(request).doesNotContainKeys("head_office_flag", "head_huifu_id");
    }
    /**
     * 商户自己没有微信商户号(我们没实现「微信特约商户进件」),开通时仍然复用上级商户号发起交易,
     * 否则子商户根本没有可用的微信商户号。留空该配置即不下发字段。
     */
    @Test
    void businessOpenReusesTheSuperiorWeChatMerchantNumber() {
        Map<String, Object> request = HuifuMerchantBusinessOpenGateway.buildRequestForTest(
                properties(), "BO-TEST-1", "6666000241335656", "成都喜望科技", "5812", false);
        assertThat(request).containsEntry("use_chains_flag", "3");
    }
    @Test
    void aBlankUseChainsFlagKeepsTheFieldOutOfTheRequest() {
        HuifuPaymentProperties properties = properties();
        properties.setMerchantUseChainsFlag(" ");
        Map<String, Object> request = HuifuMerchantBusinessOpenGateway.buildRequestForTest(
                properties, "BO-TEST-1", "6666000241335656", "成都喜望科技", "5812", false);
        assertThat(request).doesNotContainKey("use_chains_flag");
    }
    private static HuifuPaymentProperties properties() {
        HuifuPaymentProperties properties = new HuifuPaymentProperties();
        properties.setEnabled(true);
        properties.setSystemId("6666000226048687");
        properties.setProductId("KAZX");
        properties.setPrivateKey("x");
        properties.setPlatformPublicKey("y");
        properties.setMerchantWechatFeeRate("0.60");
        properties.setMerchantOnlineBusinessType("H5311L");
        properties.setMerchantOnlineMediaName("东区人才卡");
        properties.setMerchantAgreementUrl("https://example.com/agreement.htm");
        properties.setOnboardingUpperHuifuId(CHANNEL_HUIFU_ID);
        return properties;
    }
    private static HuifuIndividualOnboardingCommand individualCommand() {
        return new HuifuIndividualOnboardingCommand(
                "MO-TEST-1", "阳野", "511602199803310019", "0", "20120110", "20320110", "15708261096",
                "四川省广安市广安区兴安中街227号3栋1单元6楼2号", "1347747024@qq.com", "5812",
                "510000", "510100", "510104", "F40-ID", "F41-ID",
                "6217003680002760924", "510000", "511600", "01050000", "F13-CARD");
    }
    private static HuifuEnterpriseOnboardingCommand enterpriseCommand() {
        return new HuifuEnterpriseOnboardingCommand(
                "MO-TEST-2", "成都喜望科技有限公司", "成都喜望科技", "91510100MA6C5XXXXX", "0", "20180110", "20480110",
                "510000", "510100", "510104", "四川省成都市高新区天府大道1号", "张维", "510101199001010011",
                "0", "20120110", "20320110", "四川省成都市高新区天府大道1号", "张维", "13800000000", "zhangwei@example.com",
                "3", "5812", "F07-LICENSE", "F02-LEGAL", "F03-LEGAL",
                "0", "成都喜望科技有限公司", "6217003680002760924", "510000", "510100", "01050000",
                "CCB510400", "中国建设银行攀枝花东区支行营业部", "", "", "", "", "",
                "F08-ACCT", "", "", "",
                "20180110", "100.00");
    }
}
talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuOnboardingParentMerchantTests.java
File was deleted