# Autenticazione e Firma delle Richieste

> Firma le richieste API con HMAC-SHA256 utilizzando il tuo project UUID e l'API key.

Ogni richiesta API (eccetto i webhook in entrata) deve contenere il tuo project UUID e una firma della richiesta. La firma dimostra che la richiesta proviene da te e che nessuno l'ha modificata lungo il percorso.

## API key

2328.io utilizza **due chiavi** che condividono lo stesso algoritmo di firma ma coprono endpoint differenti:

| Chiave | Utilizzata per |
|-----|----------|
| **API key** | Pagamenti, wallet statici, saldo, tassi di cambio e verifica dei webhook di pagamento / wallet statico |
| **Payout API key** | Tutti gli endpoint `/v1/payout/*` e la verifica dei webhook di prelievo |

Entrambe le chiavi si trovano nelle impostazioni del progetto su [2328.io](https://2328.io). Negli esempi seguenti viene indicato genericamente "API key" — sostituiscila con quella corretta in base all'endpoint che stai chiamando.

> **INFO:** **Non** mescolare mai le due chiavi: firmare una richiesta di prelievo con l'API key normale (o una richiesta di pagamento con la payout key) restituisce un errore di firma.

## Header obbligatori

| Header | Tipo | Obbligatorio | Descrizione |
|--------|------|----------|-------------|
| `Content-Type` | string | sì | Sempre `application/json` |
| `project` | string | sì | Il tuo project UUID |
| `sign` | string | sì | Firma HMAC-SHA256 della richiesta, calcolata con la tua API key |
| `User-Agent` | string | sì | Identifica la tua applicazione (es. `MyShop/1.4 (+https://myshop.example)`). Le richieste senza `User-Agent` possono essere bloccate. |

## Come funziona la firma

Pensa alla firma come a un'impronta digitale del body della richiesta. Viene costruita così:

1. Serializzando il body in JSON (compatto — senza spazi extra).
2. Codificando in Base64 quel JSON. Questo passaggio normalizza l'input tra i linguaggi — una volta che è ASCII puro, ogni linguaggio produce gli stessi byte per HMAC.
3. Calcolando **HMAC-SHA256** della stringa Base64 utilizzando la tua API key, quindi convertendo il risultato in hex minuscolo.

Per le richieste **GET** e gli altri tipi senza body, firma una stringa vuota invece del JSON.

> **INFO:** La firma della stringa vuota è costante per una determinata API key. Puoi memorizzarla in cache se effettui molte chiamate GET.

## Implementazioni

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

### Richieste senza body (GET)

Per le richieste con body vuoto (es. `GET /v1/payout/status/{uuid}`), firma una stringa vuota:

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

## Esempio completo di richiesta

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

> **DANGER:** **Non esporre mai la tua API key nel codice lato client.** Firma le richieste sul tuo backend. Una API key compromessa concede a chiunque pieno accesso al tuo account merchant.

## Verifica delle firme dei webhook

Quando 2328.io ti invia un webhook, viene eseguito lo stesso algoritmo in senso inverso:

1. Estrai il campo `sign` dal payload.
2. Codifica in JSON i campi rimanenti (compatto, senza spazi).
3. Codifica in Base64 quella stringa.
4. Calcola `HMAC-SHA256` con la chiave appropriata.
5. Confrontala con il `sign` ricevuto utilizzando un confronto **a tempo costante** (`hash_equals`, `crypto.timingSafeEqual`, `hmac.compare_digest`, `subtle.ConstantTimeCompare`, `OpenSSL.fixed_length_secure_compare`).

La chiave di firma dipende dalla sorgente del webhook:

| Webhook | Chiave per la verifica |
|---------|---------------------|
| Webhook di pagamento / wallet statico (`/v1/payment`, `/v1/static-wallet`) | **API key** |
| Webhook di prelievo (`/v1/payout`) | **Payout API key** |

> **WARNING:** **Errori comuni di verifica.** Il tuo encoder JSON deve produrre **gli stessi byte identici** prodotti dal mittente — altrimenti il Base64 differisce e la firma non corrisponderà.

- **Go**: usa `json.NewEncoder` con `SetEscapeHTML(false)`. Il `json.Marshal` predefinito esegue l'escape di `<`, `>`, `&` in `<` e rompe la firma.
- **Python**: passa `ensure_ascii=False` a `json.dumps`. Senza, i caratteri non ASCII (cirillico, cinese, …) vengono fatti escape come `\uXXXX`.
- **JSON compatto**: nessuno spazio tra i campi (`separators=(",", ":")` in Python).
- **Ordine dei campi** (Go): un semplice `map[string]any` randomizza le chiavi alla ri-codifica. Usa `json.RawMessage`, una struct ordinata, oppure rimuovi `sign` dai byte grezzi.

Se la verifica continua a fallire, esegui tu stesso `apiSign` sul payload — deve produrre la stessa stringa esadecimale del `sign` ricevuto.

> **INFO:** **Una firma valida non previene i replay.** Dimostra solo che il webhook proviene da 2328.io — non impedisce a un attaccante di ripubblicare in seguito un webhook *catturato*. Verifica sempre l'idempotenza tramite `uuid` (o `txid` per i wallet statici) prima di accreditare i fondi. Rifiuta con HTTP `401` se la firma è mancante o errata.

Gli esempi di codice completi sono su **[Webhook Notifications](/docs/webhooks#verifying-the-signature)**. La gestione dei retry e le regole di idempotenza sono in [Best practice](/docs/webhooks#best-practices).