Authentification
Tous les endpoints /v2 exigent une signature de requête en HMAC-SHA256.
La clé API avec laquelle vous signez porte également des scopes (resource:action), le périmètre fin
de la clé ; un endpoint répond 403 (code: INSUFFICIENT_SCOPE) si la clé n’a pas celui qu’il exige.
Le catalogue des scopes se trouve dans la référence de l’API, sur Create an API key : c’est de la
référence, et il évolue avec le produit.
Chaque requête doit comporter trois en-têtes :
| En-tête | Exemple |
|---|---|
X-CowriePay-Key |
cpk_live_xxxxxxxxxxxxxxxxxxxx |
X-CowriePay-Timestamp |
1716825600 (horodatage Unix, en secondes) |
X-CowriePay-Signature |
a1b2c3d4... (64 caractères hexadécimaux) |
Construire la signature
Section intitulée « Construire la signature »La signature est le HMAC-SHA256 d’une chaîne unique dont les parties sont jointes par des points
littéraux (.) :
signing_string = TIMESTAMP + "." + METHOD + "." + PATH + "." + BODY_SHA256signature = hex( HMAC_SHA256(secret, signing_string) )Où :
TIMESTAMP, le même horodatage Unix (en secondes) que celui envoyé dansX-CowriePay-TimestampMETHOD, la méthode HTTP en majuscules :GET,POST,PATCH,DELETEPATH, la cible de la requête exactement telle qu’envoyée, préfixe/v2et chaîne de requête compris. Par exemple/v2/wallets?page=2&status=ACTIVEBODY_SHA256, le SHA-256 du corps brut de la requête, en hexadécimal minuscule. Pour une requête sans corps (GET, DELETE), utilisez le SHA-256 de la chaîne vide :e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
La signature est en hexadécimal minuscule et s’envoie telle quelle dans X-CowriePay-Signature,
sans préfixe. Comme c’est le corps qui est haché, ni l’ordre des clés JSON ni les espaces n’ont
d’importance : hachez exactement les octets que vous envoyez.
Exemples de code
Section intitulée « Exemples de code »const crypto = require('crypto');
function sign(method, path, body, apiKeyId, apiSecret) { const timestamp = Math.floor(Date.now() / 1000).toString(); const bodyHash = crypto.createHash('sha256').update(body ?? '').digest('hex'); const signingString = [timestamp, method.toUpperCase(), path, bodyHash].join('.'); const signature = crypto.createHmac('sha256', apiSecret).update(signingString).digest('hex'); return { 'X-CowriePay-Key': apiKeyId, 'X-CowriePay-Timestamp': timestamp, 'X-CowriePay-Signature': signature, };}
// POST avec un corps : signez et envoyez LA MEME chaine.const body = JSON.stringify({ chain: 'TRON', asset: 'USDT_TRON' });const headers = sign('POST', '/v2/wallets', body, 'cpk_live_xxxxxxxxxxxxxxxxxxxx', 'your_secret');
// GET / DELETE, sans corps : passez '' (et incluez la chaine de requete dans le chemin) :// sign('GET', '/v2/wallets?page=1&limit=20', '', 'cpk_live_xxx', 'your_secret');import hashlib, hmac, time
def sign(method, path, body, api_key_id, api_secret): timestamp = str(int(time.time())) body_hash = hashlib.sha256((body or '').encode()).hexdigest() signing_string = '.'.join([timestamp, method.upper(), path, body_hash]) signature = hmac.new( api_secret.encode(), signing_string.encode(), hashlib.sha256 ).hexdigest() return { 'X-CowriePay-Key': api_key_id, 'X-CowriePay-Timestamp': timestamp, 'X-CowriePay-Signature': signature, }TIMESTAMP=$(date +%s)BODY='{"chain":"TRON","asset":"USDT_TRON"}'BODY_HASH=$(printf '%s' "$BODY" | openssl dgst -sha256 | sed 's/^.*= //')SIGNING_STRING="$TIMESTAMP.POST./v2/wallets.$BODY_HASH"SIG=$(printf '%s' "$SIGNING_STRING" | openssl dgst -sha256 -hmac "your_secret" | sed 's/^.*= //')
curl -X POST https://api.cowriepay.io/v2/wallets \ -H "Content-Type: application/json" \ -H "X-CowriePay-Key: cpk_live_xxxxxxxxxxxxxxxxxxxx" \ -H "X-CowriePay-Timestamp: $TIMESTAMP" \ -H "X-CowriePay-Signature: $SIG" \ -d "$BODY"Erreurs courantes
Section intitulée « Erreurs courantes »Toute réponse d’erreur a la forme { "error": "<message>", "code": "<CODE_MACHINE>" }. Branchez-vous
sur code, jamais sur le texte du message. Les codes propres à l’authentification :
| code | HTTP | Cause | Correctif |
|---|---|---|---|
MISSING_AUTH_HEADERS |
401 | Un en-tête X-CowriePay-* obligatoire est absent |
Envoyez les trois en-têtes |
TIMESTAMP_EXPIRED |
401 | Décalage d’horloge supérieur à 5 minutes | Synchronisez l’horloge de votre serveur (NTP) |
INVALID_SIGNATURE |
401 | Mauvais secret, ou chaîne de signature mal construite | Journalisez la chaîne de signature exacte ; vérifiez que vous avez haché le corps brut et inclus /v2 ainsi que la chaîne de requête dans PATH |
REPLAY_DETECTED |
401 | La même signature a été envoyée deux fois en moins de 5 minutes | Utilisez un horodatage neuf à chaque requête |
INVALID_API_KEY |
401 | Clé inconnue ou révoquée | Vérifiez l’identifiant de la clé et qu’elle n’est pas révoquée |
INSUFFICIENT_SCOPE |
403 | La clé n’a pas le scope requis | Créez une clé portant le scope nécessaire |