> ## Documentation Index
> Fetch the complete documentation index at: https://docs.paymove.io/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> ## paymove integration rules for AI agents
>
> These rules are verified against the live API. Follow them exactly.
>
> 1. Amounts are INTEGERS in grosze (minor units). `1000` means 10.00 PLN. PLN is the only supported currency and there is no currency field in the API.
> 2. A float `price` (e.g. `12.99`) is SILENTLY TRUNCATED to 12 grosze and still returns HTTP 200. Always send an integer: `Math.round(pln * 100)`.
> 3. Authenticate with the `X-API-KEY` header. Never `Authorization: Bearer`. Sandbox keys start with `sk_test_`, production keys with `sk_live_`.
> 4. Server-side only. A call from a merchant page is rejected. Never put the key in frontend code.
> 5. Unknown request fields are silently ignored and still return HTTP 200 — a wrong body shape looks like success. Match the documented shape exactly.
> 6. A missing `externalId` returns HTTP 500 `Something went wrong`, not 400. A missing `price` or `details.returnUrl` returns HTTP 200 and a usable `redirectUrl` — no error at all. Validate the body yourself before sending it.
> 7. Always verify the `X-Paymove-Signature` header on incoming webhooks before trusting them: https://docs.paymove.io/en/webhook-signature.md
> 8. Errors are `{"status": <int>, "message": "<text>"}`. Branch on the HTTP status only — never on `message`, which is unstable and leaks internal class names.
> 9. There is no rate limiting, no HTTP 429, no HTTP 422, no `Idempotency-Key` header and no API versioning. Do not write code that handles them.
> 10. Re-POSTing an `externalId` that already exists returns HTTP 200 with the ORIGINAL `redirectUrl` and silently discards EVERY field you send — the new price, description and details are all ignored. Use `PATCH /api/pay/product/{productId}/subproduct/{externalId}` to change a price.
> 11. Webhooks fire only on `COMPLETED` by default, and `retries` defaults to `0` (no retries) unless you set it explicitly. Delivery counts as successful when the HTTP status equals `expectedCode` — the response body is never inspected.
> 12. Never fulfil an order on the `returnUrl` redirect. The checkout does not redirect there by itself: the customer has to click "back to shop", and inside the widget modal that redirect never happens. Fulfil only in the webhook handler, after verifying the signature.
> 13. Do not pin a version of `@paymove-io/sdk` — install the latest.
> 14. Full documentation index: https://docs.paymove.io/llms.txt · Copy-paste quickstart: https://docs.paymove.io/en/quickstart.md · Agent skill: https://docs.paymove.io/skill.md

# Konfiguracja

> Jednorazowa konfiguracja bramki płatniczej: utworzenie produktu, rejestracja webhooka i przypisanie go do produktu.

Integracja z bramką płatniczą Paymove składa się z czterech kroków: tworzysz produkt, rejestrujesz webhook, przypisujesz go do produktu, a następnie tworzysz płatności przez SDK lub REST API.

***

## 1. Utworzenie produktu

Produkt reprezentuje Twój sklep w systemie Paymove. Wszystkie płatności i subprodukty są tworzone w ramach tego produktu.

**Przykładowy request tworzenia produktu:**

```
POST https://gateway-api.sandbox.paymove.io/api/product/pay
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "partnerId": "{{partnerId}}",
  "productType": "PAY",
  "id": "2f6c19e8-84a7-4f50-b950-8d5a05e0bbf2",
  "name": "MerchantShop",
  "fullName": "Merchant Shop Sp. z o.o.",
  "shortName": "MShop",
  "location": "PL",
  "timezone": "Europe/Warsaw",
  "productMetadata": {
    "locale": "pl-PL"
  }
}
```

| Pole                     | Typ      | Opis                                                        |
| ------------------------ | -------- | ----------------------------------------------------------- |
| `partnerId`              | `uuid`   | Identyfikator partnera (wewnętrzny, nadawany przez Paymove) |
| `productType`            | `enum`   | Typ produktu -  `PAY` dla bramki płatniczej                 |
| `id`                     | `uuid`   | UUID produktu                                               |
| `name`                   | `string` | Nazwa skrócona produktu                                     |
| `fullName`               | `string` | Pełna nazwa firmy                                           |
| `shortName`              | `string` | Nazwa wyświetlana                                           |
| `location`               | `string` | Lokalizacja (kod kraju)                                     |
| `timezone`               | `string` | Strefa czasowa IANA (np. `Europe/Warsaw`)                   |
| `productMetadata.locale` | `string` | Opcjonalnie -  domyślny język checkoutu (np. `pl-PL`)       |

***

## 2. Rejestracja webhooka

Webhook to adres URL po Twojej stronie, który Paymove wywołuje po każdej zmianie statusu płatności. Dzięki temu nie musisz ręcznie sprawdzać statusu - zamówienia mogą być realizowane automatycznie. Lista możliwych statusów: [tabela statusów](#jak-działa-webhook).

**Przykładowy request rejestracji webhooka:**

```
POST https://gateway-api.sandbox.paymove.io/api/pay/plugin/webhook
```

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "name": "PaymentSuccessHook",
  "endpoint": "https://merchant-shop.com/api/payments/webhook",
  "method": "POST",
  "requestTemplate": {
    "orderId": "{{externalId}}",
    "price": "{{price}}"
  },
  "responseTemplate": {
    "status": "ok"
  },
  "expectedCode": 200,
  "expectedResponse": "{ \"status\": \"ok\" }",
  "retries": 3,
  "partnerId": "{{partnerId}}",
  "type": "PAYMENT",
  "headers": {
    "Content-Type": ["application/json"]
  }
}
```

| Pole               | Typ       | Opis                                                                                                                                                       |
| ------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`             | `string`  | Nazwa webhooka                                                                                                                                             |
| `endpoint`         | `string`  | URL, na który Paymove wyśle powiadomienie po udanej płatności                                                                                              |
| `method`           | `enum`    | Metoda HTTP (`POST`)                                                                                                                                       |
| `requestTemplate`  | `object`  | Szablon payloadu -  podstawiane są zmienne odpowiadające polom domyślnego payloadu, m.in. `{{externalId}}`, `{{price}}`, `{{status}}`, `{{paymentMethod}}` |
| `responseTemplate` | `object`  | Oczekiwana struktura odpowiedzi od merchanta                                                                                                               |
| `expectedCode`     | `integer` | Oczekiwany kod HTTP odpowiedzi (`200`). **Pole jest wymagane** - jego pominięcie kończy się błędem `500`                                                   |
| `expectedResponse` | `string`  | Oczekiwana odpowiedź jako string JSON. Zapisywana przy webhooku, ale **nieporównywana** z faktyczną odpowiedzią                                            |
| `retries`          | `integer` | Liczba ponownych prób przy niepowodzeniu. **Pole jest wymagane** - jego pominięcie kończy się błędem `500`. Wartość `0` oznacza brak ponawiania            |
| `partnerId`        | `uuid`    | Identyfikator partnera (wewnętrzny)                                                                                                                        |
| `type`             | `enum`    | Typ zdarzenia (`PAYMENT`)                                                                                                                                  |
| `headers`          | `object`  | Nagłówki HTTP dołączane do webhooka                                                                                                                        |

### Jak działa webhook

<Warning>
  **Domyślnie webhook jest wysyłany wyłącznie dla statusu `COMPLETED`.** Powiadomienia o pozostałych statusach (`CANCELED`, `ERROR`, `REFUNDED`) wymagają włączenia po stronie Paymove dla konkretnego produktu - napisz na [integration@paymove.io](mailto:integration@paymove.io).
</Warning>

Statusy płatności w Paymove:

| Status                        | Znaczenie                                                    |
| ----------------------------- | ------------------------------------------------------------ |
| `INITIALIZED`                 | Płatność utworzona, klient jeszcze nie zapłacił              |
| `PENDING`                     | Płatność w toku po stronie operatora                         |
| `COMPLETED`                   | Płatność zakończona sukcesem - **realizuj zamówienie**       |
| `CANCELED`                    | Klient anulował płatność                                     |
| `ERROR`                       | Płatność nie powiodła się                                    |
| `REFUNDED`                    | Płatność zwrócona                                            |
| `WAITING_FOR_EXTERNAL_ACTION` | Stan przejściowy wewnątrz Paymove - nie jest stanem końcowym |

<Info>
  Status jest zawsze przesyłany jako **łańcuch znaków** (np. `"COMPLETED"`). Liczby spotykane w starszych integracjach są wewnętrzną reprezentacją bazodanową i nie pojawiają się w API ani w webhookach.
</Info>

### Payload webhooka

Kształt payloadu zależy od tego, czy ustawiłeś `requestTemplate`.

**Bez `requestTemplate`** Paymove wysyła pełny obiekt (pola puste są pomijane):

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "id": "891412c8-8717-4449-9543-e34112bec470",
  "name": "MerchantShop",
  "fullName": "Merchant Shop Sp. z o.o.",
  "shortName": "MShop",
  "location": "PL",
  "externalId": "order-123",
  "orderId": "PAY1784798914400",
  "price": 950,
  "status": "COMPLETED",
  "paymentMethod": "BLIK",
  "email": "klient@example.com",
  "date": 1783246791.745352526,
  "requestId": "8f2b1c44-0d7e-4a91-b2c3-5e7f9a1d3c60"
}
```

<Warning>
  W domyślnym payloadzie `price` to kwota **pomniejszona o prowizję Paymove**, a nie kwota pobrana od klienta. Jeśli weryfikujesz zgodność kwoty, porównuj ją z wartością zapisaną u siebie przy tworzeniu płatności, a nie z tym polem.
</Warning>

**Z `requestTemplate`** payload to dokładnie to, co wyrenderuje Twój szablon. Dla szablonu z punktu 2:

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "orderId": "order-123",
  "price": "1000"
}
```

| Pole      | Typ      | Opis                                                     |
| --------- | -------- | -------------------------------------------------------- |
| `orderId` | `string` | Wartość `{{externalId}}` - Twój identyfikator zamówienia |
| `price`   | `string` | Wartość `{{price}}` - kwota w groszach (jako string)     |

### Weryfikacja podpisu

Każde żądanie webhooka zawiera nagłówek `X-Paymove-Signature`. **Zweryfikuj go, zanim zaufasz treści** - bez tego każdy, kto zna Twój URL, może podszyć się pod Paymove i zrealizować zamówienie bez płatności.

Gotowy kod w Node.js, Pythonie i Javie: [Weryfikacja podpisu webhooka](/webhook-signature).

### Wymagana odpowiedź

Twój serwer musi odpowiedzieć kodem HTTP równym `expectedCode`. Przyjęło się zwracać przy tym:

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{ "status": "ok" }
```

<Warning>
  O powodzeniu doręczenia decyduje **wyłącznie kod HTTP**, i musi być **dokładnie równy** `expectedCode`. Przy domyślnym `200` odpowiedź `201` lub `204` jest traktowana jako niepowodzenie - i, co gorsza, **nie jest ponawiana**: harmonogram ponowień uruchamiają tylko kody 4xx, 5xx i błędy sieci. Zły kod z rodziny 2xx bezpowrotnie gubi powiadomienie.
</Warning>

<Info>
  Treść odpowiedzi **nie jest w ogóle sprawdzana**. Pole `expectedResponse` jest zapisywane przy webhooku i zwracane w odpowiedzi API, ale nigdy nie jest porównywane z tym, co zwróci Twój serwer - możesz odesłać dowolne body.
</Info>

Jeśli doręczenie się nie powiedzie, webhook zostanie ponowiony tyle razy, ile wskazuje `retries` - **domyślnie `0`, czyli ani razu**. Przy ustawionym `retries` kolejne próby następują po 1 s, 5 s, 5 min, 1 h, a następnie co 3 h.

<Info>
  Wywołania webhooka nie mają limitu czasu po stronie Paymove. Odpowiadaj natychmiast, a właściwe przetwarzanie zamówienia wykonuj asynchronicznie - inaczej wolny endpoint będzie blokował przetwarzanie.
</Info>

***

## 3. Przypisanie webhooka do produktu

Po utworzeniu produktu i zarejestrowaniu webhooka należy je ze sobą powiązać. Dzięki temu każde zdarzenie płatności dotyczące tego produktu automatycznie trafia pod wskazany URL webhooka.

**Przykładowy request:**

```
POST https://gateway-api.sandbox.paymove.io/api/pay/plugin/webhook/{webhookId}/products/{productId}
```

| Parametr    | Typ    | Opis                                         |
| ----------- | ------ | -------------------------------------------- |
| `webhookId` | `uuid` | Identyfikator webhooka otrzymany w punkcie 2 |
| `productId` | `uuid` | UUID produktu utworzonego w punkcie 1        |

***

## 4. Tworzenie płatności

Po skonfigurowaniu produktu i webhooka możesz zacząć tworzyć płatności. Wybierz metodę integracji:

<CardGroup cols={2}>
  <Card title="SDK" icon="npm" href="/sdk/javascript">
    Pakiet Node.js -  zainicjalizuj klienta i wywołaj `createPayment`.
  </Card>

  <Card title="REST API" icon="code" href="/rest-api">
    Bezpośrednie wywołania HTTP -  działa z każdym językiem.
  </Card>
</CardGroup>
