使用aiohttp调用Backpack API时签名无效问题求助
排查Backpack API签名无效问题
问题描述
使用aiohttp向Backpack交易所API发送POST请求时,持续收到「Invalid signature」错误。已验证除X-Signature外,其余请求头及请求体参数均正确(修改参数会得到不同响应)。
要求的请求头
headers = { 'X-Timestamp': str(timestamp), 'X-Window': str(self.WINDOW), 'X-API-Key': self.public_key, 'X-Signature': signature }
签名结构示例
签名需按以下结构拼接字符串,使用与公钥配对的私钥进行签名:
instruction=orderExecute&postOnly=False&price=1&quantity=10&selfTradePrevention=Allow&side=Bid&symbol=SOL_USDC&timeInForce=IOC×tamp=1710451343478&window=6000
相关代码
class Site: WINDOW = 6000 def __init__(self, token, private_key): self.public_key = token self.private_key = private_key async def headers(self, params: dict, instruction: str) -> dict: timestamp = await self._unix_time() params["window"] = self.WINDOW params["timestamp"] = timestamp sorted_params = sorted(params.items()) query_string = f"instruction={instruction}&"+'&'.join(f"{key}={value}" for key, value in sorted_params) signature = await self._sign(query_string) headers = { 'X-Timestamp': str(timestamp), 'X-Window': str(self.WINDOW), 'X-API-Key': self.public_key, 'X-Signature': signature } return headers async def _sign(self, data): private_key_bytes = base64.b64decode(self.private_key) private_key = Ed25519PrivateKey.from_private_bytes(private_key_bytes) signature = private_key.sign(data.encode()) signature_base64 = base64.b64encode(signature).decode() return signature_base64 @staticmethod async def _unix_time(): async with aiohttp.ClientSession() as session: async with session.get("https://api.backpack.exchange/api/v1/time") as response: return await response.text() class Trade(Site): def __init__(self, public_key, private_key): super().__init__(public_key, private_key) async def buy_order(self): params = { "orderType": OrderType.LIMIT.value, "postOnly": PostOnly.FALSE.value, "price": "1", "quantity": "10", "selfTradePrevention": SelfTradePrevention.ALLOW.value, "side": Side.BUY.value, "symbol": Symbol.SOL.value, "timeInForce": TimeInForce.IOC.value, } headers = await self.headers(params=params, instruction=Instruction.ORDER_EXECUTE.value) headers['Content-Type'] = 'application/json; charset=utf-8' body = json.dumps(params) r = await _handle_post(headers=headers, body=body)
排查与修复方案
1. 时间戳类型不匹配
_unix_time方法返回的是API返回的文本字符串,而签名需要的是毫秒级整数格式的时间戳。如果返回的字符串包含非数字内容(如引号),会导致签名字符串不匹配。修改如下:
@staticmethod async def _unix_time(): async with aiohttp.ClientSession() as session: async with session.get("https://api.backpack.exchange/api/v1/time") as response: # 将返回的文本转为整数 return int(await response.text())
2. 请求体与签名参数不一致
当前代码直接修改了原params字典,添加了window和timestamp,导致请求体中包含这两个字段,但Backpack API可能要求这两个字段仅用于签名,不包含在请求体内。需使用副本处理签名参数:
async def headers(self, params: dict, instruction: str) -> dict: timestamp = await self._unix_time() # 创建参数副本,避免修改原请求体参数 sign_params = params.copy() sign_params["window"] = self.WINDOW sign_params["timestamp"] = timestamp sorted_params = sorted(sign_params.items()) query_string = f"instruction={instruction}&"+'&'.join(f"{key}={value}" for key, value in sorted_params) signature = await self._sign(query_string) headers = { 'X-Timestamp': str(timestamp), 'X-Window': str(self.WINDOW), 'X-API-Key': self.public_key, 'X-Signature': signature } return headers
3. 参数值大小写/内容不匹配
对比签名示例与代码中的参数值:
- 示例中
side=Bid,需确认Side.BUY.value的返回值是否为"Bid"而非"Buy"; - 示例中
postOnly=False,需确认PostOnly.FALSE.value是否为"False"(首字母大写)而非"false"; - 检查
symbol参数:示例为SOL_USDC,代码中Symbol.SOL.value是否对应正确的交易对字符串。
4. 签名字符串拼接验证
在生成query_string后,打印输出并与官方示例对比,确保:
- 无多余空格或字符;
- 参数顺序与示例逻辑一致(官方要求按参数名升序排序,代码中
sorted(params.items())已实现,但需确认排序后的结果与示例匹配); - 未多传/漏传参数:比如代码中
params包含orderType,需确认Backpack的orderExecute接口是否要求该字段包含在签名中。
5. Ed25519签名细节确认
- 确保私钥是Backpack提供的base64编码Ed25519私钥,解码后长度正确(Ed25519私钥为32字节);
- 确认使用的
cryptography库版本正确,Ed25519PrivateKey.sign方法默认使用的签名格式符合Backpack要求(无需添加额外上下文或哈希算法)。
内容的提问来源于stack exchange,提问作者sndmndss
相关产品推荐
相关产品推荐

