| | |
| | | import com.huifu.bspay.sdk.opps.client.BasePayClient; |
| | | import com.huifu.bspay.sdk.opps.core.BasePay; |
| | | import com.huifu.bspay.sdk.opps.core.config.MerConfig; |
| | | import com.huifu.bspay.sdk.opps.core.request.V2UserBasicdataEntRequest; |
| | | import com.talentcard.server.common.BusinessException; |
| | | import com.talentcard.server.common.ErrorCode; |
| | | import java.time.LocalDate; |
| | | import java.time.format.DateTimeFormatter; |
| | | import java.util.LinkedHashMap; |
| | | import java.util.List; |
| | | import java.util.Map; |
| | | import org.slf4j.Logger; |
| | | import org.slf4j.LoggerFactory; |
| | | import org.springframework.stereotype.Service; |
| | | |
| | | @Service |
| | | public class HuifuEnterpriseOnboardingGateway { |
| | | private static final Object SDK_LOCK = new Object(); |
| | | private static final DateTimeFormatter DATE = DateTimeFormatter.ofPattern("yyyyMMdd"); |
| | | private static final Logger log = LoggerFactory.getLogger(HuifuEnterpriseOnboardingGateway.class); |
| | | private final HuifuPaymentProperties properties; |
| | | |
| | | public HuifuEnterpriseOnboardingGateway(HuifuPaymentProperties properties) { this.properties = properties; } |
| | | |
| | | public Result create(HuifuEnterpriseOnboardingCommand command) { |
| | | ensureConfigured(); |
| | | V2UserBasicdataEntRequest request = new V2UserBasicdataEntRequest(); |
| | | request.setReqSeqId(command.applicationNo()); |
| | | request.setReqDate(DATE.format(LocalDate.now())); |
| | | request.setRegName(command.companyName()); |
| | | request.setLicenseCode(command.creditCode()); |
| | | request.setLicenseValidityType(command.licenseValidityType()); |
| | | request.setLicenseBeginDate(command.licenseBeginDate()); |
| | | if (!blank(command.licenseEndDate())) request.setLicenseEndDate(command.licenseEndDate()); |
| | | request.setRegProvId(command.registeredProvinceCode()); |
| | | request.setRegAreaId(command.registeredCityCode()); |
| | | request.setRegDistrictId(command.registeredDistrictCode()); |
| | | request.setRegDetail(command.registeredAddress()); |
| | | request.setLegalName(command.legalName()); |
| | | request.setLegalCertType("00"); |
| | | request.setLegalCertNo(command.legalIdNumber()); |
| | | request.setLegalCertValidityType(command.legalValidityType()); |
| | | request.setLegalCertBeginDate(command.legalBeginDate()); |
| | | if (!blank(command.legalEndDate())) request.setLegalCertEndDate(command.legalEndDate()); |
| | | request.setLegalCertNationality("CHN"); |
| | | request.setContactName(command.contactName()); |
| | | request.setContactMobile(command.contactMobile()); |
| | | Map<String, Object> card = new LinkedHashMap<>(); |
| | | card.put("branch_code", command.bankBranchCode()); |
| | | card.put("branch_name", command.bankBranchName()); |
| | | card.put("card_no", command.cardNumber()); |
| | | card.put("card_name", command.cardName()); |
| | | card.put("card_type", command.cardType()); |
| | | card.put("default_cash_flag", "1"); |
| | | putIfPresent(card, "prov_id", command.bankProvinceCode()); |
| | | putIfPresent(card, "area_id", command.bankCityCode()); |
| | | putIfPresent(card, "bank_code", command.bankCode()); |
| | | if ("1".equals(command.cardType()) || "2".equals(command.cardType())) { |
| | | card.put("cert_type", "00"); |
| | | card.put("cert_no", command.cardCertificateNumber()); |
| | | card.put("cert_validity_type", command.cardCertificateValidityType()); |
| | | card.put("cert_begin_date", command.cardCertificateBeginDate()); |
| | | putIfPresent(card, "cert_end_date", command.cardCertificateEndDate()); |
| | | putIfPresent(card, "mp", command.cardMobile()); |
| | | } |
| | | |
| | | Map<String, Object> cash = new LinkedHashMap<>(); |
| | | cash.put("cash_type", "DM"); |
| | | cash.put("fix_amt", "0.00"); |
| | | cash.put("fee_rate", ""); |
| | | |
| | | Map<String, Object> request = new LinkedHashMap<>(); |
| | | request.put("req_seq_id", command.applicationNo()); |
| | | request.put("req_date", LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE)); |
| | | request.put("upper_huifu_id", properties.getOnboardingUpperHuifuId().trim()); |
| | | request.put("reg_name", command.companyName()); |
| | | request.put("short_name", command.shortName()); |
| | | request.put("receipt_name", command.shortName()); |
| | | request.put("ent_type", command.entType()); |
| | | request.put("mcc", command.mcc()); |
| | | request.put("busi_type", "1"); |
| | | request.put("scene_type", "ONLINE"); |
| | | request.put("license_pic", command.licensePictureId()); |
| | | request.put("license_code", command.creditCode()); |
| | | request.put("license_validity_type", command.licenseValidityType()); |
| | | request.put("license_begin_date", command.licenseBeginDate()); |
| | | putIfPresent(request, "license_end_date", command.licenseEndDate()); |
| | | request.put("found_date", command.foundDate()); |
| | | putIfPresent(request, "reg_capital", command.registeredCapital()); |
| | | request.put("reg_prov_id", command.registeredProvinceCode()); |
| | | request.put("reg_area_id", command.registeredCityCode()); |
| | | request.put("reg_district_id", command.registeredDistrictCode()); |
| | | request.put("reg_detail", command.registeredAddress()); |
| | | request.put("prov_id", command.registeredProvinceCode()); |
| | | request.put("area_id", command.registeredCityCode()); |
| | | request.put("district_id", command.registeredDistrictCode()); |
| | | request.put("detail_addr", command.registeredAddress()); |
| | | request.put("legal_name", command.legalName()); |
| | | request.put("legal_cert_type", "00"); |
| | | request.put("legal_cert_no", command.legalIdNumber()); |
| | | request.put("legal_cert_validity_type", command.legalValidityType()); |
| | | request.put("legal_cert_begin_date", command.legalBeginDate()); |
| | | putIfPresent(request, "legal_cert_end_date", command.legalEndDate()); |
| | | request.put("legal_addr", command.legalAddress()); |
| | | request.put("legal_cert_front_pic", command.legalFrontPictureId()); |
| | | request.put("legal_cert_back_pic", command.legalBackPictureId()); |
| | | request.put("contact_name", command.contactName()); |
| | | request.put("contact_mobile_no", command.contactMobile()); |
| | | request.put("contact_email", command.contactEmail()); |
| | | request.put("login_name", loginName(command.applicationNo())); |
| | | request.put("card_info", JSON.toJSONString(card)); |
| | | putIfPresent(request, "reg_acct_pic", command.registrationAccountPictureId()); |
| | | putIfPresent(request, "settle_card_front_pic", command.settlementCardFrontPictureId()); |
| | | putIfPresent(request, "settle_cert_front_pic", command.settlementCertificateFrontPictureId()); |
| | | putIfPresent(request, "settle_cert_back_pic", command.settlementCertificateBackPictureId()); |
| | | request.put("cash_config", JSON.toJSONString(List.of(cash))); |
| | | putIfPresent(request, "async_return_url", properties.getMerchantOnboardingNotifyUrl()); |
| | | try { |
| | | Map<String, Object> response; |
| | | synchronized (SDK_LOCK) { |
| | | initializeSdk(); |
| | | response = BasePayClient.request(request, false); |
| | | Map<String, Object> response = BasePayClient.request(request, "v2.merchant.basicdata.ent", false); |
| | | log.info("Huifu enterprise merchant onboarding response: applicationNo={}, upperHuifuId={}, response={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), JSON.toJSONString(response)); |
| | | return Result.from(response); |
| | | } |
| | | return Result.from(response); |
| | | } catch (BusinessException exception) { |
| | | throw exception; |
| | | } catch (Exception exception) { |
| | | throw new HuifuPaymentGatewayException("Huifu enterprise onboarding request failed", outcomeUnknown(exception), exception); |
| | | log.error("Huifu enterprise merchant onboarding request error: applicationNo={}, upperHuifuId={}", command.applicationNo(), properties.getOnboardingUpperHuifuId(), exception); |
| | | throw new HuifuPaymentGatewayException("Huifu enterprise merchant onboarding request failed", outcomeUnknown(exception), exception); |
| | | } |
| | | } |
| | | |
| | | private void ensureConfigured() { |
| | | if (!properties.isEnabled()) throw new BusinessException(ErrorCode.CONFLICT, "Huifu payment is disabled"); |
| | | if (blank(properties.getSystemId()) || blank(properties.getProductId()) || blank(properties.getPrivateKey()) || blank(properties.getPlatformPublicKey())) { |
| | | throw new BusinessException(ErrorCode.CONFLICT, "Huifu onboarding configuration is incomplete"); |
| | | } |
| | | if (blank(properties.getSystemId()) || blank(properties.getProductId()) || blank(properties.getOnboardingUpperHuifuId()) || blank(properties.getPrivateKey()) || blank(properties.getPlatformPublicKey())) throw new BusinessException(ErrorCode.CONFLICT, "Huifu merchant onboarding configuration is incomplete"); |
| | | } |
| | | |
| | | private void initializeSdk() throws Exception { |
| | |
| | | BasePay.initWithMerConfig(config); |
| | | } |
| | | |
| | | private boolean blank(String value) { return value == null || value.isBlank(); } |
| | | private static String loginName(String applicationNo) { return ("tc" + applicationNo).replaceAll("[^0-9A-Za-z_]", ""); } |
| | | private static void putIfPresent(Map<String, Object> request, String key, String value) { if (!blank(value)) request.put(key, value.trim()); } |
| | | private static boolean blank(String value) { return value == null || value.isBlank(); } |
| | | private boolean outcomeUnknown(Exception exception) { String message = exception.getMessage() == null ? "" : exception.getMessage().toLowerCase(); return message.contains("timeout") || message.contains("timed out") || message.contains("no response"); } |
| | | |
| | | @SuppressWarnings("unchecked") |
| | | public record Result(boolean accepted, String huifuId, String code, String message) { |
| | | public record Result(boolean accepted, String huifuId, String applyNo, String tokenNo, String code, String message) { |
| | | static Result from(Map<String, Object> response) { |
| | | Object raw = response.get("data"); |
| | | Map<String, Object> data = raw instanceof Map<?, ?> value ? (Map<String, Object>) value : raw instanceof String json && !json.isBlank() ? JSON.parseObject(json, Map.class) : response; |
| | | String code = value(data, "sub_resp_code", "resp_code", "code"); |
| | | String message = value(data, "sub_resp_desc", "resp_desc", "message"); |
| | | String huifuId = value(data, "huifu_id", "huifuId"); |
| | | return new Result("00000000".equals(code) || "10000".equals(code) || "0".equals(code) || "SUCCESS".equalsIgnoreCase(code), huifuId, code, message); |
| | | return new Result(success(code), value(data, "huifu_id", "huifuId"), value(data, "apply_no", "applyNo"), value(data, "token_no", "tokenNo"), 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); } |
| | | 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 ""; } |
| | | } |
| | | } |