liujie
2026-07-30 fc767bf18e262b724c78b5f860b6d908d4993dcb
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package com.tourguide.common.huifu.biz;
 
import com.alibaba.fastjson.JSONObject;
import com.tourguide.common.huifu.properties.HuiFuProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.*;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
 
import java.math.BigDecimal;
import java.security.KeyFactory;
import java.security.PrivateKey;
import java.security.Signature;
import java.security.spec.PKCS8EncodedKeySpec;
import java.text.SimpleDateFormat;
import java.util.Base64;
import java.util.Date;
 
/**
 * 斗拱主动提现业务。
 *
 * 备注:该类只向斗拱发起提现,导游余额扣减、提现单状态变更应在本项目业务服务中幂等完成。
 */
@Component
public class DouGongWithdrawBiz {
    private static final Logger log = LoggerFactory.getLogger(DouGongWithdrawBiz.class);
 
    @Autowired
    private HuiFuProperties huiFuProperties;
    @Autowired
    private RestTemplate restTemplate;
 
    private static final String ENCASHMENT_URL = "https://api.huifu.com/v2/trade/settlement/encashment";
    /**
     * 主动取现接口。
     *
     * @param cashAmt 取现金额,单位元
     * @param huifuId 取现方汇付 ID
     * @param tokenNo 绑定取现卡后返回的取现卡序列号
     * @return 斗拱响应结果
     */
    public JSONObject encashment(BigDecimal cashAmt, String huifuId, String tokenNo) {
        try {
            log.info("开始调用斗拱提现接口,取现方ID: {}, 取现金额: {}", huifuId, cashAmt);
 
            JSONObject data = new JSONObject();
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
            String reqDate = dateFormat.format(new Date());
 
            data.put("req_date", reqDate);
            data.put("req_seq_id", DouGongRequestSupport.generateForHuiFu("TX"));
            data.put("cash_amt", cashAmt.toString());
            data.put("huifu_id", huifuId);
            // 备注:提现到账周期先固定 D1,后续如果后台提供提现规则,再从配置或提现申请中读取。
            data.put("into_acct_date_type", "D1");
            data.put("token_no", tokenNo);
            data.put("notify_url", huiFuProperties.getCallbackPath() + "/base/dougong/encashment");
 
            String dataStr = data.toJSONString();
            log.info("业务参数data: {}", dataStr);
 
            JSONObject requestJson = new JSONObject();
            requestJson.put("sys_id", huiFuProperties.getSysId());
            requestJson.put("product_id", huiFuProperties.getProductId());
            requestJson.put("data", dataStr);
 
            // 备注:主动提现接口按完整报文签名,不复用支付创建接口只签 data 的逻辑。
            String signData = requestJson.toJSONString();
            log.info("待签名报文: {}", signData);
 
            String signature = sign(signData, huiFuProperties.getRsaPrivateKey());
            if (signature == null) {
                log.error("签名生成失败");
                return createErrorResult("签名生成失败");
            }
 
            requestJson.put("sign", signature);
 
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
 
            HttpEntity<String> requestEntity = new HttpEntity<>(requestJson.toJSONString(), headers);
 
            log.info("发送斗拱提现请求到: {}", ENCASHMENT_URL);
            ResponseEntity<String> response = restTemplate.exchange(
                ENCASHMENT_URL,
                HttpMethod.POST,
                requestEntity,
                String.class
            );
 
            log.info("斗拱提现接口响应状态: {}, 响应内容: {}", response.getStatusCode(), response.getBody());
 
            // 解析响应
            JSONObject result = JSONObject.parseObject(response.getBody());
            return result;
 
        } catch (Exception e) {
            log.error("调用斗拱提现接口异常: {}", e.getMessage(), e);
            return createErrorResult("调用接口异常: " + e.getMessage());
        }
    }
 
 
    private JSONObject createErrorResult(String errorMsg) {
        JSONObject result = new JSONObject();
        result.put("code", "9999");
        result.put("msg", errorMsg);
        result.put("success", false);
        return result;
    }
    public static String sign(String data, String privateKeyBase64) {
        // Base64 --> Key
        try {
            byte[] bytes = Base64.getDecoder().decode(privateKeyBase64);
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(bytes);
            KeyFactory keyFactory;
            keyFactory = KeyFactory.getInstance("RSA");
            PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
            // Sign
            Signature signature = Signature.getInstance("SHA256WithRSA");
            signature.initSign(privateKey);
            signature.update(data.getBytes("UTF-8"));
            return Base64.getEncoder().encodeToString(signature.sign());
        } catch (Exception e) {
            return null;
        }
    }
}