# 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 key** | Paiements, portefeuilles statiques, solde, taux de change et vérification des webhooks de paiement / portefeuille statique |
| **Payout API key** | Tous 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](https://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.

> **INFO:** **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

| Header | Type | Requis | Description |
|--------|------|--------|-------------|
| `Content-Type` | string | oui | Toujours `application/json` |
| `project` | string | oui | UUID de votre projet |
| `sign` | string | oui | Signature HMAC-SHA256 de la requête, calculée avec votre API key |
| `User-Agent` | string | oui | Identifie 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.

> **INFO:** 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

<CodeSnippet name="apiSign" langs="php,js,ts,python,go" />

### Requêtes sans corps (GET)

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

<CodeSnippet name="apiSignBodyless" langs="curl,php,js,ts,python,go" />

## Exemple complet de requête

<CodeSnippet name="fullRequestExample" langs="curl,php,js,ts,python,go" />

> **DANGER:** **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 :

| Webhook | Clé de vérification |
|---------|---------------------|
| Webhooks de paiement / portefeuille statique (`/v1/payment`, `/v1/static-wallet`) | **API key** |
| Webhooks de retrait (`/v1/payout`) | **Payout API key** |

> **WARNING:** **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.

> **INFO:** **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](/docs/webhooks#verifying-the-signature)**. La gestion des retentatives et les règles d'idempotence sont dans [Bonnes pratiques](/docs/webhooks#best-practices).