跳转到内容

Java 示例

此示例基于 Spring RestTemplate,包含签名、POST JSONGET Query,以及代收下单、代付下单、商户信息查询三个接口。

java
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Instant;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;

public class SkynetClient {
    private final String baseUrl;
    private final int mchId;
    private final String apiToken;
    private final RestTemplate restTemplate = new RestTemplate();

    public SkynetClient(String baseUrl, int mchId, String apiToken) {
        this.baseUrl = baseUrl;
        this.mchId = mchId;
        this.apiToken = apiToken;
    }

    public Map<String, Object> createCollectOrder() {
        Map<String, Object> params = new HashMap<>();
        params.put("trans_id", "ORDER-" + Instant.now().getEpochSecond());
        params.put("currency", "VND");
        params.put("amount", "100.00");
        params.put("channel", "bank");
        params.put("callback_url", "https://merchant.example.com/callback/collect");
        params.put("return_url", "https://merchant.example.com/payment/result");
        params.put("remarks", "collect demo");

        return post("/api/v1/mch/pmt-orders", params);
    }

    public Map<String, Object> createPayoutOrder() {
        Map<String, Object> params = new HashMap<>();
        params.put("trans_id", "WDL-" + Instant.now().getEpochSecond());
        params.put("channel", "bank");
        params.put("amount", "100.00");
        params.put("currency", "VND");
        params.put("account_no", "2333667799212341");
        params.put("account_name", "NGUYEN XUAN HUNG");
        params.put("account_org", "PVCOMBANK");
        params.put("account_org_code", "970412");
        params.put("callback_url", "https://merchant.example.com/callback/payout");
        params.put("remarks", "payout demo");

        return post("/api/v1/mch/wdl-orders", params);
    }

    public Map<String, Object> getMerchantInfo() {
        return get("/api/v1/mch/info", new HashMap<>());
    }

    private Map<String, Object> post(String path, Map<String, Object> params) {
        Map<String, Object> signedParams = withSignature(params);
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);

        ResponseEntity<Map> response = restTemplate.postForEntity(
            baseUrl + path,
            new HttpEntity<>(signedParams, headers),
            Map.class
        );

        return parseResponse(response);
    }

    private Map<String, Object> get(String path, Map<String, Object> params) {
        Map<String, Object> signedParams = withSignature(params);
        UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(baseUrl + path);
        signedParams.forEach(builder::queryParam);

        ResponseEntity<Map> response = restTemplate.exchange(
            builder.toUriString(),
            HttpMethod.GET,
            HttpEntity.EMPTY,
            Map.class
        );

        return parseResponse(response);
    }

    private Map<String, Object> withSignature(Map<String, Object> params) {
        Map<String, Object> signedParams = new HashMap<>(params);
        signedParams.put("mch_id", mchId);
        signedParams.put("nonce", UUID.randomUUID().toString().replace("-", "").substring(0, 12));
        signedParams.put("timestamp", Instant.now().getEpochSecond());
        signedParams.put("sign", sign(signedParams));

        return signedParams;
    }

    private String sign(Map<String, Object> params) {
        List<String> keys = new ArrayList<>(params.keySet());
        keys.sort(String::compareTo);

        StringBuilder source = new StringBuilder(apiToken);
        for (String key : keys) {
            Object value = params.get(key);
            if ("sign".equals(key) || value == null || value.toString().isEmpty()) {
                continue;
            }
            source.append("&").append(key).append("=").append(value);
        }

        return md5(source.toString());
    }

    private String md5(String input) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
            StringBuilder hex = new StringBuilder();
            for (byte b : digest) {
                hex.append(String.format("%02x", b));
            }
            return hex.toString();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private Map<String, Object> parseResponse(ResponseEntity<Map> response) {
        if (!response.getStatusCode().is2xxSuccessful()) {
            throw new RuntimeException("HTTP request failed: " + response.getStatusCode());
        }

        Map<String, Object> body = response.getBody();
        if (body == null || !Objects.equals(body.get("code"), 200)) {
            throw new RuntimeException("API request failed: " + body);
        }

        Object payload = body.get("payload");
        return payload instanceof Map ? (Map<String, Object>) payload : new HashMap<>();
    }

    public static void main(String[] args) {
        SkynetClient client = new SkynetClient(
            "https://api.example.com",
            10001,
            "demo_key_123456"
        );

        Map<String, Object> collectOrder = client.createCollectOrder();
        Map<String, Object> payoutOrder = client.createPayoutOrder();
        Map<String, Object> merchantInfo = client.getMerchantInfo();
    }
}

Released under the MIT License.