shinnosuke
2026-08-03 98d1455efaa4cd3d5d3213e047ccc0ac187ba05c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
package com.talentcard.server.business.payment.huifu;
 
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.V2TradeSettlementEncashmentRequest;
import com.talentcard.server.common.BusinessException;
import com.talentcard.server.common.ErrorCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.Map;
 
@Service
public class HuifuWithdrawalGateway {
    private static final Object SDK_LOCK = new Object();
    private static final Logger log = LoggerFactory.getLogger(HuifuWithdrawalGateway.class);
    private final HuifuPaymentProperties properties;
    public HuifuWithdrawalGateway(HuifuPaymentProperties properties) { this.properties = properties; }
 
    public Map<String, Object> create(HuifuWithdrawalCommand command) {
        ensureConfigured();
        V2TradeSettlementEncashmentRequest request = new V2TradeSettlementEncashmentRequest();
        request.setReqDate(LocalDate.now().format(DateTimeFormatter.BASIC_ISO_DATE));
        request.setReqSeqId(command.withdrawalNo());
        request.setCashAmt(BigDecimal.valueOf(command.amountCents(), 2).toPlainString());
        request.setHuifuId(command.huifuId());
        request.setIntoAcctDateType(properties.getWithdrawalIntoAccountDateType().trim());
        request.setTokenNo(command.tokenNo());
        Map<String, Object> extension = new LinkedHashMap<>();
        if (!blank(properties.getWithdrawalNotifyUrl())) extension.put("notify_url", properties.getWithdrawalNotifyUrl().trim());
        request.setExtendInfo(extension);
        try {
            synchronized (SDK_LOCK) {
                initializeSdk();
                return BasePayClient.request(request, false);
            }
        } catch (BusinessException exception) { throw exception; }
        catch (Exception exception) {
            log.warn("Huifu withdrawal request failed: type={}, message={}", exception.getClass().getSimpleName(), safe(exception.getMessage()));
            throw new HuifuPaymentGatewayException("Huifu withdrawal request failed", unknown(exception), exception);
        }
    }
    private void ensureConfigured() {
        if (!properties.isEnabled()) throw new BusinessException(ErrorCode.CONFLICT, "Huifu payment is disabled");
        if (blank(properties.getSystemId()) || blank(properties.getWithdrawalProductId()) || blank(properties.getPrivateKey()) || blank(properties.getPlatformPublicKey())) throw new BusinessException(ErrorCode.CONFLICT, "Huifu withdrawal configuration is incomplete");
        if (!"D0".equals(properties.getWithdrawalIntoAccountDateType()) && !"T1".equals(properties.getWithdrawalIntoAccountDateType()) && !"D1".equals(properties.getWithdrawalIntoAccountDateType()) && !"DM".equals(properties.getWithdrawalIntoAccountDateType())) throw new BusinessException(ErrorCode.BAD_REQUEST, "Invalid Huifu withdrawal arrival date type");
    }
    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.getWithdrawalProductId().trim()); config.setRsaPrivateKey(properties.getPrivateKey().trim()); config.setRsaPublicKey(properties.getPlatformPublicKey().trim()); BasePay.initWithMerConfig(config); }
    private boolean blank(String value) { return value == null || value.isBlank(); }
    private boolean unknown(Exception exception) { String value = safe(exception.getMessage()).toLowerCase(); return value.contains("timeout") || value.contains("no response"); }
    private String safe(String value) { return value == null ? "" : value.substring(0, Math.min(value.length(), 300)); }
}