| | |
| | | |
| | | import static org.assertj.core.api.Assertions.assertThat; |
| | | |
| | | import java.util.ArrayList; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | |
| | |
| | | * <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> |
| | |
| | | } |
| | | |
| | | @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 |
| | |
| | | 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"); |
| | |
| | | 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 |
| | |
| | | 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"); |
| | | }); |
| | | } |
| | | |
| | | /** |
| | |
| | | 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); |
| | | } |
| | | } |