В связанных документах есть пробел между двоеточием и значениями.
signature_string = 'date' + ':' + formated_time + '\n' + 'x-mod-nonce' + ':' + nonce
должно быть:
signature_string = 'date' + ': ' + formated_time + '\n' + 'x-mod-nonce' + ': ' + nonce
или (проще):
signature_string = 'date: ' + formated_time + '\n' + 'x-mod-nonce: ' + nonce
Обновление
Я зарегистрировался, чтобы увидеть, что происходит. Я также запустил ваш код на примере, приведенном в документации, и увидел, что подпись не совсем верна.
В дополнение к изменению, которое я предложил выше, необходимы дальнейшие изменения.
После смены строки
b64 = codecs.encode(codecs.decode(hex_code, 'hex'), 'base64').decode()
до
b64 = codecs.encode(codecs.decode(hex_code, 'hex'), 'base64').decode().strip()
подпись примера соответствует.
После этого я смог подключиться к API своими собственными ключами.
Вот полный рабочий код:
import codecs
import hashlib
import hmac
import secrets
import urllib.parse
from datetime import datetime
from time import mktime
from wsgiref.handlers import format_date_time
import requests
key = '<key>'
secret = '<secret>'
account_id = '<account id>'
url = f'https://api-sandbox.modulrfinance.com/api-sandbox/accounts/{account_id}'
# Getting current time
now = datetime.now()
stamp = mktime(now.timetuple())
# Formats time into this format --> Mon, 25 Jul 2016 16:36:07 GMT
formatted_time = format_date_time(stamp)
# Generates a secure random string for the nonce
nonce = secrets.token_urlsafe(30)
# Combines date and nonce into a single string that will be signed
signature_string = 'date' + ': ' + formatted_time + '\n' + 'x-mod-nonce' + ': ' + nonce
# Encodes secret and message into a format that can be signed
secret = bytes(secret, encoding='utf-8')
message = bytes(signature_string, encoding='utf-8')
# Signing process
digester = hmac.new(secret, message, hashlib.sha1)
# Converts to hex
hex_code = digester.hexdigest()
# Decodes the signed string in hex into base64
b64 = codecs.encode(codecs.decode(hex_code, 'hex'), 'base64').decode().strip()
# Encodes the string so it is safe for URL
url_safe_code = urllib.parse.quote(b64, safe='')
# Adds the key and signed response
authorization = f'Signature keyId="{key}",algorithm="hmac-sha1",headers="date x-mod-nonce",signature="{url_safe_code}"'
headers = {
'Authorization': authorization, # Authorisation header
'Date': formatted_time, # Date header
'x-mod-nonce': nonce, # Adds nonce
'accept': 'application/json',
}
response = requests.get(url, headers=headers)
print(response.text)