# Uwierzytelnianie i podpisywanie żądań

> Podpisuj żądania API za pomocą HMAC-SHA256, używając project UUID i klucza API.

Każde żądanie API (z wyjątkiem przychodzących webhooków) musi zawierać Twój project UUID oraz podpis żądania. Podpis potwierdza, że żądanie pochodzi od Ciebie i nie zostało zmodyfikowane podczas przesyłania.

## Klucze API

2328.io korzysta z **dwóch kluczy**, które używają tego samego algorytmu podpisywania, ale obsługują różne endpointy:

| Klucz | Stosowany do |
|-------|--------------|
| **API key** | Płatności, statyczne portfele, saldo, kursy walutowe oraz weryfikacja webhooków płatności / statycznych portfeli |
| **Payout API key** | Wszystkie endpointy `/v1/payout/*` oraz weryfikacja webhooków wypłat |

Oba klucze znajdują się w ustawieniach Twojego projektu na [2328.io](https://2328.io). Poniższe przykłady używają nazwy „API key" w sposób ogólny — podstaw odpowiedni klucz dla wywoływanego endpointu.

> **INFO:** **Nigdy** nie mieszaj obu kluczy: podpisanie żądania wypłaty zwykłym kluczem API (lub żądania płatności kluczem wypłaty) zwróci błąd podpisu.

## Wymagane nagłówki

| Nagłówek | Typ | Wymagany | Opis |
|----------|-----|----------|------|
| `Content-Type` | string | tak | Zawsze `application/json` |
| `project` | string | tak | Twój project UUID |
| `sign` | string | tak | Podpis HMAC-SHA256 żądania, obliczony za pomocą Twojego klucza API |
| `User-Agent` | string | tak | Identyfikuje Twoją aplikację (np. `MyShop/1.4 (+https://myshop.example)`). Żądania bez `User-Agent` mogą zostać zablokowane. |

## Jak działa podpis

Podpis można rozumieć jako odcisk palca treści żądania. Tworzony jest poprzez:

1. Serializację treści do formatu JSON (kompaktowo — bez dodatkowych białych znaków).
2. Zakodowanie tego JSON-a w base64. Ten krok normalizuje wejście pomiędzy językami — gdy mamy zwykły ASCII, każdy język produkuje te same bajty dla HMAC.
3. Obliczenie **HMAC-SHA256** ciągu base64 przy użyciu Twojego klucza API, a następnie konwersję wyniku na hex zapisany małymi literami.

Dla żądań **GET** oraz innych typów żądań bez treści podpisuj pusty ciąg zamiast JSON-a.

> **INFO:** Podpis pustego ciągu jest stały dla danego klucza API. Możesz go zbuforować, jeśli wykonujesz wiele wywołań GET.

## Implementacje

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

### Żądania bez treści (GET)

Dla żądań z pustą treścią (np. `GET /v1/payout/status/{uuid}`) podpisuj pusty ciąg:

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

## Pełny przykład żądania

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

> **DANGER:** **Nigdy nie ujawniaj swojego klucza API w kodzie po stronie klienta.** Podpisuj żądania na swoim backendzie. Wyciek klucza API daje każdemu pełny dostęp do Twojego konta sprzedawcy.

## Weryfikacja podpisów webhooków

Gdy 2328.io wysyła webhook, ten sam algorytm jest wykonywany w odwrotnej kolejności:

1. Wyciągnij pole `sign` z payloadu.
2. Zakoduj pozostałe pola jako JSON (kompaktowo, bez białych znaków).
3. Zakoduj ten ciąg w base64.
4. Oblicz `HMAC-SHA256` z odpowiednim kluczem.
5. Porównaj wynik z otrzymanym `sign` przy użyciu porównania **w czasie stałym** (`hash_equals`, `crypto.timingSafeEqual`, `hmac.compare_digest`, `subtle.ConstantTimeCompare`, `OpenSSL.fixed_length_secure_compare`).

Klucz podpisujący zależy od źródła webhooka:

| Webhook | Klucz do weryfikacji |
|---------|----------------------|
| Webhooki płatności / statycznych portfeli (`/v1/payment`, `/v1/static-wallet`) | **API key** |
| Webhooki wypłat (`/v1/payout`) | **Payout API key** |

> **WARNING:** **Typowe pułapki weryfikacji.** Twój enkoder JSON musi produkować **dokładnie te same bajty**, które wyprodukował nadawca — inaczej Base64 będzie różny i podpis się nie zgodzi.

- **Go**: użyj `json.NewEncoder` z `SetEscapeHTML(false)`. Domyślny `json.Marshal` escapeuje `<`, `>`, `&` na `<` i psuje podpis.
- **Python**: przekaż `ensure_ascii=False` do `json.dumps`. Bez tego znaki spoza ASCII (cyrylica, chiński, …) są escapeowane do `\uXXXX`.
- **Kompaktowy JSON**: bez białych znaków między polami (`separators=(",", ":")` w Pythonie).
- **Kolejność pól** (Go): zwykła `map[string]any` randomizuje klucze przy ponownym kodowaniu. Użyj `json.RawMessage`, uporządkowanej struktury lub usuń `sign` z surowych bajtów.

Jeśli weryfikacja nadal zawodzi, uruchom `apiSign` na payloadzie samodzielnie — musi wyprodukować ten sam ciąg szesnastkowy co otrzymane `sign`.

> **INFO:** **Poprawny podpis nie chroni przed replayami.** Dowodzi tylko, że webhook pochodzi z 2328.io — nie zapobiega ponownemu wysłaniu *przechwyconego* webhooka przez atakującego później. Zawsze sprawdzaj idempotentność po `uuid` (lub `txid` dla statycznych portfeli) zanim zaksięgujesz środki. Odrzuć z HTTP `401`, jeśli podpis jest brakujący lub błędny.

Pełne przykłady kodu znajdziesz na **[Webhook Notifications](/docs/webhooks#verifying-the-signature)**. Obsługa ponawiania prób i reguły idempotentności są w [Najlepszych praktykach](/docs/webhooks#best-practices).