yeg
2026-09-24 3dff4451ffe12e87d16792cdd8f744e1e29aad9c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
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 name}/{@code mobile}/{@code id_card_number} 三项都必须是**法人本人**的,
 * 不能用联系人资料顶替。实测那张商户的联系人是经办人、法人是另一个人,混淆会把经办人的号码
 * 报给微信审核,而这一关是驳回要换申请单重来的。
 */
class HuifuMerchantRealnameTests {
 
    @Test
    void anEnterpriseRealnameApplicationUsesTheLegalPersonOnly() {
        Map<String,Object> request = request(command("成都市成华区东丽街138号9栋2单元16楼5号",true));
 
        assertThat(request).containsEntry("contact_type","LEGAL");
        // pay_scene 引用的是「微信业务开通类型」,线下是 02、线上是 01,不是业务开通的支付场景 1/2/3。
        assertThat(request).containsEntry("pay_scene","02");
        assertThat(request).containsEntry("name","张维");
        assertThat(request).containsEntry("mobile","13900000000");
        assertThat(request).containsEntry("id_card_number","513021198109304667");
        assertThat(request).containsEntry("owner","Y");
        assertThat(request).containsEntry("identification_address","成都市成华区东丽街138号9栋2单元16楼5号");
        assertThat(request).containsEntry("finance_institution_flag","N");
    }
 
    @Test
    void aLegalPersonWhoIsNotTheBeneficiaryIsDeclaredAsSuch() {
        assertThat(request(command("成都市成华区东丽街138号",false))).containsEntry("owner","N");
    }
 
    /** 法人姓名与身份证号按大写提交;手机号必须原样是法人本人的。 */
    @Test
    void theLegalIdNumberIsNormalisedToUpperCase() {
        Map<String,Object> request = request(new HuifuMerchantRealnameCommand("张维","13900000000","513021198109304667x","成都市",true,null));
        assertThat(request).containsEntry("id_card_number","513021198109304667X");
    }
 
    @Test
    void aSubmittedRealnameApplicationCountsAsAccepted() {
        HuifuMerchantRealnameGateway.Result accepted = HuifuMerchantRealnameGateway.Result.from(
                Map.of("resp_code","00000000","resp_desc","成功","applyment_id","2000002327252449"));
        assertThat(accepted.accepted()).isTrue();
        assertThat(accepted.applymentId()).isEqualTo("2000002327252449");
        assertThat(accepted.code()).isEqualTo("00000000");
    }
 
    @Test
    void aRejectedRealnameApplicationIsNotAccepted() {
        HuifuMerchantRealnameGateway.Result rejected = HuifuMerchantRealnameGateway.Result.from(
                Map.of("resp_code","00000001","resp_desc","基础参数校验失败"));
        assertThat(rejected.accepted()).isFalse();
        assertThat(rejected.applymentId()).isEmpty();
    }
 
    /**
     * 个人(小微)主体的实名另有一组门店字段,字段名与业务开通那套**不一样**:
     * 实名用 {@code store_header_copy}/{@code store_indoor_copy}(不是业务开通的
     * {@code store_header_pic}/{@code store_indoor_pic}),身份证用的是
     * {@code identification_front_copy}/{@code identification_back_copy}。
     */
    @Test
    void anIndividualRealnameApplicationCarriesTheStoreMaterial() {
        Map<String,Object> request = request(individualCommand());
 
        assertThat(request).containsEntry("micro_biz_type","MICRO_TYPE_STORE");
        assertThat(request).containsEntry("store_name","张维的餐饮店");
        assertThat(request).containsEntry("store_header_copy","file-f22");
        assertThat(request).containsEntry("store_indoor_copy","file-f24");
        assertThat(request).containsEntry("store_address_code","510100");
        assertThat(request).containsEntry("store_address","成都市成华区东丽街138号");
        assertThat(request).containsEntry("identification_front_copy","file-f40");
        assertThat(request).containsEntry("identification_back_copy","file-f41");
        // 文档写明 identification_address「主体类型为企业时,需要填写。其他主体类型,无需上传」。
        assertThat(request).doesNotContainKey("identification_address");
        // 企业才要 owner(是否为受益人);小微不填,避免把不在受益人名单上的经营者报成受益人。
        assertThat(request).containsEntry("owner","N");
    }
 
    /** 企业主体一个门店字段都不该出现——企业实名走营业执照与法人资料,不要塞小微材料。 */
    @Test
    void anEnterpriseRealnameApplicationCarriesNoStoreMaterial() {
        Map<String,Object> request = request(command("成都市成华区东丽街138号",true));
 
        assertThat(request).doesNotContainKeys("micro_biz_type","store_name","store_header_copy",
                "store_indoor_copy","store_address_code","store_address",
                "identification_front_copy","identification_back_copy");
    }
 
    private static HuifuMerchantRealnameCommand individualCommand() {
        return new HuifuMerchantRealnameCommand("张维","13900000000","513021198109304667","",false,
                new HuifuMerchantRealnameCommand.MicroStore("MICRO_TYPE_STORE","张维的餐饮店","file-f22","file-f24",
                        "510100","成都市成华区东丽街138号","file-f40","file-f41"));
    }
 
    private Map<String,Object> request(HuifuMerchantRealnameCommand command) {
        HuifuPaymentProperties properties = new HuifuPaymentProperties();
        properties.setEnabled(true);
        properties.setSystemId("6666000220591054");
        properties.setProductId("KAZX");
        properties.setPrivateKey("x");
        properties.setPlatformPublicKey("y");
        return HuifuMerchantRealnameGateway.buildRequestForTest(properties,"RN-TEST-1","6666000241335656",command);
    }
 
    private static HuifuMerchantRealnameCommand command(String legalAddress, boolean legalIsBeneficiary) {
        return new HuifuMerchantRealnameCommand("张维","13900000000","513021198109304667",legalAddress,legalIsBeneficiary,null);
    }
}