| | |
| | | |
| | | public HuifuMerchantBusinessOpenGateway(HuifuPaymentProperties properties) { this.properties = properties; } |
| | | |
| | | public Result open(String requestNo, String huifuId, String shortName, String mcc) { |
| | | public Result open(String requestNo, String huifuId, String shortName, String mcc, boolean individualMerchant) { |
| | | ensureConfigured(); |
| | | Map<String, Object> request = buildRequest(requestNo, huifuId, shortName, mcc, individualMerchant); |
| | | try { |
| | | synchronized (SDK_LOCK) { |
| | | initializeSdk(); |
| | | Map<String, Object> response = BasePayClient.request(request, "v2.merchant.busi.open", false); |
| | | log.info("Huifu merchant business open response: requestNo={}, huifuId={}, response={}", requestNo, huifuId, JSON.toJSONString(response)); |
| | | return Result.from(response); |
| | | } |
| | | } catch (BusinessException exception) { |
| | | throw exception; |
| | | } catch (Exception exception) { |
| | | log.error("Huifu merchant business open request error: requestNo={}, huifuId={}", requestNo, huifuId, exception); |
| | | throw new HuifuPaymentGatewayException("Huifu merchant business open request failed", outcomeUnknown(exception), exception); |
| | | } |
| | | } |
| | | |
| | | /** |
| | | * 组装「商户业务开通」请求体。与发送解耦,便于直接断言字段与层级 |
| | | * (见 {@code HuifuBusinessOpenRequiredFieldsTests})。 |
| | | * |
| | | * <p>{@code individualMerchant} 为真时把 {@code micro_biz_type} 塞进 |
| | | * {@code wx_conf_list} 元素里的 {@code wx_real_name_info}——文档写明个人商户需要填写, |
| | | * 而该字段在文档中属于 wx_real_name_info 子表(父字段列 + 同名子表的写法约定即为嵌套对象)。 |
| | | */ |
| | | static Map<String, Object> buildRequestForTest(HuifuPaymentProperties properties, String requestNo, String huifuId, String shortName, String mcc, boolean individualMerchant) { |
| | | 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"); |
| | | // 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)); |
| | | } |
| | | } |
| | | |
| | | Map<String, Object> media = new LinkedHashMap<>(); |
| | | media.put("media_type", "S3"); |
| | |
| | | request.put("wx_conf_list", JSON.toJSONString(List.of(wechat))); |
| | | 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()); |
| | | request.put("agreement_info", JSON.toJSONString(agreement)); |
| | | // 进件时就把延迟入账能力配好:先付钱后核销的订单必须把钱留在通道「待确认」, |
| | | // 核销确认后才分配给商户。forced_delay_flag 保持关闭——支付即核销的到店消费订单不该被延迟。 |
| | |
| | | if (properties.isOnboardingUnderParentMerchant()) request.put("use_chains_flag", "3"); |
| | | putIfPresent(request, "async_return_url", properties.getMerchantBusinessOpenNotifyUrl()); |
| | | putIfPresent(request, "busi_async_return_url", properties.getMerchantBusinessOpenNotifyUrl()); |
| | | try { |
| | | synchronized (SDK_LOCK) { |
| | | initializeSdk(); |
| | | Map<String, Object> response = BasePayClient.request(request, "v2.merchant.busi.open", false); |
| | | log.info("Huifu merchant business open response: requestNo={}, huifuId={}, response={}", requestNo, huifuId, JSON.toJSONString(response)); |
| | | return Result.from(response); |
| | | } |
| | | } catch (BusinessException exception) { |
| | | throw exception; |
| | | } catch (Exception exception) { |
| | | log.error("Huifu merchant business open request error: requestNo={}, huifuId={}", requestNo, huifuId, exception); |
| | | throw new HuifuPaymentGatewayException("Huifu merchant business open request failed", outcomeUnknown(exception), exception); |
| | | } |
| | | return request; |
| | | } |
| | | |
| | | private void ensureConfigured() { |