# Autentisering och signering av förfrågningar

> Signera API-förfrågningar med HMAC-SHA256 med ditt project UUID och din API key.

Varje API-förfrågan (utom inkommande webhooks) måste innehålla ditt project UUID och en signatur av förfrågan. Signaturen bevisar att förfrågan kommer från dig och att ingen har ändrat den på vägen.

## API-nycklar

2328.io använder **två nycklar** som delar samma signeringsalgoritm men täcker olika endpoints:

| Nyckel | Används för |
|-----|----------|
| **API key** | Betalningar, statiska plånböcker, saldo, växelkurser och verifiering av betalnings- / statisk plånbok-webhooks |
| **Payout API key** | Alla `/v1/payout/*` endpoints och verifiering av uttags-webhooks |

Båda nycklarna finns i dina projektinställningar på [2328.io](https://2328.io). Exemplen nedan säger generiskt "API key" — byt ut till rätt nyckel för den endpoint du anropar.

> **INFO:** Blanda **aldrig** de två nycklarna: att signera en uttagsbegäran med den vanliga API-nyckeln (eller en betalningsbegäran med uttagsnyckeln) returnerar ett signaturfel.

## Obligatoriska headers

| Header | Typ | Obligatorisk | Beskrivning |
|--------|------|----------|-------------|
| `Content-Type` | string | ja | Alltid `application/json` |
| `project` | string | ja | Ditt project UUID |
| `sign` | string | ja | HMAC-SHA256-signatur av förfrågan, beräknad med din API key |
| `User-Agent` | string | ja | Identifierar din applikation (t.ex. `MyShop/1.4 (+https://myshop.example)`). Förfrågningar utan `User-Agent` kan blockeras. |

## Hur signaturen fungerar

Tänk på signaturen som ett fingeravtryck av förfrågans body. Den byggs så här:

1. Serialisera body till JSON (kompakt — inga extra blanksteg).
2. Base64-koda den JSON-strängen. Det här steget normaliserar indata mellan språk — när det väl är ren ASCII producerar varje språk samma byte för HMAC.
3. Beräkna **HMAC-SHA256** av Base64-strängen med din API key och konvertera sedan resultatet till hex med små bokstäver.

För **GET** och andra typer av förfrågningar utan body, signera en tom sträng istället för JSON.

> **INFO:** Signaturen för en tom sträng är konstant för en given API key. Du kan cacha den om du gör många GET-anrop.

## Implementationer

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

### Förfrågningar utan body (GET)

För förfrågningar utan body (t.ex. `GET /v1/payout/status/{uuid}`), signera en tom sträng:

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

## Fullständigt exempel på förfrågan

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

> **DANGER:** **Exponera aldrig din API key i klientkod.** Signera förfrågningar i din backend. En läckt API key ger vem som helst full åtkomst till ditt handlarkonto.

## Verifiera webhook-signaturer

När 2328.io skickar en webhook till dig körs samma algoritm omvänt:

1. Plocka ut fältet `sign` ur payloaden.
2. JSON-koda de återstående fälten (kompakt, utan blanksteg).
3. Base64-koda den strängen.
4. Beräkna `HMAC-SHA256` med rätt nyckel.
5. Jämför den med den mottagna `sign` med en **tidskonstant** jämförelse (`hash_equals`, `crypto.timingSafeEqual`, `hmac.compare_digest`, `subtle.ConstantTimeCompare`, `OpenSSL.fixed_length_secure_compare`).

Signeringsnyckeln beror på webhook-källan:

| Webhook | Nyckel att verifiera med |
|---------|---------------------|
| Betalnings- / statisk plånbok-webhooks (`/v1/payment`, `/v1/static-wallet`) | **API key** |
| Uttags-webhooks (`/v1/payout`) | **Payout API key** |

> **WARNING:** **Vanliga verifieringsfallgropar.** Din JSON-kodare måste producera **exakt samma bytes** som avsändaren producerade — annars skiljer sig Base64 och signaturen matchar inte.

- **Go**: använd `json.NewEncoder` med `SetEscapeHTML(false)`. Standard `json.Marshal` escapar `<`, `>`, `&` till `<` och förstör signaturen.
- **Python**: skicka `ensure_ascii=False` till `json.dumps`. Utan det escapas icke-ASCII (kyrilliska, kinesiska, …) till `\uXXXX`.
- **Kompakt JSON**: inga blanksteg mellan fält (`separators=(",", ":")` i Python).
- **Fältordning** (Go): en vanlig `map[string]any` randomiserar nycklar vid omkodning. Använd `json.RawMessage`, en ordnad struct eller ta bort `sign` ur de råa byten.

Om verifieringen fortsätter att misslyckas, kör `apiSign` på payloaden själv — den måste producera samma hex-sträng som det mottagna `sign`.

> **INFO:** **En giltig signatur förhindrar inte replays.** Den bevisar endast att webhooken kom från 2328.io — den hindrar inte en angripare från att skicka en *fångad* webhook igen senare. Kontrollera alltid idempotens via `uuid` (eller `txid` för statiska plånböcker) innan du krediterar medel. Avvisa med HTTP `401` om signaturen saknas eller är fel.

Fullständiga kodexempel finns på **[Webhook Notifications](/docs/webhooks#verifying-the-signature)**. Återförsökshantering och idempotensregler finns i [Bästa praxis](/docs/webhooks#best-practices).