概述
平台提供RESTful API接口,支持商户系统通过HMAC签名认证方式调用。所有API请求都需要进行签名验证,确保请求的安全性和完整性。
核心特性
- HMAC签名认证:使用SHA256算法生成请求签名
- 时间戳防重放:请求包含时间戳,有效期5分钟
- IP白名单:支持配置IP白名单限制访问来源
- 重试机制:内置请求重试,提高稳定性
- SSE通知:支持服务器推送事件,实时接收平台通知
认证机制
HMAC签名算法
平台使用HMAC-SHA256算法进行请求签名验证。
签名生成步骤
- 获取时间戳
- 构建待签名字符串
- 生成签名
const timestamp = Math.floor(Date.now() / 1000).toString();
将请求参数(query参数 + body参数)按key排序,格式:key1=value1&key2=value2×tamp=1234567890
function buildSignString(params, timestamp) {
const sortedKeys = Object.keys(params).sort();
const signParts = sortedKeys.map((key) => `${key}=${params[key]}`);
signParts.push(`timestamp=${timestamp}`);
return signParts.join('&');
}
const signString = buildSignString(params, timestamp);
const signature = crypto.createHmac('sha256', apiSecret)
.update(signString)
.digest('hex');
请求头设置
所有API请求必须包含以下HTTP头:
X-Merchant-Id: {商户ID}
X-API-Key: {API密钥}
X-Timestamp: {时间戳(秒)}
X-Signature: {HMAC签名}
Content-Type: application/json
时间戳验证:时间戳必须是Unix时间戳(秒),请求时间戳与服务器时间差不能超过5分钟(300秒),超过有效期将返回
TIMESTAMP_EXPIRED 错误。
环境配置
必需的环境变量
在 .env 文件中配置以下变量:
# Platform API配置
PLATFORM_API_URL=http://localhost:3000 # 平台API地址
MERCHANT_API_KEY=your_api_key # API密钥
MERCHANT_API_SECRET=your_api_secret # API密钥
MERCHANT_ID=your_merchant_id # 商户ID
配置示例(Node.js)
// config/index.js
require('dotenv').config();
module.exports = {
platform: {
url: process.env.PLATFORM_API_URL || 'http://localhost:3000',
apiKey: process.env.MERCHANT_API_KEY,
apiSecret: process.env.MERCHANT_API_SECRET,
merchantId: process.env.MERCHANT_ID,
},
};
API客户端实现
基础客户端类
const axios = require('axios');
const crypto = require('crypto');
const config = require('./config');
class PlatformApiClient {
constructor() {
this.baseURL = config.platform.url;
this.apiKey = config.platform.apiKey;
this.apiSecret = config.platform.apiSecret;
this.merchantId = config.platform.merchantId;
}
/**
* 生成HMAC签名
*/
generateSignature(data, timestamp) {
const signString = this.buildSignString(data, timestamp);
return crypto.createHmac('sha256', this.apiSecret)
.update(signString)
.digest('hex');
}
/**
* 构建待签名字符串
*/
buildSignString(params, timestamp) {
const sortedKeys = Object.keys(params).sort();
const signParts = sortedKeys.map((key) => `${key}=${params[key]}`);
signParts.push(`timestamp=${timestamp}`);
return signParts.join('&');
}
/**
* 发送请求
*/
async request(method, path, data = null, options = {}) {
const timestamp = Math.floor(Date.now() / 1000).toString();
const url = `${this.baseURL}${path}`;
// 构建请求参数(query + body)
const params = {
...(options.params || {}),
...(data || {}),
};
// 生成签名
const signature = this.generateSignature(params, timestamp);
// 构建请求头
const headers = {
'X-Merchant-Id': this.merchantId,
'X-API-Key': this.apiKey,
'X-Timestamp': timestamp,
'X-Signature': signature,
'Content-Type': 'application/json',
...options.headers,
};
try {
let response;
if (method === 'GET') {
response = await axios.get(url, { params: data, headers });
} else if (method === 'POST') {
response = await axios.post(url, data, { headers });
} else if (method === 'PUT') {
response = await axios.put(url, data, { headers });
} else if (method === 'DELETE') {
response = await axios.delete(url, { params: data, headers });
}
return response.data;
} catch (error) {
throw this.handleError(error);
}
}
/**
* 错误处理
*/
handleError(error) {
if (error.response) {
const { status, data } = error.response;
const errorMessage = data?.error?.message || error.message;
const errorCode = data?.error?.code || 'UNKNOWN_ERROR';
return new Error(`${errorCode}: ${errorMessage}`);
} else if (error.request) {
return new Error('Network error: Unable to connect to platform API');
} else {
return error;
}
}
// API方法
async getModels(params = {}) {
return this.request('GET', '/api/platform/models', params);
}
async getModelById(modelId) {
return this.request('GET', `/api/platform/models/${modelId}`);
}
async getModelStreamUrl(modelId) {
return this.request('GET', `/api/platform/models/${modelId}/stream`);
}
async getCreditBalance() {
return this.request('GET', '/api/platform/credits/balance');
}
async getCreditTransactions(params = {}) {
return this.request('GET', '/api/platform/credits/transactions', params);
}
async checkCreditBalance(amount) {
return this.request('POST', '/api/platform/credits/check', { amount });
}
async consumeCredit(amount, description, orderId, modelId = null) {
const data = { amount, description, orderId };
if (modelId) data.modelId = modelId;
return this.request('POST', `/api/platform/merchants/${this.merchantId}/credit/consume`, data);
}
async createRechargeOrder(amount, activityId = null) {
const data = {
type: 'recharge',
amount,
description: `积分充值:${amount}元`,
};
if (activityId) data.activityId = activityId;
return this.request('POST', '/api/platform/payments/orders', data);
}
async getPaymentOrder(orderId) {
return this.request('GET', `/api/platform/payments/orders/${orderId}`);
}
async getMerchantStatistics(startDate = null, endDate = null) {
const params = {};
if (startDate) params.startDate = startDate;
if (endDate) params.endDate = endDate;
return this.request('GET', '/api/platform/statistics/merchant/me', params);
}
}
module.exports = new PlatformApiClient();
重试机制
async function retry(fn, options = {}) {
const { maxRetries = 3, delay = 1000 } = options;
let lastError;
for (let i = 0; i <= maxRetries; i++) {
try {
return await fn();
} catch (error) {
lastError = error;
// 如果是认证错误,不重试
if (error.response?.status === 401 || error.response?.status === 403) {
throw error;
}
// 最后一次尝试失败,抛出错误
if (i === maxRetries) {
break;
}
// 等待后重试
await new Promise(resolve => setTimeout(resolve, delay * (i + 1)));
}
}
throw lastError;
}
API接口列表
1. 模型管理
获取模型列表
GET /api/platform/models
请求参数:
page(number, 可选): 页码,默认1pageSize(number, 可选): 每页数量,默认20search(string, 可选): 搜索关键词auditStatus(string, 可选): 审核状态(pending, approved, rejected)status(string, 可选): 状态(active, inactive)
// 使用示例
const result = await platformApiClient.getModels({
page: 1,
pageSize: 20,
search: '模型名称',
auditStatus: 'approved'
});
获取模型详情
GET /api/platform/models/:id
const result = await platformApiClient.getModelById('model-id');
获取模型流URL
GET /api/platform/models/:id/stream
const result = await platformApiClient.getModelStreamUrl('model-id');
const streamUrl = result.data.url; // 临时访问URL
2. 积分管理
查询积分余额
GET /api/platform/credits/balance
const result = await platformApiClient.getCreditBalance();
console.log('余额:', result.data.balance);
查询积分交易记录
GET /api/platform/credits/transactions
const result = await platformApiClient.getCreditTransactions({
page: 1,
pageSize: 20,
type: 'recharge',
startDate: '2025-01-01',
endDate: '2025-12-31'
});
检查积分是否充足
POST /api/platform/credits/check
const result = await platformApiClient.checkCreditBalance(100.00);
if (result.data.sufficient) {
console.log('积分充足');
} else {
console.log('积分不足');
}
消费积分
POST /api/platform/merchants/:merchantId/credit/consume
const result = await platformApiClient.consumeCredit(
100.00,
'使用模型',
'order-id',
'model-id'
);
console.log('消费成功,剩余余额:', result.data.balance);
3. 支付管理
创建充值订单
POST /api/platform/payments/orders
const result = await platformApiClient.createRechargeOrder(100.00, 'activity-id');
console.log('订单ID:', result.data.orderId);
console.log('支付URL:', result.data.paymentUrl);
查询支付订单状态
GET /api/platform/payments/orders/:orderId
const result = await platformApiClient.getPaymentOrder('order-id');
console.log('订单状态:', result.data.status);
4. 统计信息
获取商户统计信息
GET /api/platform/statistics/merchant/me
const result = await platformApiClient.getMerchantStatistics(
'2025-01-01',
'2025-12-31'
);
console.log('积分余额:', result.data.creditBalance);
console.log('充值总额:', result.data.rechargeTotal);
console.log('消费总额:', result.data.consumeTotal);
错误处理
错误响应格式
{
"success": false,
"error": {
"code": "ERROR_CODE",
"message": "错误描述"
}
}
常见错误码
| 错误码 | HTTP状态码 | 说明 |
|---|---|---|
MISSING_AUTH_HEADERS |
401 | 缺少认证头信息 |
INVALID_API_KEY |
401 | API密钥无效 |
INVALID_SIGNATURE |
401 | 签名验证失败 |
TIMESTAMP_EXPIRED |
401 | 时间戳过期 |
MERCHANT_INACTIVE |
403 | 商户账户未激活 |
IP_NOT_ALLOWED |
401 | IP不在白名单中 |
INSUFFICIENT_CREDIT |
400 | 积分不足 |
错误处理示例
try {
const result = await platformApiClient.getCreditBalance();
console.log('Balance:', result.data.balance);
} catch (error) {
if (error.response) {
const { status, data } = error.response;
switch (data?.error?.code) {
case 'INVALID_SIGNATURE':
console.error('签名验证失败,请检查API密钥');
break;
case 'TIMESTAMP_EXPIRED':
console.error('请求时间戳过期,请检查系统时间');
break;
case 'INSUFFICIENT_CREDIT':
console.error('积分不足');
break;
default:
console.error('API错误:', data?.error?.message);
}
} else {
console.error('网络错误:', error.message);
}
}
最佳实践
1. 时间同步
确保系统时间与平台服务器时间同步,避免时间戳验证失败。
2. 请求重试
实现指数退避重试机制,提高稳定性。
3. 请求日志
记录所有API请求和响应,便于调试和审计。
4. 连接池
使用HTTP连接池提高性能。
5. 缓存策略
对不经常变化的数据进行缓存。
安全建议:
- 不要将API密钥提交到代码仓库
- 使用环境变量或密钥管理服务
- 定期轮换API密钥
- 生产环境必须使用HTTPS
- 在平台管理后台配置IP白名单
示例代码
完整示例:积分充值流程
const platformApiClient = require('./platformApiClient');
async function rechargeCredit(amount, activityId = null) {
try {
// 1. 检查当前余额
const balanceResult = await platformApiClient.getCreditBalance();
console.log('当前余额:', balanceResult.data.balance);
// 2. 创建充值订单
const orderResult = await platformApiClient.createRechargeOrder(amount, activityId);
const orderId = orderResult.data.orderId;
console.log('订单创建成功:', orderId);
// 3. 轮询订单状态(或使用SSE接收通知)
const checkOrderStatus = async () => {
const orderResult = await platformApiClient.getPaymentOrder(orderId);
return orderResult.data.status;
};
// 等待支付完成
let status = 'pending';
while (status === 'pending') {
await new Promise(resolve => setTimeout(resolve, 2000));
status = await checkOrderStatus();
}
if (status === 'paid') {
// 4. 支付成功,查询最新余额
const newBalanceResult = await platformApiClient.getCreditBalance();
console.log('充值成功,新余额:', newBalanceResult.data.balance);
return { success: true, orderId, balance: newBalanceResult.data.balance };
} else {
throw new Error('支付失败');
}
} catch (error) {
console.error('充值失败:', error.message);
throw error;
}
}
// 使用示例
rechargeCredit(100.00, 'activity-id')
.then(result => console.log('充值完成:', result))
.catch(error => console.error('充值失败:', error));
完整示例:使用模型
async function useModel(modelId, orderId) {
try {
// 1. 获取模型详情
const modelResult = await platformApiClient.getModelById(modelId);
const model = modelResult.data;
console.log('模型信息:', model.name, '价格:', model.price);
// 2. 检查积分是否充足
const checkResult = await platformApiClient.checkCreditBalance(model.price);
if (!checkResult.data.sufficient) {
throw new Error('积分不足');
}
// 3. 获取模型流URL
const streamResult = await platformApiClient.getModelStreamUrl(modelId);
const streamUrl = streamResult.data.url;
console.log('模型流URL:', streamUrl);
// 4. 消费积分
const consumeResult = await platformApiClient.consumeCredit(
model.price,
`使用模型: ${model.name}`,
orderId,
modelId
);
console.log('积分消费成功,剩余余额:', consumeResult.data.balance);
return {
success: true,
streamUrl,
balance: consumeResult.data.balance,
};
} catch (error) {
console.error('使用模型失败:', error.message);
throw error;
}
}