Sign in
Introduction/Authentification

Authentification et signature des requêtes

Signez les requêtes API avec HMAC-SHA256 en utilisant votre project UUID et votre API key.

Chaque requête API (à l'exception des webhooks entrants) doit comporter votre project UUID et une signature de requête. La signature prouve que la requête provient bien de vous et que personne ne l'a modifiée en chemin.

Clés API

2328.io utilise deux clés qui partagent le même algorithme de signature mais couvrent des endpoints différents :

CléUtilisée pour
API keyPaiements, portefeuilles statiques, solde, taux de change et vérification des webhooks de paiement / portefeuille statique
Payout API keyTous les endpoints /v1/payout/* et la vérification des webhooks de retrait

Les deux clés se trouvent dans les paramètres de votre projet sur 2328.io. Les exemples ci-dessous utilisent « API key » de manière générique — remplacez-la par la clé appropriée pour l'endpoint que vous appelez.

Ne mélangez jamais les deux clés : signer une requête de retrait avec l'API key classique (ou une requête de paiement avec la Payout API key) renvoie une erreur de signature.

Headers requis

HeaderTypeRequisDescription
Content-TypestringouiToujours application/json
projectstringouiUUID de votre projet
signstringouiSignature HMAC-SHA256 de la requête, calculée avec votre API key
User-AgentstringouiIdentifie votre application (p. ex. MyShop/1.4 (+https://myshop.example)). Les requêtes sans User-Agent peuvent être bloquées.

Comment fonctionne la signature

Considérez la signature comme une empreinte du corps de la requête. Elle est construite ainsi :

  1. Sérialisation du corps en JSON (compact — sans espaces superflus).
  2. Encodage Base64 de ce JSON. Cette étape normalise l'entrée entre les langages — une fois en ASCII pur, chaque langage produit les mêmes octets pour HMAC.
  3. Calcul de HMAC-SHA256 de la chaîne Base64 avec votre API key, puis conversion du résultat en hex minuscule.

Pour les requêtes GET et autres types de requêtes sans corps, signez une chaîne vide à la place du JSON.

La signature de la chaîne vide est constante pour une API key donnée. Vous pouvez la mettre en cache si vous effectuez de nombreux appels GET.

Implémentations

PHP
<?php
function apiSign(array $data, string $apiKey): string {
    $json = json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    $base64 = base64_encode($json);
    return hash_hmac('sha256', $base64, $apiKey);
}

Requêtes sans corps (GET)

Pour les requêtes au corps vide (par exemple GET /v1/payout/status/{uuid}), signez une chaîne vide :

Shell
SIGN=$(printf '' | openssl dgst -sha256 -hmac "$API_KEY" -hex | awk '{print $NF}')

Exemple complet de requête

Shell
curl -X POST https://api.2328.io/api/v1/payment \
  -H "Content-Type: application/json" \
  -H "User-Agent: MyShop/1.0 (+https://myshop.example)" \
  -H "project: YOUR_PROJECT_UUID" \
  -H "sign: YOUR_HMAC_SIGNATURE" \
  -d '{"amount":"100.00","currency":"USD","order_id":"ORDER-123"}'

N'exposez jamais votre API key dans le code côté client. Signez les requêtes sur votre backend. Une API key divulguée donne à n'importe qui un accès complet à votre compte marchand.

Vérification des signatures de webhook

Lorsque 2328.io vous envoie un webhook, le même algorithme s'exécute en sens inverse :

  1. Extrayez le champ sign du payload.
  2. Encodez les champs restants en JSON (compact, sans espaces).
  3. Encodez cette chaîne en Base64.
  4. Calculez HMAC-SHA256 avec la clé appropriée.
  5. Comparez-la au sign reçu en utilisant une comparaison à temps constant (hash_equals, crypto.timingSafeEqual, hmac.compare_digest, subtle.ConstantTimeCompare, OpenSSL.fixed_length_secure_compare).

La clé de signature dépend de la source du webhook :

WebhookClé de vérification
Webhooks de paiement / portefeuille statique (/v1/payment, /v1/static-wallet)API key
Webhooks de retrait (/v1/payout)Payout API key

Pièges courants de vérification. Votre encodeur JSON doit produire exactement les mêmes octets que l'expéditeur — sinon le Base64 diffère et la signature ne correspondra pas.

  • Go : utilisez json.NewEncoder avec SetEscapeHTML(false). Le json.Marshal par défaut échappe <, >, & en < et casse la signature.
  • Python : passez ensure_ascii=False à json.dumps. Sans cela, les caractères non ASCII (cyrillique, chinois, …) sont échappés en \uXXXX.
  • JSON compact : pas d'espaces entre les champs (separators=(",", ":") en Python).
  • Ordre des champs (Go) : un simple map[string]any randomise les clés lors de la ré-encodage. Utilisez json.RawMessage, une struct ordonnée, ou retirez sign des octets bruts.

Si la vérification échoue toujours, exécutez apiSign sur le payload vous-même — il doit produire la même chaîne hexadécimale que le sign reçu.

Une signature valide n'empêche pas les rejeux. Elle prouve seulement que le webhook provient de 2328.io — elle n'empêche pas un attaquant de re-publier un webhook capturé plus tard. Vérifiez toujours l'idempotence via uuid (ou txid pour les wallets statiques) avant de créditer des fonds. Rejetez avec HTTP 401 si la signature est absente ou incorrecte.

Les exemples de code complets sont sur Webhook Notifications. La gestion des retentatives et les règles d'idempotence sont dans Bonnes pratiques.