| | |
| | | assertThat(statusOf("wechat_config_status",String.valueOf(fixture.get("applicationNo")))).isEqualTo("approved"); |
| | | assertThat(statusOf("business_open_status",String.valueOf(fixture.get("applicationNo")))).isEqualTo("approved"); |
| | | verify(wechatConfigGateway,never()).configure(anyString(),anyString()); |
| | | verify(businessOpenGateway,never()).open(anyString(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),anyString(),anyString()); |
| | | verify(businessOpenGateway,never()).open(anyString(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),any(),any(),any()); |
| | | } |
| | | |
| | | /** |
| | |
| | | MerchantPrincipal principal=(MerchantPrincipal)fixture.get("principal"); |
| | | when(gateway.queryApplication(anyString(),anyString())).thenReturn(Map.of("resp_code","00000000","apply_status","Y","reg_status","S")); |
| | | when(gateway.queryCapabilities(anyString())).thenReturn(Map.of("resp_code","00000000","mer_stat","NORMAL","delay_flag","Y","forced_delay_flag","N")); |
| | | when(businessOpenGateway.open(anyString(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),anyString(),anyString())).thenReturn(new com.talentcard.server.business.payment.huifu.HuifuMerchantBusinessOpenGateway.Result(true,"BO-APPLY","90000000","审核中")); |
| | | when(businessOpenGateway.open(anyString(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),any(),any(),any())).thenReturn(new com.talentcard.server.business.payment.huifu.HuifuMerchantBusinessOpenGateway.Result(true,"BO-APPLY","90000000","审核中")); |
| | | |
| | | // 第一轮:通道侧还没有微信业务 → 只提交业务开通,配置这一步根本不碰。 |
| | | service.refreshOnboarding(principal,false); |
| | |
| | | service.refreshOnboarding(principal,false); |
| | | |
| | | // 业务开通只提交过一次——通道已开通时不再重复提交,避免第二份申请。 |
| | | verify(businessOpenGateway,times(1)).open(anyString(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),anyString(),anyString()); |
| | | verify(businessOpenGateway,times(1)).open(anyString(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),any(),any(),any()); |
| | | verify(wechatConfigGateway,times(1)).configure(anyString(),anyString()); |
| | | assertThat(statusOf("business_open_status",applicationNo)).isEqualTo("approved"); |
| | | assertThat(statusOf("wechat_config_status",applicationNo)).isEqualTo("approved"); |
| | |
| | | String applicationNo=String.valueOf(fixture.get("applicationNo")); |
| | | when(gateway.queryApplication(anyString(),anyString())).thenReturn(Map.of("resp_code","00000000","apply_status","Y","reg_status","S")); |
| | | when(gateway.queryCapabilities(anyString())).thenReturn(Map.of("resp_code","00000000","mer_stat","NORMAL","delay_flag","Y","forced_delay_flag","N")); |
| | | when(businessOpenGateway.open(anyString(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),anyString(),anyString())).thenReturn(new com.talentcard.server.business.payment.huifu.HuifuMerchantBusinessOpenGateway.Result(false,"","00000001","wx_conf_list格式错误")); |
| | | when(businessOpenGateway.open(anyString(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),any(),any(),any())).thenReturn(new com.talentcard.server.business.payment.huifu.HuifuMerchantBusinessOpenGateway.Result(false,"","00000001","wx_conf_list格式错误")); |
| | | |
| | | MerchantPrincipal principal=(MerchantPrincipal)fixture.get("principal"); |
| | | service.refreshOnboarding(principal,false); |
| | |
| | | service.refreshOnboarding(principal,false); |
| | | |
| | | var requests=org.mockito.ArgumentCaptor.forClass(String.class); |
| | | verify(businessOpenGateway,times(2)).open(requests.capture(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),anyString(),anyString()); |
| | | verify(businessOpenGateway,times(2)).open(requests.capture(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),any(),any(),any()); |
| | | assertThat(requests.getAllValues()).doesNotHaveDuplicates(); |
| | | assertThat(requests.getAllValues().get(0)).isEqualTo(applicationNo+"BO"); |
| | | assertThat(statusOf("business_open_request_no",applicationNo)).isEqualTo(requests.getAllValues().get(1)); |
| | | } |
| | | |
| | | /** |
| | | * 门店材料缺一张就不发业务开通请求:通道按 F-code 逐张核对,缺图发出去只会被驳回, |
| | | * 还会在通道侧留下一条缺材料的申请。进件收口成 failed,并明确告诉商户去进件页补哪三张图。 |
| | | * |
| | | * <p>2026-09-29 的真实驳回就是这条口径:线下报备要「门头照 + 内景照 + 收银台」三张, |
| | | * 企业主体原先一张都不交。这里盯住的是「缺图不要发注定被拒的请求」这道闸门,以及它给出的补图提示。 |
| | | */ |
| | | @Test |
| | | void aFailedWechatConfigIsRetriedWithANewRequestNumber() { |
| | | void aMissingStorePhotoStopsTheBusinessOpenBeforeItIsSent() { |
| | | Map<String,Object> fixture=provisioningFixture(); |
| | | String applicationNo=String.valueOf(fixture.get("applicationNo")); |
| | | jdbc.update("update merchant_onboarding_application set store_cashier_url=null where application_no=?",applicationNo); |
| | | when(gateway.queryApplication(anyString(),anyString())).thenReturn(Map.of("resp_code","00000000","apply_status","Y","reg_status","S")); |
| | | when(gateway.queryCapabilities(anyString())).thenReturn(Map.of("resp_code","00000000","mer_stat","NORMAL","delay_flag","Y","forced_delay_flag","N")); |
| | | |
| | | service.refreshOnboarding((MerchantPrincipal)fixture.get("principal"),false); |
| | | |
| | | verify(businessOpenGateway,never()).open(anyString(),anyString(),anyString(),anyString(),anyBoolean(),anyBoolean(),any(),any(),any()); |
| | | assertThat(statusOf("business_open_status",applicationNo)).isEqualTo("failed"); |
| | | assertThat(statusOf("reject_reason",applicationNo)).contains("收银台"); |
| | | } |
| | | |
| | | @Test |
| | | void aFailedWechatConfigIsRetriedWithANewRequestNumber() { Map<String,Object> fixture=provisioningFixture(); |
| | | String applicationNo=String.valueOf(fixture.get("applicationNo")); |
| | | when(gateway.queryApplication(anyString(),anyString())).thenReturn(Map.of("resp_code","00000000","apply_status","Y","reg_status","S")); |
| | | // 通道侧已开通线上场景,配置这一步才有东西可挂(见 advanceWechatConfig 的闸门)。 |
| | |
| | | Long store=jdbc.queryForObject("select id from merchant_store where merchant_id=?",Long.class,merchant); |
| | | jdbc.update("insert into merchant_bank_card(merchant_id,store_id,bank_name,branch_name,city_code,card_tail,card_masked,card_hash,status,idempotency_key) values (?,?,'Bank','Branch','510100','1234','****1234',?,'approved',?)",merchant,store,key,key); |
| | | Long card=jdbc.queryForObject("select id from merchant_bank_card where idempotency_key=?",Long.class,key); |
| | | jdbc.update("insert into merchant_onboarding_application(merchant_id,store_id,application_no,subject_type,subject_name,contact_name,contact_mobile,idempotency_key,status,provider_huifu_id,provider_apply_no,initial_bank_card_id,business_open_status) values (?, ?, ?, 'individual','Owner','Owner','13700000000',?,'pending','6666000241335656','original-apply',?,'failed')",merchant,store,key,key,card); |
| | | jdbc.update("insert into merchant_onboarding_application(merchant_id,store_id,application_no,subject_type,subject_name,contact_name,contact_mobile,idempotency_key,status,provider_huifu_id,provider_apply_no,initial_bank_card_id,business_open_status,store_header_url,store_indoor_url,store_cashier_url) values (?, ?, ?, 'individual','Owner','Owner','13700000000',?,'pending','6666000241335656','original-apply',?,'failed','https://cdn.example.com/store-header.jpg','https://cdn.example.com/store-indoor.jpg','https://cdn.example.com/store-cashier.jpg')",merchant,store,key,key,card); |
| | | return Map.of("principal",new MerchantPrincipal(1L,merchant,store,"owner"),"applicationNo",key); |
| | | } |
| | | |
| | |
| | | private static MerchantOnboardingRequest request(String storeHeaderUrl,String storeIndoorUrl,String settlementCardFrontUrl) { |
| | | return new MerchantOnboardingRequest("individual","Owner",null,null,null,null,null,null,null,null, |
| | | null,null,"510100",null,null,null,null,null,null,null,null,null,null,null,null,null, |
| | | "Owner","13700000000",null,null,"510100",null,settlementCardFrontUrl,null,storeHeaderUrl,storeIndoorUrl,"四川省成都市武侯区某路1号"); |
| | | "Owner","13700000000",null,null,"510100",null,settlementCardFrontUrl,null,storeHeaderUrl,storeIndoorUrl,"四川省成都市武侯区某路1号",null); |
| | | } |
| | | |
| | | /** |
| | |
| | | "https://cdn.example.com/legal-front.jpg","https://cdn.example.com/legal-back.jpg","四川省成都市武侯区某路1号",null,"owner@example.com", |
| | | "测试联系人","13700000001","6222000012345678","510000","510100",clearingCode, |
| | | null,"https://cdn.example.com/registration-account.jpg", |
| | | null,null,null); |
| | | "https://cdn.example.com/store-header.jpg","https://cdn.example.com/store-indoor.jpg",null, |
| | | "https://cdn.example.com/store-cashier.jpg"); |
| | | } |
| | | |
| | | /** 对公主体重提时要补的是开户许可证那一张(请求里走 registrationAccountUrl)。 */ |
| | | private static MerchantOnboardingRequest registrationAccountRequest(String registrationAccountUrl) { |
| | | return new MerchantOnboardingRequest("enterprise","Owner",null,null,null,null,null,null,null,null, |
| | | null,null,"510100",null,null,null,null,null,null,null,null,null,null,null,null,null, |
| | | "Owner","13700000000",null,null,"510100",null,null,registrationAccountUrl,null,null,null); |
| | | "Owner","13700000000",null,null,"510100",null,null,registrationAccountUrl,null,null,null,null); |
| | | } |
| | | |
| | | /** 个人进件的一次完整提交(除门店照片与结算卡卡号面外的必填项也都给上)。 */ |
| | |
| | | "https://cdn.example.com/id-front.jpg","https://cdn.example.com/id-back.jpg","四川省邻水县丰禾镇9组108号","13700000000","owner@example.com", |
| | | "测试负责人","13700000000","6222000012345678","510000","510100",clearingCode, |
| | | "https://cdn.example.com/card-front.jpg",null, |
| | | "https://cdn.example.com/store-header.jpg","https://cdn.example.com/store-indoor.jpg","四川省邻水县丰禾镇9组108号"); |
| | | "https://cdn.example.com/store-header.jpg","https://cdn.example.com/store-indoor.jpg","四川省邻水县丰禾镇9组108号", |
| | | "https://cdn.example.com/store-cashier.jpg"); |
| | | } |
| | | |
| | | /** 建一张还停在 not_started 的进件,用来观察刷新会不会去推进开通步骤。 */ |
| | |
| | | Long store=jdbc.queryForObject("select id from merchant_store where merchant_id=?",Long.class,merchant); |
| | | jdbc.update("insert into merchant_bank_card(merchant_id,store_id,bank_name,branch_name,city_code,card_tail,card_masked,card_hash,status,idempotency_key) values (?,?,'Bank','Branch','510100','1234','****1234',?,'approved',?)",merchant,store,key,key); |
| | | Long card=jdbc.queryForObject("select id from merchant_bank_card where idempotency_key=?",Long.class,key); |
| | | jdbc.update("insert into merchant_onboarding_application(merchant_id,store_id,application_no,subject_type,subject_name,contact_name,contact_mobile,idempotency_key,status,provider_huifu_id,provider_apply_no,initial_bank_card_id) values (?, ?, ?, 'enterprise','Owner','Owner','13700000000',?,'pending','6666000241335656','original-apply',?)",merchant,store,key,key,card); |
| | | jdbc.update("insert into merchant_onboarding_application(merchant_id,store_id,application_no,subject_type,subject_name,contact_name,contact_mobile,idempotency_key,status,provider_huifu_id,provider_apply_no,initial_bank_card_id,store_header_url,store_indoor_url,store_cashier_url) values (?, ?, ?, 'enterprise','Owner','Owner','13700000000',?,'pending','6666000241335656','original-apply',?,'https://cdn.example.com/store-header.jpg','https://cdn.example.com/store-indoor.jpg','https://cdn.example.com/store-cashier.jpg')",merchant,store,key,key,card); |
| | | return Map.of("principal",new MerchantPrincipal(1L,merchant,store,"owner"),"applicationNo",key); |
| | | } |
| | | |