package com.talentcard.server.business.payment.huifu;
|
|
import com.alibaba.fastjson.JSON;
|
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.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.Map;
|
import org.slf4j.Logger;
|
import org.slf4j.LoggerFactory;
|
import org.springframework.stereotype.Service;
|
|
@Service
|
public class HuifuMerchantWechatConfigGateway {
|
private static final Object SDK_LOCK = new Object();
|
private static final Logger log = LoggerFactory.getLogger(HuifuMerchantWechatConfigGateway.class);
|
private final HuifuPaymentProperties properties;
|
|
public HuifuMerchantWechatConfigGateway(HuifuPaymentProperties properties) { this.properties = properties; }
|
|
public Result configure(String requestNo, String huifuId) {
|
ensureConfigured();
|
Map<String, Object> request = new LinkedHashMap<>();
|
request.put("req_seq_id", requestNo);
|
request.put("req_date", LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE));
|
request.put("huifu_id", huifuId);
|
// 业务开通类型:线下 02(线上 01)。AppID 必须绑在该类型下,否则阶段判定读的是另一组配置项、
|
// 永远看不到绑定结果——2026-09-24 之前线上口径写死 01,改成线下后这里不跟着改就会出现这个问题。
|
request.put("fee_type", properties.getMerchantWechatFeeType());
|
// 文档《微信商户配置》的请求参数表里 product_id 是必填(示例值 JCTEST)。
|
// 它与公共参数里的 product_id(SDK 自动带)不是同一处:这一处在 data 里,必须自己传。
|
request.put("product_id", properties.getProductId().trim());
|
request.put("wx_applet_app_id", properties.getMiniProgramAppId().trim());
|
putIfPresent(request, "async_return_url", properties.getMerchantWechatConfigNotifyUrl());
|
try {
|
synchronized (SDK_LOCK) {
|
initializeSdk();
|
Map<String, Object> response = BasePayClient.request(request, "v2.merchant.busi.config", false);
|
log.info("Huifu merchant WeChat config 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 WeChat config request error: requestNo={}, huifuId={}", requestNo, huifuId, exception);
|
throw new HuifuPaymentGatewayException("Huifu merchant WeChat config 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.getMiniProgramAppId()) || blank(properties.getPrivateKey()) || blank(properties.getPlatformPublicKey())) throw new BusinessException(ErrorCode.CONFLICT, "Huifu merchant WeChat configuration is incomplete");
|
}
|
|
private void initializeSdk() throws Exception {
|
BasePay.debug = false;
|
BasePay.prodMode = "prod".equalsIgnoreCase(properties.getEnvironment()) ? BasePay.MODE_PROD : BasePay.MODE_TEST;
|
MerConfig config = new MerConfig();
|
config.setSysId(properties.getSystemId().trim());
|
config.setProductId(properties.getProductId().trim());
|
config.setRsaPrivateKey(properties.getPrivateKey().trim());
|
config.setRsaPublicKey(properties.getPlatformPublicKey().trim());
|
BasePay.initWithMerConfig(config);
|
}
|
|
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 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");
|
return new Result(success(code), code, value(data, "sub_resp_desc", "resp_desc", "message"));
|
}
|
private static boolean success(String code) { return "00000000".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 ""; }
|
}
|
}
|