From c18b9fac0edf5ebe3aecd74c93ec3f841fe5bf9e Mon Sep 17 00:00:00 2001
From: yeg <1347747024@qq.com>
Date: 星期三, 16 九月 2026 00:13:18 +0800
Subject: [PATCH] fix(merchant): 修正存量商户微信补开的请求组装与审核中判定
---
talent-card-server/src/main/resources/application.yml | 24 +++-
talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuBusinessOpenResultTests.java | 36 +++++++
talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuBusinessOpenRequiredFieldsTests.java | 76 ++++++++++++--
talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuOnboardingMerchantLevelTests.java | 17 ++-
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuMerchantBusinessOpenGateway.java | 88 +++++++++++------
talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuPaymentProperties.java | 9 +
6 files changed, 186 insertions(+), 64 deletions(-)
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuMerchantBusinessOpenGateway.java b/talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuMerchantBusinessOpenGateway.java
index 22fcd73..84b5493 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuMerchantBusinessOpenGateway.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuMerchantBusinessOpenGateway.java
@@ -8,6 +8,7 @@
import com.talentcard.server.common.ErrorCode;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
+import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -53,33 +54,45 @@
return new HuifuMerchantBusinessOpenGateway(properties).buildRequest(requestNo, huifuId, shortName, mcc, individualMerchant);
}
- private Map<String, Object> buildRequest(String requestNo, String huifuId, String shortName, String mcc, boolean individualMerchant) {
- Map<String, Object> wechat = new LinkedHashMap<>();
- wechat.put("pay_scene", "5");
- // 企业微信业务使用汇付确认的 fee_rule_id;仅在未配置规则 ID 时兼容旧的 mcc 路径。
- // 不得把参考商户的规则 ID 猜测套用到其它行业。
- if (!properties.getMerchantWechatFeeRuleId().isBlank()) {
- wechat.put("fee_rule_id", properties.getMerchantWechatFeeRuleId());
- } else {
- // mcc:文档注明「个体工商户、小微商户必填」——个人主体属于小微商户,所以必须带上。
- wechat.put("mcc", mcc);
- }
- wechat.put("fee_rate", properties.getMerchantWechatFeeRate().trim());
- // 实名认证由通道在业务开通成功后另行发起;取 N 表示本次不发起。
- // 改成 Y 之前要先确认实名所需的材料(含 F22/F24 照片)已就绪。
- wechat.put("is_check_real_name", "N");
- if (individualMerchant) {
- String microBizType = properties.getMerchantMicroBusinessType();
- if (!microBizType.isBlank()) {
- Map<String, Object> realName = new LinkedHashMap<>();
- realName.put("micro_biz_type", microBizType);
- wechat.put("wx_real_name_info", JSON.toJSONString(realName));
- }
- }
+ /**
+ * 线上微信业务场景:4=线上公众号、5=线上小程序、12=线上反扫。
+ *
+ * <p>三个场景一起开:平台的小程序、H5 公众号与反扫共用同一个子商户号,只开小程序会让另外两条
+ * 支付路径在通道侧没有配置。汇付《微信费率规则ID》表里场景 12 没有独立行,它与 4/5 共用同一条
+ * 线上规则——参考商户 {@code 6666000226048687} 线上三场景费率同为 0.70% 且真实生效。
+ */
+ private static final List<String> ONLINE_WECHAT_SCENES = List.of("4", "5", "12");
- Map<String, Object> media = new LinkedHashMap<>();
- media.put("media_type", "S3");
- media.put("media_name", properties.getMerchantOnlineMediaName().trim());
+ private Map<String, Object> buildRequest(String requestNo, String huifuId, String shortName, String mcc, boolean individualMerchant) {
+ String feeRuleId = properties.getMerchantWechatFeeRuleId();
+ List<Map<String, Object>> wechatList = new ArrayList<>();
+ for (String scene : ONLINE_WECHAT_SCENES) {
+ Map<String, Object> wechat = new LinkedHashMap<>();
+ wechat.put("pay_scene", scene);
+ // 企业微信业务使用汇付确认的 fee_rule_id;仅在未配置规则 ID 时兼容旧的 mcc 路径。
+ // 不得把参考商户的规则 ID 猜测套用到其它行业。
+ if (!feeRuleId.isBlank()) {
+ wechat.put("fee_rule_id", feeRuleId);
+ } else {
+ // mcc:文档注明「个体工商户、小微商户必填」——个人主体属于小微商户,所以必须带上。
+ wechat.put("mcc", mcc);
+ }
+ wechat.put("fee_rate", properties.getMerchantWechatFeeRate().trim());
+ // 最低收取手续费留空:通道按不设最低收费处理,详情查询读回 fee_min_amt=0.00
+ // (参考商户线上三场景实测就是 0.00)。该字段可选且写明「大于 0」,不要主动送 0。
+ // 实名认证由通道在业务开通成功后另行发起;取 N 表示本次不发起。
+ // 改成 Y 之前要先确认实名所需的材料(含 F22/F24 照片)已就绪。
+ wechat.put("is_check_real_name", "N");
+ if (individualMerchant) {
+ String microBizType = properties.getMerchantMicroBusinessType();
+ if (!microBizType.isBlank()) {
+ Map<String, Object> realName = new LinkedHashMap<>();
+ realName.put("micro_biz_type", microBizType);
+ wechat.put("wx_real_name_info", JSON.toJSONString(realName));
+ }
+ }
+ wechatList.add(wechat);
+ }
Map<String, Object> agreement = new LinkedHashMap<>();
agreement.put("agreement_type", "3");
@@ -91,12 +104,19 @@
request.put("huifu_id", huifuId);
request.put("upper_huifu_id", properties.getOnboardingUpperHuifuId().trim());
request.put("short_name", shortName);
- request.put("wx_conf_list", JSON.toJSONString(List.of(wechat)));
+ request.put("wx_conf_list", JSON.toJSONString(wechatList));
request.put("online_busi_type", properties.getMerchantOnlineBusinessType().trim());
- request.put("online_media_info_list", JSON.toJSONString(List.of(media)));
- // 运营媒介主体与商户主体是否一致。平台用一个运营媒介为众多子商户开通,主体天然不一致,
- // 而且它是顶层必填——不传会被通道拒(实测报「media_merCommon_flag…不能为空,或格式非法」)。
- putIfPresent(request, "media_mer_common_flag", properties.getMerchantMediaSubjectSame());
+ // 运营媒介只在开通快捷/网银/余额支付Pro/分账/大额转账/代扣时才需要,本网关只开线上微信,
+ // 因此默认不下发。要下发就把 merchant-online-media-name 配上;注意声明
+ // media_mer_common_flag=N(媒介主体与商户主体不一致)时,通道会一并索要授权或开发证明材料。
+ String mediaName = properties.getMerchantOnlineMediaName().trim();
+ if (!mediaName.isBlank()) {
+ Map<String, Object> media = new LinkedHashMap<>();
+ media.put("media_type", "S3");
+ media.put("media_name", mediaName);
+ putIfPresent(media, "media_mer_common_flag", properties.getMerchantMediaSubjectSame());
+ request.put("online_media_info_list", JSON.toJSONString(List.of(media)));
+ }
request.put("agreement_info", JSON.toJSONString(agreement));
// 进件时就把延迟入账能力配好:先付钱后核销的订单必须把钱留在通道「待确认」,
// 核销确认后才分配给商户。forced_delay_flag 保持关闭——支付即核销的到店消费订单不该被延迟。
@@ -112,7 +132,7 @@
private void ensureConfigured() {
if (!properties.isEnabled()) throw new BusinessException(ErrorCode.CONFLICT, "Huifu payment is disabled");
- if (blank(properties.getSystemId()) || blank(properties.getProductId()) || blank(properties.getOnboardingUpperHuifuId()) || blank(properties.getMerchantWechatFeeRate()) || blank(properties.getMerchantOnlineBusinessType()) || blank(properties.getMerchantOnlineMediaName()) || blank(properties.getMerchantAgreementUrl()) || blank(properties.getPrivateKey()) || blank(properties.getPlatformPublicKey())) throw new BusinessException(ErrorCode.CONFLICT, "Huifu merchant business opening configuration is incomplete");
+ if (blank(properties.getSystemId()) || blank(properties.getProductId()) || blank(properties.getOnboardingUpperHuifuId()) || blank(properties.getMerchantWechatFeeRate()) || blank(properties.getMerchantOnlineBusinessType()) || blank(properties.getMerchantAgreementUrl()) || blank(properties.getPrivateKey()) || blank(properties.getPlatformPublicKey())) throw new BusinessException(ErrorCode.CONFLICT, "Huifu merchant business opening configuration is incomplete");
}
private void initializeSdk() throws Exception {
@@ -138,7 +158,9 @@
String code = value(data, "sub_resp_code", "resp_code", "code");
return new Result(success(code), value(data, "apply_no", "applyNo"), code, value(data, "sub_resp_desc", "resp_desc", "message"));
}
- private static boolean success(String code) { return "00000000".equals(code) || "00000100".equals(code) || "10000".equals(code) || "0".equals(code) || "SUCCESS".equalsIgnoreCase(code); }
+ // 90000000 = 审核中:业务开通已被受理、微信子商户号当场就入驻成功,但整张申请单仍在审核。
+ // 它与两个进件网关的判定保持一致;漏掉它会把一次成功受理记成 business_open_status=failed。
+ private static boolean success(String code) { return "00000000".equals(code) || "00000100".equals(code) || "90000000".equals(code) || "10000".equals(code) || "0".equals(code) || "SUCCESS".equalsIgnoreCase(code); }
private static String value(Map<String, Object> data, String... keys) { for (String key : keys) if (data.get(key) != null) return String.valueOf(data.get(key)).trim(); return ""; }
}
}
diff --git a/talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuPaymentProperties.java b/talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuPaymentProperties.java
index 431d210..3e1dca0 100644
--- a/talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuPaymentProperties.java
+++ b/talent-card-server/src/main/java/com/talentcard/server/business/payment/huifu/HuifuPaymentProperties.java
@@ -37,11 +37,12 @@
/**
* 商户业务开通的 {@code use_chains_flag}:是否用上级(渠道商)的微信商户号发起交易。
*
- * <p>我们是平台自有的一个小程序服务多个商户,没有实现「微信特约商户进件」给每个商户单独
- * 申请微信商户号,所以保持 {@code 3}——仅微信、复用上级商户号。留空则不下发该字段,
- * 由商户使用自己的微信商户号。
+ * <p>留空(默认)即不下发该字段,子商户会在渠道下拿到自己的微信子商户号。
+ * 不要下发 {@code 3}:那是「复用上级微信商户号」,要求上级本身有商户级微信配置,而我们的上级是
+ * 渠道商(没有商户级配置,查询返回「huifuId不存在」),实测会被通道拒:
+ * 「失败:复用交易信息,上级商户号为空」。
*/
- private String merchantUseChainsFlag = "3";
+ private String merchantUseChainsFlag = "";
private String merchantWechatFeeRate = "0.60";
/**
* 企业微信业务开通使用的汇付费率规则 ID。企业场景应优先填写该值,
diff --git a/talent-card-server/src/main/resources/application.yml b/talent-card-server/src/main/resources/application.yml
index 33228f3..a5c0b2c 100644
--- a/talent-card-server/src/main/resources/application.yml
+++ b/talent-card-server/src/main/resources/application.yml
@@ -88,15 +88,23 @@
# 进件只做「渠道商 → 普通商户」两级:不再用 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}
- # 企业微信开通应填写汇付按实际行业确认的规则 ID;留空时沿用 mcc 兼容路径。
- merchant-wechat-fee-rule-id: ${HUIFU_MERCHANT_WECHAT_FEE_RULE_ID:}
+ # 是否复用上级(渠道商)的微信商户号:1=微信+支付宝,2=不使用,3=仅微信,4=支付宝;留空=不下发。
+ # 渠道商 6666000220591054 本身没有商户级微信配置(查询返回「huifuId不存在」),实测下发 3 会被拒:
+ # 「失败:复用交易信息,上级商户号为空」。不下发时,子商户在渠道 00016904 下拿到自己的微信
+ # 子商户号(实测 sub_mer_id=W15737921333057628112,sub_mer_name 即商户注册名)。
+ merchant-use-chains-flag: ${HUIFU_MERCHANT_USE_CHAINS_FLAG:}
+ merchant-wechat-fee-rate: ${HUIFU_MERCHANT_WECHAT_FEE_RATE:0.70}
+ # 企业微信业务的费率规则号(cid)。756 是汇付《微信费率规则ID》表里「企业 + fee_type=01(线上)」
+ # 唯一的一条规则,细分行业含「就业信息平台/其他信息服务平台」,与本平台业务对应;
+ # 场景 12 在该表没有独立行,与线上公众号/小程序共用这一条。留空则退回 mcc 兼容路径。
+ merchant-wechat-fee-rule-id: ${HUIFU_MERCHANT_WECHAT_FEE_RULE_ID:756}
merchant-online-business-type: ${HUIFU_MERCHANT_ONLINE_BUSINESS_TYPE:H5311L}
- merchant-online-media-name: ${HUIFU_MERCHANT_ONLINE_MEDIA_NAME:东区人才卡}
- # 运营媒介主体与商户主体是否一致。平台用一个运营媒介给众多子商户开通,主体天然不一致。
- # 这是商户业务开通的顶层必填,缺了会被通道拒(实测报 media_merCommon_flag 不能为空)。
+ # 运营媒介(online_media_info_list)。文档写明它只在开通快捷/网银/余额支付Pro/分账/
+ # 大额转账/代扣时才需要,且是「基本信息入驻接口」的必填项,不是「商户业务开通」的必填项;
+ # 本平台只开线上微信,所以默认留空即不下发。要下发时再配媒介名称。
+ merchant-online-media-name: ${HUIFU_MERCHANT_ONLINE_MEDIA_NAME:}
+ # 媒介主体与商户主体是否一致;它是 online_media_info_list 的**元素字段**,不是顶层参数。
+ # 声明 N(不一致)时通道会一并索要授权或开发证明材料,因此不下发媒介时它也用不上。
merchant-media-subject-same: ${HUIFU_MERCHANT_MEDIA_SUBJECT_SAME:N}
# 小微经营类型,个人商户开通微信业务时必填;三种取值都要求 F22 门头照 + F24 内景照。
merchant-micro-business-type: ${HUIFU_MERCHANT_MICRO_BUSINESS_TYPE:MICRO_TYPE_ONLINE}
diff --git a/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuBusinessOpenRequiredFieldsTests.java b/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuBusinessOpenRequiredFieldsTests.java
index 7e55a80..fe0091d 100644
--- a/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuBusinessOpenRequiredFieldsTests.java
+++ b/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuBusinessOpenRequiredFieldsTests.java
@@ -2,6 +2,7 @@
import static org.assertj.core.api.Assertions.assertThat;
+import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -14,9 +15,9 @@
* <p>以汇付《商户业务开通》(v2.merchant.busi.open) 的请求规则为准逐项核对后发现两处缺口:
*
* <ul>
- * <li>{@code media_mer_common_flag}(顶层):运营媒介主体与商户主体是否一致。平台用一个运营媒介
- * 给众多子商户开通,主体天然不一致。实测缺它会被拒:
- * 「【media_merCommon_flag-媒介主体与商户主体是否一致】不能为空,或格式非法」。</li>
+ * <li>{@code media_mer_common_flag}({@code online_media_info_list} 的元素字段):运营媒介主体与商户主体
+ * 是否一致。平台用一个运营媒介给众多子商户开通,主体天然不一致,取 N。它属于媒介**元素**而不是顶层参数,
+ * 放顶层会被拒:「【media_merCommon_flag-媒介主体与商户主体是否一致】不能为空,或格式非法」。</li>
* <li>{@code micro_biz_type}(wx_conf_list 元素内的 wx_real_name_info):个人商户开通微信业务必填,
* 且三种取值都要求 F22 店铺门头照 + F24 店铺内景照。</li>
* </ul>
@@ -43,11 +44,30 @@
}
@Test
- void theMediaSubjectFlagIsAlwaysSent() {
+ void theMediaSubjectFlagIsInsideTheMediaElement() {
Map<String, Object> request = request(false);
- // 平台媒介主体与商户主体不一致,取 N;关键是必须出现,否则通道判参数非法。
- assertThat(request).containsEntry("media_mer_common_flag", "N");
+ // 平台媒介主体与商户主体不一致,取 N。它是 online_media_info_list 的**元素字段**,
+ // 不是顶层参数——放顶层会被通道判「不能为空,或格式非法」。
+ assertThat(request).doesNotContainKey("media_mer_common_flag");
+
+ Map<String, Object> media = onlineMedia(request);
+ assertThat(media).containsEntry("media_mer_common_flag", "N");
+ }
+
+ /**
+ * 运营媒介只在开通快捷/网银/余额支付Pro/分账/大额转账/代扣时才需要,本平台只开线上微信,
+ * 默认不下发;不下发媒介,通道也就不会再索要「授权或开发证明材料」。
+ */
+ @Test
+ void aBlankMediaNameKeepsTheMediaDeclarationOutOfTheRequest() {
+ HuifuPaymentProperties properties = properties();
+ properties.setMerchantOnlineMediaName(" ");
+
+ Map<String, Object> request = HuifuMerchantBusinessOpenGateway.buildRequestForTest(
+ properties, "BO-TEST-1", "6666000241390821", "阳野", "5812", false);
+
+ assertThat(request).doesNotContainKey("online_media_info_list");
}
@Test
@@ -55,9 +75,9 @@
Map<String, Object> request = request(true);
List<Map<String, Object>> wechat = wechatConfig(request);
- assertThat(wechat).hasSize(1);
- // 个人主体属于小微商户,mcc 也是必填。
- assertThat(wechat.get(0)).containsKeys("mcc", "wx_real_name_info");
+ assertThat(wechat).hasSize(3);
+ // 个人主体属于小微商户,mcc 也是必填;三个线上场景都要带。
+ assertThat(wechat).allSatisfy(entry -> assertThat(entry).containsKeys("mcc", "wx_real_name_info"));
// 层级:micro_biz_type 在 wx_real_name_info 这个嵌套 jsonObject 里,不在元素顶层。
assertThat(wechat.get(0)).doesNotContainKey("micro_biz_type");
@@ -71,7 +91,7 @@
Map<String, Object> request = request(false);
List<Map<String, Object>> wechat = wechatConfig(request);
- assertThat(wechat.get(0)).doesNotContainKey("wx_real_name_info");
+ assertThat(wechat).allSatisfy(entry -> assertThat(entry).doesNotContainKey("wx_real_name_info"));
}
@Test
@@ -82,8 +102,32 @@
List<Map<String, Object>> wechat = wechatConfig(HuifuMerchantBusinessOpenGateway.buildRequestForTest(
properties, "BO-TEST-RULE", "6666000241335656", "成都喜望科技", "7372", false));
- assertThat(wechat.get(0)).containsEntry("fee_rule_id", "756");
- assertThat(wechat.get(0)).doesNotContainKey("mcc");
+ assertThat(wechat).allSatisfy(entry -> {
+ assertThat(entry).containsEntry("fee_rule_id", "756");
+ assertThat(entry).doesNotContainKey("mcc");
+ });
+ }
+
+ /**
+ * 线上公众号、小程序、反扫三个场景一起开。只开小程序会让另外两条支付路径在通道侧缺配置,
+ * 而平台的小程序、H5 公众号与反扫共用同一个子商户号。
+ */
+ @Test
+ void theOnlineWeChatScenesAreOpenedTogether() {
+ List<Map<String, Object>> wechat = wechatConfig(request(false));
+
+ List<String> scenes = new ArrayList<>();
+ for (Map<String, Object> entry : wechat) scenes.add(String.valueOf(entry.get("pay_scene")));
+ assertThat(scenes).containsExactly("4", "5", "12");
+
+ assertThat(wechat).allSatisfy(entry -> {
+ assertThat(entry).containsEntry("fee_rate", "0.60");
+ // 最低收取手续费可选、文档写明「大于 0」:留空让通道按不设最低收费处理,
+ // 详情查询读回 fee_min_amt=0.00(参考商户线上三场景实测即 0.00),不要主动送 0。
+ assertThat(entry).doesNotContainKey("fee_min_amt");
+ // 不填子渠道号即入驻到渠道默认配置的微信渠道号下。
+ assertThat(entry).doesNotContainKey("pay_channel_id");
+ });
}
/**
@@ -100,4 +144,12 @@
private List<Map<String, Object>> wechatConfig(Map<String, Object> request) {
return JSON.parseObject(String.valueOf(request.get("wx_conf_list")), List.class);
}
+
+ @SuppressWarnings("unchecked")
+ private Map<String, Object> onlineMedia(Map<String, Object> request) {
+ List<Map<String, Object>> media =
+ JSON.parseObject(String.valueOf(request.get("online_media_info_list")), List.class);
+ assertThat(media).hasSize(1);
+ return media.get(0);
+ }
}
diff --git a/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuBusinessOpenResultTests.java b/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuBusinessOpenResultTests.java
new file mode 100644
index 0000000..f122f5d
--- /dev/null
+++ b/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuBusinessOpenResultTests.java
@@ -0,0 +1,36 @@
+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 90000000=审核中} 表示申请已被受理(微信子商户号当场就入驻成功),整张申请单仍在审核,
+ * 它与两个进件网关的判定一致。漏掉它会把一次成功受理记成 {@code business_open_status=failed},
+ * 两端页面就会把已经开好的商户显示成失败。
+ */
+class HuifuBusinessOpenResultTests {
+
+ @Test
+ void aSubmittedApplicationUnderReviewCountsAsAccepted() {
+ HuifuMerchantBusinessOpenGateway.Result result = HuifuMerchantBusinessOpenGateway.Result.from(
+ Map.of("resp_code", "90000000", "resp_desc", "审核中", "apply_no", "2026091530554624"));
+
+ assertThat(result.accepted()).isTrue();
+ assertThat(result.applyNo()).isEqualTo("2026091530554624");
+ assertThat(result.code()).isEqualTo("90000000");
+ }
+
+ @Test
+ void aRejectedApplicationIsNotAccepted() {
+ HuifuMerchantBusinessOpenGateway.Result result = HuifuMerchantBusinessOpenGateway.Result.from(
+ Map.of("resp_code", "00000001", "resp_desc", "基础参数校验失败"));
+
+ assertThat(result.accepted()).isFalse();
+ assertThat(result.applyNo()).isEmpty();
+ }
+}
diff --git a/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuOnboardingMerchantLevelTests.java b/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuOnboardingMerchantLevelTests.java
index 83052e9..cdaaf24 100644
--- a/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuOnboardingMerchantLevelTests.java
+++ b/talent-card-server/src/test/java/com/talentcard/server/business/payment/huifu/HuifuOnboardingMerchantLevelTests.java
@@ -47,26 +47,29 @@
}
/**
- * 商户自己没有微信商户号(我们没实现「微信特约商户进件」),开通时仍然复用上级商户号发起交易,
- * 否则子商户根本没有可用的微信商户号。留空该配置即不下发字段。
+ * 默认不下发 {@code use_chains_flag}:子商户在渠道下取得自己的微信子商户号。
+ *
+ * <p>下发 {@code 3}(复用上级微信商户号)曾是默认值,但上级是渠道商、没有商户级微信配置,
+ * 通道会拒:「失败:复用交易信息,上级商户号为空」。2026-09-15 的真实补开在不带该字段时被受理,
+ * 线上公众号/小程序/反扫三场景各自入驻成功并返回自己的 {@code sub_mer_id}。
*/
@Test
- void businessOpenReusesTheSuperiorWeChatMerchantNumber() {
+ void businessOpenLeavesTheUseChainsFlagOutByDefault() {
Map<String, Object> request = HuifuMerchantBusinessOpenGateway.buildRequestForTest(
properties(), "BO-TEST-1", "6666000241335656", "成都喜望科技", "5812", false);
- assertThat(request).containsEntry("use_chains_flag", "3");
+ assertThat(request).doesNotContainKey("use_chains_flag");
}
@Test
- void aBlankUseChainsFlagKeepsTheFieldOutOfTheRequest() {
+ void aConfiguredUseChainsFlagIsSentAsIs() {
HuifuPaymentProperties properties = properties();
- properties.setMerchantUseChainsFlag(" ");
+ properties.setMerchantUseChainsFlag("3");
Map<String, Object> request = HuifuMerchantBusinessOpenGateway.buildRequestForTest(
properties, "BO-TEST-1", "6666000241335656", "成都喜望科技", "5812", false);
- assertThat(request).doesNotContainKey("use_chains_flag");
+ assertThat(request).containsEntry("use_chains_flag", "3");
}
private static HuifuPaymentProperties properties() {
--
Gitblit v1.7.1