跳到主要内容

API 签名验证

本文介绍如何使用 API 签名和验签机制来确保 API 请求的安全性和完整性。

1. 获取 API Key 和 API Secret

参考商户接入文档,获取 API Key 和 API Secret。

2. RESTful 请求签名

2.1 请求标头

所有 RESTful 请求都需要在标头中包含以下参数:

  • X-PAY-KEY: 字符串类型的 API Key。
  • X-PAY-SIGN: 使用 HMAC-SHA256 算法生成的哈希值,再使用 Base64 编码后的字符串。
  • X-PAY-TIMESTAMP: 请求的 Unix 时间戳,单位为秒,字符串类型,如 '1684304935'。为了防止重放攻击,时间戳与服务器时间的差值不能超过 1 分钟。
信息

注:所有的 POST 请求还需要在标头中包含 Content-Type: application/json 参数,并且保证请求体是有效的 JSON。

2.2 签名生成

X-PAY-SIGN 的请求标头是由 API Secret 和请求的参数生成的。生成签名的步骤如下:

  1. 将请求的时间戳 timestamp、请求方法 method(GET、POST)、请求路径 requestPath(不包含域名)和请求体 body 拼接成字符串timestamp + method + requestPath + body
  2. 使用 HMAC-SHA256 算法,使用 API Secret 作为密钥,对拼接的字符串进行哈希。
  3. 将哈希值使用 Base64 编码,得到 X-PAY-SIGN 的值。

以下是一个使用 JavaScript 生成签名的示例:

import crypto from 'crypto';
const timestamp = '1684304935';
const sign = crypto.createHmac('sha256', apiSecret)
.update(timestamp + 'GET' + '/api/mer/conf/list/currency?chainId=101')
.digest('base64');

其中,

  • apiSecret 是 API Secret;
  • timestamp 是请求的 Unix 时间戳,单位为秒,与 X-PAY-TIMESTAMP 的值相同;
  • GET 是请求方法 method
  • /api/mer/conf/list/currency?chainId=101 是请求路径 requestPath,即 URL 中的 pathname + search(对于 GET 请求,包含了查询参数)部分;
  • body 是请求体,对于 GET 请求,为空。对于 POST 请求,是请求体的 JSON 字符串,比如 {"chainId":101,"description": "some products","isLegalTender": 1,"notifyUrl":"https://some-notify-url.com","outTradeNo":"12345","quoteAmount":"11.22","quoteCurrencySymbol":"USD"}
  • 得到的 sign 即为 X-PAY-SIGN 的值。

2.3 签名示例

public void getSignature(String requestPath, String body, String method) throws NoSuchAlgorithmException, InvalidKeyException {
String apiUrl = 'API Request Domain';
String apiKey = "your-api-key";
String secret = "your-api-secret";
// 组装签名内容
StringBuilder sign = new StringBuilder();
// 时间戳
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
sign.append(timestamp);
// 请求方法
sign.append(method.toUpperCase());
// 请求路径
sign.append(requestPath);
// 请求体为json字符串
sign.append(body);
// HMAC SHA256加密
Mac sha256Hmac = Mac.getInstance("HmacSHA256");
SecretKeySpec secretKeySpec = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256");
sha256Hmac.init(secretKeySpec);
byte[] hmacBytes = sha256Hmac.doFinal(sign.toString().getBytes(StandardCharsets.UTF_8));

String apiSign = Base64.getEncoder().encodeToString(hmacBytes);

// 发送请求
RestTemplate restTemplate = new RestTemplate();
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("X-PAY-KEY", apiKey);
headers.set("X-PAY-SIGN", apiSign);
headers.set("X-PAY-TIMESTAMP", timestamp);

HttpEntity<String> requestEntity = new HttpEntity<>(body, headers);

// 请求地址
String url = apiUrl + requestPath;

// 发送请求并获取响应(此处为post请求)
ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);
}

3. 请求测试

在每个 API 参考页面,我们都提供了请求试用面板,可以对接口进行测试:

请求试用面板

在试用面板中,您必须输入 API Key 和 API Secret,然后填入请求参数,面板会自动生成签名所需的三个请求标头。点击 “SEND API REQUEST” 按钮,即可发送请求并在下方查看响应。

提示

测试时可以选择 BaseURL,以便在不同环境中测试 API。沙盒环境和生产环境是两套独立的系统,使用时请注意区分。您在沙盒环境注册的商户无法在生产环境使用,反之亦然。