KKSU
2025-02-07 6539b325b5ce95d1fafa864c75a32279c954167f
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
package cc.mrbird.febs.pay.util;
 
import cc.mrbird.febs.pay.model.RefundStatus;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Service;
 
import java.util.concurrent.*;
 
@Service(value="FiuuRefundUtil")
public class FiuuRefundUtil {
 
    private static final String API_BASE_URL = "https://api.fiuu.com/RMS/API/refundAPI/";
    private static final String MERCHANT_ID = "e2umart01";
    private static final String VERIFY_KEY = "4e3a4ed58e62ddbfacf41f6d5ec56bf2";
    private static final int MAX_RETRIES = 3;
    private static final int POLL_INTERVAL = 1000; // 5秒
    private static final int TIMEOUT = 60000; // 60秒超时
 
    private final ObjectMapper objectMapper = new ObjectMapper();
 
    // 退款状态查询(根据TxnID)
    public RefundStatus queryByTxnId(String txnId) throws Exception {
        String signature = HashUtils.md5(txnId + MERCHANT_ID + VERIFY_KEY);
        String url = API_BASE_URL + "q_by_txn.php?TxnID=" + txnId
                + "&MerchantID=" + MERCHANT_ID
                + "&Signature=" + signature;
 
        return executeQuery(url);
    }
 
    // 退款状态查询(根据RefID)
    public RefundStatus queryByRefId(String refId) throws Exception {
        String signature = HashUtils.md5(refId + MERCHANT_ID + VERIFY_KEY);
        String url = API_BASE_URL + "q_by_refID.php?RefID=" + refId
                + "&MerchantID=" + MERCHANT_ID
                + "&Signature=" + signature;
 
        return executeQuery(url);
    }
 
    private RefundStatus executeQuery(String url) throws Exception {
        HttpGet request = new HttpGet(url);
        try (CloseableHttpClient client = HttpClients.createDefault()) {
            String response = EntityUtils.toString(client.execute(request).getEntity());
            return objectMapper.readValue(response, RefundStatus.class);
        }
    }
 
    // 异步轮询退款状态
    public RefundStatus pollRefundStatus(String txnId) throws Exception {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<RefundStatus> future = executor.submit(() -> {
            int retryCount = 0;
            while (retryCount < MAX_RETRIES) {
                try {
                    RefundStatus status = queryByTxnId(txnId);
                    if (!"pending".equals(status.getStatus())) {
                        return status;
                    }
                    Thread.sleep(POLL_INTERVAL);
                    retryCount++;
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("Polling interrupted", e);
                }
            }
            throw new TimeoutException("Max retries reached");
        });
 
        try {
            return future.get(TIMEOUT, TimeUnit.MILLISECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            throw new RuntimeException("Refund status check timeout");
        } finally {
            executor.shutdown();
        }
    }
}