# 常见问题
## Q1: 签名验证失败怎么办?
### 排查步骤
1. **检查时间戳**
- 确保时间戳在当前时间的 ±300 秒(5分钟)内
- 检查服务器时间是否准确同步
- 使用 NTP 同步系统时间
2. **检查参数顺序**
- 确保所有参数按字母顺序排序
- 确保排除 signature 参数
- 参数名大小写要完全匹配
3. **检查密钥**
- 确认 SecretKey 正确无误
- 检查是否有前导或后缀空格
- 确认使用的是正确环境的密钥
4. **检查编码**
- 确保使用 UTF-8 编码
- 检查特殊字符是否正确转义
- 确认签名字符串构造正确
### 示例修复
```python
# 问题:密钥包含空格
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())) # 正确(当前时间)
```
### 调试技巧
```python
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: 请求频率超限怎么解决?
### 解决方案
1. **降低请求频率**
- 合理规划请求时间
- 批量操作改为分批处理
- 增加请求间隔
2. **实现指数退避重试**
- 使用 2^n 秒间隔重试
- 添加随机抖动避免同时重试
- 设置最大重试次数
3. **联系管理员调整限流配置**
- 说明业务需求
- 申请更高的频率限制
- 考虑使用多个 AccessKey 分散请求
### 重试示例
```python
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: 卡板状态不允许操作怎么办?
### 状态说明
不同操作对卡板状态有要求:
| 操作 | 允许的状态 | 不允许的状态 |
|------|-----------------|---------------------|
| 复机 | 停机(4) | 已激活(2)、风险停机(5)、销户(6) |
| 停机 | 已激活(2) | 停机(4)、销户(6) |
| 订购套餐 | 已激活(2) | 其他所有状态 |
| 查询信息 | 所有状态 | 无限制 |
### 状态检查
```python
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: 如何调试签名问题?
### 调试工具
创建一个调试函数,对比客户端和服务器的签名计算过程:
```python
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 签名:
- https://www.devglan.com/online-tools/hmac-sha256-online
- https://tooltt.com/hmac-sha256/
将签名字符串和密钥输入,对比生成的签名是否一致。
---
## Q5: 如何验证环境配置?
### 验证清单
在正式使用 API 前,建议验证以下项目:
- [ ] 确认 Base URL 正确(包括协议和端口)
- [ ] 验证网络连通性(能否访问服务器)
- [ ] 检查 AccessKey 和 SecretKey 是否正确
- [ ] 确认服务器时间同步(时间偏差 < 5分钟)
- [ ] 测试心跳接口是否正常
### 验证脚本
```bash
#!/bin/bash
echo "=== WLW OpenAPI 环境验证 ==="
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 验证脚本
```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("=== WLW OpenAPI 环境验证 ===\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: 如何处理超时问题?
### 超时类型
1. **连接超时**:无法建立 TCP 连接
2. **读取超时**:连接建立但响应缓慢
3. **请求超时**:整体请求时间过长
### 解决方案
```python
# 设置合理的超时时间
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)
```
---
## 更多问题?
如果以上内容没有解决您的问题,请:
1. 查看 API 响应中的详细错误信息
2. 检查系统日志获取更多上下文
3. 联系技术支持团队,提供:
- 请求 ID(X-Request-ID)
- 完整的错误信息
- 请求时间和卡板编号
- 重现步骤