ZackPay API 认证文档
About 1774 wordsAbout 6 min
2026-01-08
1. 概述
ZackPay API 使用基于 RSA 的签名认证机制,确保 API 请求的安全性和完整性。本文档将详细介绍如何接入 ZackPay API 认证体系。
2. 认证流程
- 商户在 ZackPay 控制台生成 RSA 密钥对
- 商户保存私钥,将公钥上传至 ZackPay 控制台
- 商户发起 API 请求时,使用私钥对请求内容进行签名
- ZackPay 服务器使用商户公钥验证签名
- 验证通过后,处理请求并返回结果
3. 密钥生成与管理
3.1 密钥生成
商户可以通过以下方式生成 RSA 密钥对:
通过 ZackPay 控制台生成(推荐)
- 登录 ZackPay 商户控制台
- 进入 API 设置页面
- 点击「生成密钥对」按钮
- 保存私钥,公钥将自动上传
使用工具生成
# 使用 openssl 生成 2048 位 RSA 密钥对 openssl genrsa -out private_key.pem 2048 openssl rsa -in private_key.pem -pubout -out public_key.pem
3.2 密钥格式
- 公钥:X509 格式,Base64 编码
- 私钥:PKCS#8 格式,Base64 编码
3.3 密钥安全
- 私钥必须严格保密,不得泄露给第三方
- 定期更换密钥(建议每 6 个月更换一次)
- 更换密钥时,需在控制台先添加新公钥,再删除旧公钥
4. 请求签名规则
4.1 签名参数
| 参数名 | 类型 | 说明 | 是否必填 |
|---|---|---|---|
| X-Merchant-Id | String | 商户 ID | 是 |
| X-Timestamp | String | 时间戳(秒级) | 是 |
| X-Nonce | String | 随机字符串(32 位) | 是 |
| X-Sign | String | 签名结果 | 是 |
4.2 签名生成步骤
构建待签名字符串
- 收集请求中的所有参数(包括 URL 参数和请求体参数)
- 过滤掉值为 null 或空字符串的参数
- 按参数名的字典序排序
- 用
key=value格式拼接,参数间用&连接 - 示例:
key1=value1&key2=value2&key3=value3
添加认证参数
- 将
X-Merchant-Id、X-Timestamp、X-Nonce三个参数添加到待签名字符串中 - 同样按字典序排序
- 将
生成签名
- 使用 RSA 私钥对上述待签名字符串进行签名
- 签名算法:SHA256withRSA
- 签名结果使用 Base64 编码
添加请求头
- 将
X-Merchant-Id、X-Timestamp、X-Nonce、X-Sign添加到请求头中
- 将
4.3 签名示例
// 1. 构建请求参数
Map<String, Object> params = new HashMap<>();
params.put("orderId", "123456789");
params.put("amount", "100.00");
params.put("currency", "INR");
// 2. 添加认证参数
params.put("X-Merchant-Id", "123456");
params.put("X-Timestamp", "1635734400");
params.put("X-Nonce", "random_string_123456");
// 3. 按字典序排序并拼接
String signContent = "X-Merchant-Id=123456&X-Nonce=random_string_123456&X-Timestamp=1635734400&amount=100.00¤cy=INR&orderId=123456789";
// 4. 生成签名
String sign = RsaUtil.sign(signContent, privateKey);
// 5. 添加到请求头
request.addHeader("X-Merchant-Id", "123456");
request.addHeader("X-Timestamp", "1635734400");
request.addHeader("X-Nonce", "random_string_123456");
request.addHeader("X-Sign", sign);5. 签名验证
ZackPay 服务器接收到请求后,会按照以下步骤验证签名:
- 从请求头中获取
X-Merchant-Id、X-Timestamp、X-Nonce、X-Sign - 验证
X-Timestamp是否在有效时间范围内(当前时间 ± 5 分钟) - 根据
X-Merchant-Id获取对应的公钥 - 按照与签名生成相同的规则构建待签名字符串
- 使用公钥验证签名
- 验证通过则处理请求,否则返回 401 错误
6. API 调用示例
6.1 Java 示例
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.PrivateKey;
import java.util.HashMap;
import java.util.Map;
public class ApiClientExample {
// 商户 ID
private static final String MERCHANT_ID = "123456";
// RSA 私钥
private static final PrivateKey PRIVATE_KEY = loadPrivateKey("your_private_key_base64");
// API 地址
private static final String API_URL = "https://api.zackpay.com/v1/payments";
public static void main(String[] args) throws Exception {
// 1. 构建请求参数
Map<String, Object> params = new HashMap<>();
params.put("orderId", "123456789");
params.put("amount", "100.00");
params.put("currency", "INR");
params.put("description", "Test payment");
// 2. 生成时间戳和随机数
String timestamp = String.valueOf(System.currentTimeMillis() / 1000);
String nonce = generateNonce();
// 3. 构建签名内容
params.put("X-Merchant-Id", MERCHANT_ID);
params.put("X-Timestamp", timestamp);
params.put("X-Nonce", nonce);
String signContent = RsaUtil.buildSignContent(params);
// 4. 生成签名
String sign = RsaUtil.sign(signContent, PRIVATE_KEY);
// 5. 发送请求
URL url = new URL(API_URL);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setRequestProperty("Content-Type", "application/json");
conn.setRequestProperty("X-Merchant-Id", MERCHANT_ID);
conn.setRequestProperty("X-Timestamp", timestamp);
conn.setRequestProperty("X-Nonce", nonce);
conn.setRequestProperty("X-Sign", sign);
// 6. 写入请求体
String requestBody = toJson(params);
try (OutputStream os = conn.getOutputStream()) {
os.write(requestBody.getBytes("UTF-8"));
}
// 7. 读取响应
int responseCode = conn.getResponseCode();
String responseBody = readResponse(conn);
System.out.println("Response Code: " + responseCode);
System.out.println("Response Body: " + responseBody);
}
// 辅助方法:加载私钥
private static PrivateKey loadPrivateKey(String privateKeyBase64) throws Exception {
return RsaUtil.getPrivateKey(privateKeyBase64);
}
// 辅助方法:生成随机数
private static String generateNonce() {
return java.util.UUID.randomUUID().toString().replace("-", "");
}
// 辅助方法:对象转 JSON
private static String toJson(Object obj) {
// 使用 JSON 库实现,如 Jackson、FastJSON 等
return "{}";
}
// 辅助方法:读取响应
private static String readResponse(HttpURLConnection conn) throws Exception {
// 实现响应读取逻辑
return "";
}
}6.2 Python 示例
import requests
import json
import time
import uuid
import base64
from Crypto.Signature import pkcs1_15
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
# 商户配置
MERCHANT_ID = "123456"
PRIVATE_KEY_BASE64 = "your_private_key_base64"
API_URL = "https://api.zackpay.com/v1/payments"
# 加载私钥
def load_private_key(private_key_base64):
private_key_bytes = base64.b64decode(private_key_base64)
return RSA.import_key(private_key_bytes)
# 生成签名
def generate_sign(params, private_key):
# 构建签名内容
sign_content = "&"join([f"{k}={v}" for k, v in sorted(params.items()) if v is not None and v != ""])
# 生成签名
hash_obj = SHA256.new(sign_content.encode("utf-8"))
signature = pkcs1_15.new(private_key).sign(hash_obj)
return base64.b64encode(signature).decode("utf-8")
# 主函数
def main():
# 1. 构建请求参数
params = {
"orderId": "123456789",
"amount": "100.00",
"currency": "INR",
"description": "Test payment"
}
# 2. 生成时间戳和随机数
timestamp = str(int(time.time()))
nonce = str(uuid.uuid4()).replace("-", "")
# 3. 准备签名参数
sign_params = params.copy()
sign_params.update({
"X-Merchant-Id": MERCHANT_ID,
"X-Timestamp": timestamp,
"X-Nonce": nonce
})
# 4. 生成签名
private_key = load_private_key(PRIVATE_KEY_BASE64)
sign = generate_sign(sign_params, private_key)
# 5. 发送请求
headers = {
"Content-Type": "application/json",
"X-Merchant-Id": MERCHANT_ID,
"X-Timestamp": timestamp,
"X-Nonce": nonce,
"X-Sign": sign
}
response = requests.post(API_URL, headers=headers, json=params)
# 6. 处理响应
print(f"Response Code: {response.status_code}")
print(f"Response Body: {response.text}")
if __name__ == "__main__":
main()7. 错误码说明
| 错误码 | 描述 | 解决方案 |
|---|---|---|
| 401 | Missing required authentication headers | 检查请求头是否包含所有必需的认证参数 |
| 401 | Invalid timestamp | 检查时间戳是否在有效范围内(当前时间 ± 5 分钟) |
| 401 | Merchant API setting not found or disabled | 检查商户 ID 是否正确,API 功能是否已启用 |
| 401 | Invalid signature | 检查签名生成规则是否正确,私钥是否匹配 |
| 401 | Signature verification failed | 服务器内部错误,联系 ZackPay 技术支持 |
8. 最佳实践
保护私钥安全
- 私钥应存储在安全的环境中,如密钥管理服务(KMS)
- 不要将私钥硬编码在代码中或配置文件中
- 限制私钥的访问权限
防止重放攻击
- 每次请求使用唯一的随机数(nonce)
- 验证时间戳,拒绝过期请求
日志记录
- 记录所有 API 请求和响应
- 记录签名生成和验证过程(仅用于调试,不要记录私钥)
- 定期审计日志,发现异常请求及时处理
性能优化
- 缓存公钥,减少数据库查询
- 异步处理签名生成和验证
- 使用连接池管理 HTTP 连接
错误处理
- 实现完整的错误处理机制
- 对不同的错误码采取不同的处理策略
- 定期监控 API 调用成功率
9. 测试工具
ZackPay 提供了在线签名验证工具,可用于测试签名生成是否正确:
- 访问:https://api.zackpay.com/tools/signature-verify
- 输入待签名字符串、签名和公钥
- 点击「验证签名」按钮
- 查看验证结果