调用Pionex API下单时出现INVALID_SIGNATURE错误求助
问题:Pionex API POST下单请求返回INVALID_SIGNATURE错误
调用Pionex API进行GET查询可正常获取结果,但提交POST请求下单时收到以下错误:
{'result': False, 'timestamp': 1702144786335, 'code': 'INVALID_SIGNATURE', 'message': 'invalid signature'}
正常运行的GET查询代码
import requests import time import json import hashlib import hmac from urllib.parse import urlencode api_key = "my api key" api_secret = "my api secret" # Function to generate the PIONEX-SIGNATURE def generate_signature(api_secret, method, path, timestamp, params=None, data=None): # Set query parameters as key-value pairs: key=value if params is None: params = {} params['timestamp'] = timestamp query_string = urlencode(sorted(params.items())) # Concatenate query parameters after PATH with ? path_url = f"{path}?{query_string}" # Concatenate METHOD and PATH_URL message = f"{method.upper()}{path_url}" # Concatenate related entity body of POST and DELETE after step 5 if data is not None: message += json.dumps(data, separators=(',', ':')) print(message) # Use API Secret and the above result to generate HMAC SHA256 code, then convert it to hexadecimal signature = hmac.new(api_secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest() return signature def make_private_request(method, endpoint, params=None, data=None): url = f"https://api.pionex.com{endpoint}" # Get current millisecond timestamp timestamp = str(int(time.time() * 1000)) # Set timestamp in params if params is None: params = {} params['timestamp'] = timestamp # Set headers headers = { 'PIONEX-KEY': api_key, 'PIONEX-SIGNATURE': generate_signature(api_secret, method, endpoint, timestamp, params=params, data=data), 'Content-Type': 'application/json', } # Make the request if method == 'GET': response = requests.get(url, headers=headers, params=params) elif method == 'POST': headers['Content-Type'] = 'application/json' response = requests.post(url, headers=headers, json=data, params=params ) elif method == 'DELETE': response = requests.delete(url, headers=headers, json=data, params=params) else: raise ValueError(f"Unsupported HTTP method: {method}") print(f"Generated Signature: {headers['PIONEX-SIGNATURE']}") print(f"Generated Message: {method.upper()}{url}?{urlencode(sorted(params.items()))}") print(f"Server Timestamp: {response.json().get('timestamp')}") return response.json() endpoint = "/api/v1/trade/order" order_data = { "clientOrderId": "9e3d93d6-e9a4-465a-a39c-2e48568fe194", "symbol": "BTC_USDT", "side": "BUY", "type": "LIMIT", "size": "0.1", "price": "30000", "IOC": True } response = make_private_request('GET', '/api/v1/trade/allOrders', params={'symbol': 'BTC_USDT', 'limit': 1}) print(response)
GET查询代码返回结果
GET/api/v1/trade/allOrders?limit=1&symbol=BTC_USDT×tamp=1702145234971 Generated Signature: f34cb0064bf618d64181d7b2afb64f24f63872f38eed8f7ee8683c34c6e8a86d Generated Message: GEThttps://api.pionex.com/api/v1/trade/allOrders?limit=1&symbol=BTC_USDT×tamp=1702145234971 Server Timestamp: 1702145234500 {'result': True, 'data': {'orders': []}, 'timestamp': 1702145234500}
出现签名错误的POST下单代码
import requests import time import json import hashlib import hmac from urllib.parse import urlencode api_key = "my api key" api_secret = "my api secret" def get_trade_price(api_key, secret_key, symbol): # Endpoint voor het verkrijgen van handelsinformatie endpoint = 'https://api.pionex.com/api/v1/market/tickers' # Parameters voor het verzoek params = { 'symbol': symbol } # Voeg timestamp en API-sleutel toe aan de parameters params['timestamp'] = int(time.time() * 1000) params['apiKey'] = api_key # Creëer de handtekening (signature) voor het verzoek query_string = '&'.join([f"{key}={params[key]}" for key in sorted(params.keys())]) signature = hmac.new(secret_key.encode(), query_string.encode(), hashlib.sha256).hexdigest() params['signature'] = signature # Maak het verzoek naar de Pionex API response = requests.get(endpoint, params=params) # Controleer of het verzoek succesvol was (statuscode 200) if response.status_code == 200: # Decodeer de JSON-response data = response.json() # Controleer de structuur van de ontvangen gegevens tickers = data['data']['tickers'] for ticker in tickers: if ticker['symbol'] == symbol: # Haal de handelsprijs op trade_price = ticker.get('close') # Druk de handelsprijs af return trade_price else: print(f"Fout bij het ophalen van de handelsprijs. Statuscode: {response.status_code}") print(response.text) return None # Function to generate the PIONEX-SIGNATURE def generate_signature(api_secret, method, path, timestamp, params=None, data=None): # Set query parameters as key-value pairs: key=value if params is None: params = {} params['timestamp'] = timestamp query_string = urlencode(sorted(params.items())) # Concatenate query parameters after PATH with ? path_url = f"{path}?{query_string}" # Concatenate METHOD and PATH_URL message = f"{method.upper()}{path_url}" # Concatenate related entity body of POST and DELETE after step 5 if data is not None: message += json.dumps(data, separators=(',', ':')) print(message) # Use API Secret and the above result to generate HMAC SHA256 code, then convert it to hexadecimal signature = hmac.new(api_secret.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest() return signature def make_private_request(method, endpoint, params=None, data=None, payload=None): url = f"https://api.pionex.com{endpoint}" # Get current millisecond timestamp timestamp = str(int(time.time() * 1000)) # Set timestamp in params if params is None: params = {} params['timestamp'] = timestamp # Set headers headers = { 'PIONEX-KEY': api_key, 'PIONEX-SIGNATURE': generate_signature(api_secret, method, endpoint, timestamp, params=params, data=data), 'Content-Type': 'application/json', } # Make the request if method == 'GET': response = requests.get(url, headers=headers, params=params) elif method == 'POST': headers['Content-Type'] = 'application/json' response = requests.post(url + f"?timestamp={timestamp}", headers=headers, json=data) elif method == 'DELETE': response = requests.delete(url, headers=headers, json=data, params=params) else: raise ValueError(f"Unsupported HTTP method: {method}") print(f"Generated Signature: {headers['PIONEX-SIGNATURE']}") print(f"Generated Message: {method.upper()}{url}?{urlencode(sorted(params.items()))}") print(f"Server Timestamp: {response.json().get('timestamp')}") return response.json() trading_price = get_trade_price(api_key, api_secret, "BTC_USDT") endpoint = "/api/v1/trade/order" order_data = { "symbol": "BTC_USDT", } response = make_private_request('POST', endpoint, data=order_data) print(response)
POST下单代码返回结果
POST/api/v1/trade/order?timestamp=1702145331250{"symbol":"BTC_USDT"} Generated Signature: d404d623898e46ba61d37c86245594f2885a2160cbf401bd1c3ed081a04494af Generated Message: POSThttps://api.pionex.com/api/v1/trade/order?timestamp=1702145331250 Server Timestamp: 1702145330729 {'result': False, 'timestamp': 1702145330729, 'code': 'INVALID_SIGNATURE', 'message': 'invalid signature'}
问题原因与解决方法
核心问题
签名生成时使用了无空格的JSON字符串(json.dumps(data, separators=(',', ':'))),但发送POST请求时使用requests.post(..., json=data)参数,会自动生成带空格的JSON字符串(默认分隔符为, 和: )。两者内容不一致,导致服务器验证签名失败。
同时,POST请求手动拼接URL的方式存在潜在的参数传递风险,应统一使用params参数传递查询参数。
修复后的代码修改点
修改make_private_request函数中的POST请求逻辑:
elif method == 'POST': headers['Content-Type'] = 'application/json' # 手动生成与签名时完全一致的无空格JSON字符串 json_payload = json.dumps(data, separators=(',', ':')) # 使用params参数传递查询参数,避免手动拼接URL response = requests.post(url, headers=headers, data=json_payload, params=params)
额外注意事项
下单请求的order_data需要包含API要求的完整参数(如side、type、size等),否则签名验证通过后仍会返回参数错误。示例完整订单数据:
order_data = { "clientOrderId": "9e3d93d6-e9a4-465a-a39c-2e48568fe194", "symbol": "BTC_USDT", "side": "BUY", "type": "LIMIT", "size": "0.1", "price": "30000", "IOC": True }
内容的提问来源于stack exchange,提问作者Devi Riet
相关产品推荐
相关产品推荐

