Q1: 签名验证失败怎么办?
排查步骤
-
检查时间戳
- 确保时间戳在当前时间的 ±300 秒(5分钟)内
- 检查服务器时间是否准确同步
- 使用 NTP 同步系统时间
-
检查参数顺序
- 确保所有参数按字母顺序排序
- 确保排除 signature 参数
- 参数名大小写要完全匹配
-
检查密钥
- 确认 SecretKey 正确无误
- 检查是否有前导或后缀空格
- 确认使用的是正确环境的密钥
-
检查编码
- 确保使用 UTF-8 编码
- 检查特殊字符是否正确转义
- 确认签名字符串构造正确
示例修复
# 问题:密钥包含空格
SECRET_KEY = " sk_abc123 " # 错误
SECRET_KEY = "sk_abc123" # 正确
# 问题:参数顺序错误
params = "b=2&a=1" # 错误
params = "a=1&b=2" # 正确(按字母顺序)
# 问题:时间戳过期
timestamp = "1234567890" # 错误(太旧)
timestamp = str(int(time.time())) # 正确(当前时间)
调试技巧
def debug_signature(method, path, params, secret_key):
# 打印签名字符串
sorted_params = "&".join(
f"{'{'}k{'}'}={'{'}v{'}'}" for k, v in sorted(params.items())
if k != 'signature'
)
sign_str = f"{'{'}method{'}'}{'{'}path{'}'}{'{'}params['timestamp']{'}'}{'{'}params['nonce']{'}'}{'{'}sorted_params{'}'}"
print(f"签名字符串: {'{'}sign_str{'}'}")
# 生成签名
signature = hmac.new(
secret_key.encode(),
sign_str.encode(),
hashlib.sha256
).hexdigest()
print(f"生成签名: {'{'}signature{'}'}")
return signature
Q2: 请求频率超限怎么解决?
解决方案
-
降低请求频率
- 合理规划请求时间
- 批量操作改为分批处理
- 增加请求间隔
-
实现指数退避重试
- 使用 2^n 秒间隔重试
- 添加随机抖动避免同时重试
- 设置最大重试次数
-
联系管理员调整限流配置
- 说明业务需求
- 申请更高的频率限制
- 考虑使用多个 AccessKey 分散请求
重试示例
import time
import random
def api_request_with_backoff(request_func, max_retries=3):
"""使用指数退避策略重试"""
for attempt in range(max_retries):
try:
return request_func()
except APIError as e:
if e.code == 429: # 频率限制
if attempt == max_retries - 1:
raise # 最后一次尝试失败,抛出异常
# 指数退避: 2^n + 随机抖动
delay = (2 ** attempt) + random.uniform(0, 1)
print(f"频率限制,等待 {'{'}delay:.2f{'}'} 秒后重试...")
time.sleep(delay)
else:
raise # 其他错误直接抛出
# 使用示例
result = api_request_with_backoff(lambda: client.get_card_info("CARD123"))
监控告警
建议设置以下监控指标:
- 429 错误出现频率
- 请求成功率
- 平均响应时间
- 重试次数统计
Q3: 卡板状态不允许操作怎么办?
状态说明
不同操作对卡板状态有要求:
状态检查
def check_card_status(card_info, operation):
"""检查卡板状态是否允许操作"""
status = card_info.get('status')
status_map = {'{'}
1: "待激活",
2: "已激活",
3: "机卡分离",
4: "停机",
5: "风险停机",
6: "销户",
7: "库存",
8: "其他",
9: "可测试",
10: "达量断网"
{'}'}
status_name = status_map.get(status, "未知状态")
print(f"卡板状态: {'{'}status{'}'} ({'{'}status_name{'}'})")
# 检查操作权限
allowed_operations = {'{'}
'restart': [4], # 复机:仅停机状态
'stop': [2], # 停机:仅已激活状态
'order': [2], # 订购:仅已激活状态
'query': list(range(1, 11)) # 查询:所有状态
{'}'}
if status in allowed_operations.get(operation, []):
return True
else:
print(f"当前状态 ({'{'}status_name{'}'}) 不允许执行 {'{'}operation{'}'} 操作")
return False
# 使用示例
card_info = client.get_card_info("CARD123")
if check_card_status(card_info, 'stop'):
client.stop_card("CARD123")
处理建议
- 待激活状态:先激活卡板
- 停机状态:需要先复机才能使用
- 风险停机:联系管理员人工处理
- 销户状态:无法恢复,需重新开户
Q4: 如何调试签名问题?
调试工具
创建一个调试函数,对比客户端和服务器的签名计算过程:
def debug_signature_detailed(method, path, params, secret_key):
"""详细调试签名生成过程"""
print("=== 签名调试信息 ===")
# 1. 显示原始参数
print("\n1. 原始参数:")
for k, v in params.items():
print(f" {'{'}k{'}'} = {'{'}v{'}'}")
# 2. 排除 signature 并排序
print("\n2. 排序后参数(排除signature):")
sorted_params = []
for k in sorted(params.keys()):
if k != 'signature':
sorted_params.append(f"{'{'}k{'}'}={'{'}params[k]{'}'}")
print(f" {'{'}k{'}'} = {'{'}params[k]{'}'}")
# 3. 构造参数字符串
param_str = "&".join(sorted_params)
print(f"\n3. 参数字符串:\n {'{'}param_str{'}'}")
# 4. 构造签名字符串
sign_str = f"{'{'}method{'}'}{'{'}path{'}'}{'{'}params['timestamp']{'}'}{'{'}params['nonce']{'}'}{'{'}param_str{'}'}"
print(f"\n4. 签名字符串:\n {'{'}sign_str{'}'}")
# 5. 生成签名
signature = hmac.new(
secret_key.encode('utf-8'),
sign_str.encode('utf-8'),
hashlib.sha256
).hexdigest()
print(f"\n5. 生成签名:\n {'{'}signature{'}'}")
# 6. 对比
if 'signature' in params:
print(f"\n6. 参数中的签名:\n {'{'}params['signature']{'}'}")
print(f"\n7. 签名匹配: {'{'}signature == params['signature']{'}'}")
print("\n===================")
return signature
在线调试
可以使用在线工具验证 HMAC-SHA256 签名:
将签名字符串和密钥输入,对比生成的签名是否一致。
Q5: 如何验证环境配置?
验证清单
在正式使用 API 前,建议验证以下项目:
验证脚本
#!/bin/bash
echo "=== 天城智控环境验证 ==="
echo ""
BASE_URL="http://your-api-server.com"
ACCESS_KEY="ak_xxxxxxxxxx"
SECRET_KEY="sk_xxxxxxxxxx"
echo "1. 网络连通性测试..."
if curl -I "$BASE_URL/api/v1/heartbeat" \
--connect-timeout 10 \
--max-time 30 \
-s -o /dev/null -w "%{'{'}http_code{'}'}" | grep -q "200"; then
echo " ✅ 网络连接正常"
else
echo " ❌ 网络连接失败"
exit 1
fi
echo ""
echo "2. 时间同步检查..."
LOCAL_TIME=$(date +%s)
echo " 本地时间戳: $LOCAL_TIME"
echo " 建议使用 NTP 同步: sudo ntpdate -u pool.ntp.org"
echo ""
echo "3. 心跳接口测试..."
TIMESTAMP=$(date +%s)
NONCE=$(uuidgen)
PARAMS="access_key=$ACCESS_KEY&nonce=$NONCE×tamp=$TIMESTAMP"
SIGN_STR="GET/api/v1/heartbeat${'{'}TIMESTAMP{'}'}${'{'}NONCE{'}'}${'{'}PARAMS{'}'}"
echo " 签名字符串: $SIGN_STR"
echo " 如果签名验证失败,请检查 SecretKey 是否正确"
echo ""
echo "4. AccessKey 验证..."
if [[ $ACCESS_KEY == ak_* ]]; then
echo " ✅ AccessKey 格式正确"
else
echo " ⚠️ AccessKey 格式可能不正确(应以 ak_ 开头)"
fi
echo ""
echo "5. SecretKey 验证..."
if [[ ${'{'}#SECRET_KEY{'}'} -ge 16 ]]; then
echo " ✅ SecretKey 长度符合要求"
else
echo " ❌ SecretKey 长度不足(应至少16字符)"
fi
echo ""
echo "=== 验证完成 ==="
Python 验证脚本
#!/usr/bin/env python3
"""环境配置验证脚本"""
import sys
import time
import socket
from urllib.parse import urlparse
def check_network(base_url):
"""检查网络连通性"""
try:
parsed = urlparse(base_url)
host = parsed.hostname
port = parsed.port or (443 if parsed.scheme == 'https' else 80)
sock = socket.create_connection((host, port), timeout=10)
sock.close()
print("✅ 网络连接正常")
return True
except Exception as e:
print(f"❌ 网络连接失败: {'{'}e{'}'}")
return False
def check_time_sync():
"""检查时间同步"""
local_time = int(time.time())
print(f"✅ 本地时间戳: {'{'}local_time{'}'}")
print(f" 时间: {'{'}time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(local_time)){'}'}")
return True
def check_credentials(access_key, secret_key):
"""检查凭据格式"""
if access_key.startswith('ak_'):
print("✅ AccessKey 格式正确")
else:
print("⚠️ AccessKey 格式可能不正确")
if len(secret_key) >= 16:
print("✅ SecretKey 长度符合要求")
else:
print("❌ SecretKey 长度不足")
return True
if __name__ == "__main__":
BASE_URL = "http://your-api-server.com"
ACCESS_KEY = "ak_xxxxxxxxxx"
SECRET_KEY = "sk_xxxxxxxxxx"
print("=== 天城智控环境验证 ===\n")
print("1. 网络连通性测试")
check_network(BASE_URL)
print()
print("2. 时间同步检查")
check_time_sync()
print()
print("3. 凭据格式检查")
check_credentials(ACCESS_KEY, SECRET_KEY)
print()
print("=== 验证完成 ===")
Q6: 如何处理超时问题?
超时类型
- 连接超时:无法建立 TCP 连接
- 读取超时:连接建立但响应缓慢
- 请求超时:整体请求时间过长
解决方案
# 设置合理的超时时间
client = WLWOpenAPIClient(
base_url="https://api.example.com",
access_key="ak_xxx",
secret_key="sk_xxx",
timeout=30 # 30秒超时
)
# 实现重试机制
def request_with_retry(func, max_retries=3):
for attempt in range(max_retries):
try:
return func()
except TimeoutError:
if attempt == max_retries - 1:
raise
print(f"请求超时,重试 {'{'}attempt + 1{'}'}/{'{'}max_retries{'}'}")
time.sleep(2 ** attempt)
Q7: 池中池卡板调用 OpenAPI 怎么处理?
处理口径
- 流量查询:池中池卡板调用卡板信息查询时,
used_flow 返回运营商上游数据中的已用流量;普通卡板返回本地统计的已用流量。
- 欠费拦截:同一客户名下任一启用池子欠费并触发 OpenAPI 暂停后,该客户的 OpenAPI 请求会返回
Forbidden。
- 欠费判断:池子余额低于保证金时视为欠费,需要先充值或补足余额。
- 停复机:欠费拦截通过后,池中池卡板可以正常调用停复机接口;复机不会因为没有单卡订单而被阻断。
- 扣费:池中池余额不在接口查询或轮询时实时扣费,由后台定时任务处理昨天流量,并由月结流程结算周期差额。
- 套餐订购:套餐订购接口创建普通卡板套餐订单;池中池日扣费和月结不依赖下游通过该接口下单。
欠费响应示例
{'{'}
"code": "Forbidden",
"msg": "池中池已欠费(余额低于保证金),OpenAPI 已暂停,请先充值或补足余额",
"ok": false
{'}'}
Q8: 账单提取配置禁止 OpenAPI 后影响哪些对象?
- 执行时点:配置勾选“禁止调用接口”后,使用该配置成功提取账单时执行。该动作与池中池余额或保证金判断无关。
- 卡板配置:只禁止配置内直接绑定的卡板调用卡板 OpenAPI。
- 设备配置:只禁止配置内直接绑定的设备调用设备 OpenAPI。
- 其他对象:同一客户/代理名下未绑定到该配置的卡板、设备可正常调用,不会整个代理一起封禁。
- 关联关系:卡板和设备按配置类型精确匹配,不通过设备及其槽位卡的绑定关系相互推导。
- 恢复方式:取消“禁止调用接口”动作、解绑对象或删除配置后,对应对象恢复调用。
账单配置拦截与池中池欠费使用不同的错误信息:
- 卡板:
账单提取配置已禁止该卡板调用接口
- 设备:
账单提取配置已禁止该设备调用接口
这两种账单配置报错不会返回“池中池已欠费”的提示。
更多问题?
如果以上内容没有解决您的问题,请:
- 查看 API 响应中的详细错误信息
- 检查系统日志获取更多上下文
- 联系技术支持团队,提供:
- 请求 ID(X-Request-ID)
- 完整的错误信息
- 请求时间和卡板编号
- 重现步骤
Q1: 签名验证失败怎么办?
排查步骤
检查时间戳
检查参数顺序
检查密钥
检查编码
示例修复
调试技巧
Q2: 请求频率超限怎么解决?
解决方案
降低请求频率
实现指数退避重试
联系管理员调整限流配置
重试示例
监控告警
建议设置以下监控指标:
Q3: 卡板状态不允许操作怎么办?
状态说明
不同操作对卡板状态有要求:
状态检查
处理建议
Q4: 如何调试签名问题?
调试工具
创建一个调试函数,对比客户端和服务器的签名计算过程:
在线调试
可以使用在线工具验证 HMAC-SHA256 签名:
将签名字符串和密钥输入,对比生成的签名是否一致。
Q5: 如何验证环境配置?
验证清单
在正式使用 API 前,建议验证以下项目:
验证脚本
#!/bin/bash echo "=== 天城智控环境验证 ===" echo "" # 配置信息 BASE_URL="http://your-api-server.com" ACCESS_KEY="ak_xxxxxxxxxx" SECRET_KEY="sk_xxxxxxxxxx" # 1. 网络连通性测试 echo "1. 网络连通性测试..." if curl -I "$BASE_URL/api/v1/heartbeat" \ --connect-timeout 10 \ --max-time 30 \ -s -o /dev/null -w "%{'{'}http_code{'}'}" | grep -q "200"; then echo " ✅ 网络连接正常" else echo " ❌ 网络连接失败" exit 1 fi echo "" # 2. 时间同步检查 echo "2. 时间同步检查..." LOCAL_TIME=$(date +%s) echo " 本地时间戳: $LOCAL_TIME" echo " 建议使用 NTP 同步: sudo ntpdate -u pool.ntp.org" echo "" # 3. 心跳接口测试 echo "3. 心跳接口测试..." TIMESTAMP=$(date +%s) NONCE=$(uuidgen) # 构造签名(简化版,实际需要完整实现) PARAMS="access_key=$ACCESS_KEY&nonce=$NONCE×tamp=$TIMESTAMP" SIGN_STR="GET/api/v1/heartbeat${'{'}TIMESTAMP{'}'}${'{'}NONCE{'}'}${'{'}PARAMS{'}'}" # 这里需要实现 HMAC-SHA256 签名 # SIGNATURE=$(echo -n "$SIGN_STR" | openssl dgst -sha256 -hmac "$SECRET_KEY" | cut -d' ' -f2) echo " 签名字符串: $SIGN_STR" echo " 如果签名验证失败,请检查 SecretKey 是否正确" echo "" # 4. AccessKey 验证 echo "4. AccessKey 验证..." if [[ $ACCESS_KEY == ak_* ]]; then echo " ✅ AccessKey 格式正确" else echo " ⚠️ AccessKey 格式可能不正确(应以 ak_ 开头)" fi echo "" # 5. SecretKey 验证 echo "5. SecretKey 验证..." if [[ ${'{'}#SECRET_KEY{'}'} -ge 16 ]]; then echo " ✅ SecretKey 长度符合要求" else echo " ❌ SecretKey 长度不足(应至少16字符)" fi echo "" echo "=== 验证完成 ==="Python 验证脚本
Q6: 如何处理超时问题?
超时类型
解决方案
Q7: 池中池卡板调用 OpenAPI 怎么处理?
处理口径
used_flow返回运营商上游数据中的已用流量;普通卡板返回本地统计的已用流量。Forbidden。欠费响应示例
{'{'} "code": "Forbidden", "msg": "池中池已欠费(余额低于保证金),OpenAPI 已暂停,请先充值或补足余额", "ok": false {'}'}Q8: 账单提取配置禁止 OpenAPI 后影响哪些对象?
账单配置拦截与池中池欠费使用不同的错误信息:
账单提取配置已禁止该卡板调用接口账单提取配置已禁止该设备调用接口这两种账单配置报错不会返回“池中池已欠费”的提示。
更多问题?
如果以上内容没有解决您的问题,请: