> ## 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

# Bramka płatnicza: webhooki

> Skonfiguruj webhook bramki płatniczej i dowiaduj się o każdej opłaconej transakcji, zanim klient wróci do Twojego sklepu.

Webhook to sposób, w jaki Paymove aktywnie powiadamia Twój system o zakończonej płatności - bez odpytywania API. Konfiguracja składa się z dwóch kroków: **rejestrujesz webhook**, a następnie **przypisujesz go do produktu** (sklepu). Od tego momentu każda opłacona transakcja w tym sklepie trafia na wskazany przez Ciebie endpoint.

Wszystkie żądania autoryzujesz kluczem API przekazywanym w nagłówku `X-API-KEY`.

<Info>
  Przykłady używają środowiska sandbox: `https://gateway-api.sandbox.paymove.io`. Potrzebujesz `partnerId` oraz `productId` sklepu utworzonego w tutorialu [Bramka płatnicza: podstawowa integracja](/products/payment-gateway/tutorial).
</Info>

## Krok 1: Rejestracja webhooka

W `requestTemplate` definiujesz body żądania, które Paymove wyśle na Twój endpoint. Możesz w nim użyć zmiennych `{{externalId}}` (identyfikator zamówienia z Twojego systemu) i `{{price}}` (kwota) - Paymove podstawi je przy wysyłce.

```bash theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
curl --request POST \
  --url https://gateway-api.sandbox.paymove.io/api/pay/plugin/webhook \
  --header 'Content-Type: application/json' \
  --header 'X-API-KEY: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' \
  --data '{
    "name": "OrderPaymentHook",
    "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": "78562c79-2f5c-4415-8af4-c871eea92ef2",
    "type": "PAYMENT",
    "headers": {
      "Authorization": ["Bearer abc123"],
      "Content-Type": ["application/json"]
    }
  }'
```

| Pole               | Opis                                                                                                 |
| ------------------ | ---------------------------------------------------------------------------------------------------- |
| `name`             | Nazwa webhooka (widoczna w konfiguracji).                                                            |
| `endpoint`         | URL, na który Paymove wysyła żądanie.                                                                |
| `method`           | Metoda HTTP (np. `POST`).                                                                            |
| `requestTemplate`  | Szablon body żądania (zmienne podstawiane przez Paymove).                                            |
| `responseTemplate` | Oczekiwana struktura odpowiedzi od partnera.                                                         |
| `expectedCode`     | Oczekiwany kod HTTP odpowiedzi (np. 200).                                                            |
| `expectedResponse` | Oczekiwana treść odpowiedzi (np. `{ "status": "ok" }`).                                              |
| `retries`          | Liczba ponownych prób przy niepowodzeniu. **Domyślnie `0`** - ustaw jawnie, jeśli chcesz ponawianie. |
| `partnerId`        | Identyfikator partnera (nadawany przez Paymove).                                                     |
| `type`             | Typ zdarzenia - dla powiadomień o płatności: `PAYMENT`.                                              |
| `headers`          | Nagłówki dołączane do żądania (np. `Authorization`, `Content-Type`).                                 |

**Odpowiedź (200):**

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
{
  "id": "6b23ecd9-14c8-47fc-add0-b71ec50e9d66",
  "name": "OrderPaymentHook",
  "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,
  "type": "PAYMENT",
  "headers": {
    "Authorization": ["Bearer abc123"],
    "Content-Type": ["application/json"]
  },
  "signingSecret": "whsec_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}
```

Pole `id` z odpowiedzi to identyfikator webhooka (`webhookId`), którego użyjesz w kroku 2.

<Warning>
  Odpowiedź zawiera `signingSecret` - sekret do weryfikacji podpisu żądań przychodzących od Paymove. Zapisz go bezpiecznie po stronie swojego systemu i nie udostępniaj publicznie.
</Warning>

## Krok 2: Przypisanie webhooka do sklepu

Webhook zacznie działać dopiero po powiązaniu z produktem. W URL podmień `webhookId` (z kroku 1) oraz `productId` swojego sklepu.

```bash theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
curl --request POST \
  --url https://gateway-api.sandbox.paymove.io/api/pay/plugin/webhook/6b23ecd9-14c8-47fc-add0-b71ec50e9d66/products/891412c8-8717-4449-9543-e34112bec470 \
  --header 'X-API-KEY: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
```

**Odpowiedź (200):** obiekt webhooka (taki sam jak w kroku 1), potwierdzający powiązanie.

Od tego momentu każda opłacona płatność w tym sklepie wywołuje Twój `endpoint`.

## Weryfikacja konfiguracji

Listę produktów powiązanych z webhookiem sprawdzisz wywołaniem:

```bash theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
curl --url https://gateway-api.sandbox.paymove.io/api/pay/plugin/webhook/6b23ecd9-14c8-47fc-add0-b71ec50e9d66/products \
  --header 'X-API-KEY: sk_test_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
```

**Odpowiedź (200):** tablica produktów powiązanych z webhookiem:

```json theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
[
  {
    "id": "891412c8-8717-4449-9543-e34112bec470",
    "name": "Sklep Testowy",
    "productType": "PAY",
    "status": "ACTIVE"
  }
]
```

## Jak wygląda powiadomienie o płatności?

Po zakończonej płatności Paymove wysyła na Twój `endpoint` żądanie zgodne z `requestTemplate` i skonfigurowanymi `headers`. Dla szablonu z kroku 1 body wygląda tak:

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

`orderId` to `externalId`, który przekazałeś tworząc płatność - dzięki temu jednoznacznie dopasujesz powiadomienie do zamówienia w swoim systemie.

<Info>
  Powyższy kształt to wynik `requestTemplate`. Gdyby szablon był pusty, Paymove wysłałby pełny obiekt płatności - zestaw pól opisuje [Konfiguracja](/webhooks#payload-webhooka).
</Info>

<Warning>
  Zanim zaufasz treści powiadomienia, **zweryfikuj nagłówek `X-Paymove-Signature`**. Bez tego dowolna osoba znająca Twój `endpoint` może wysłać spreparowane powiadomienie i uzyskać realizację zamówienia bez płatności. Gotowy kod: [Weryfikacja podpisu webhooka](/webhook-signature).
</Warning>

Twój system powinien odpowiedzieć kodem **dokładnie równym** `expectedCode` (zwyczajowo `200` i body `{ "status": "ok" }`, choć treść odpowiedzi nie jest sprawdzana) - dopiero wtedy Paymove uznaje doręczenie za udane. Odpowiedź `201` czy `204` przy `expectedCode: 200` liczy się jako niepowodzenie i **nie jest ponawiana**. Doręczenie zakończone kodem 4xx, 5xx lub błędem sieci zostanie ponowione tyle razy, ile wskazuje `retries` - **domyślnie `0`, czyli ani razu**.

<Tip>
  Realizuj zamówienie po otrzymaniu webhooka, a nie po powrocie klienta na `returnUrl` - klient może zamknąć przeglądarkę zanim wróci do Twojego sklepu, a samo przekierowanie można wywołać bez opłacenia płatności.
</Tip>

## Co dalej?

<CardGroup cols={2}>
  <Card title="Bramka płatnicza: podstawowa integracja" icon="rocket" href="/products/payment-gateway/tutorial">
    Pełny przepływ płatności: produkt, płatność, przekierowanie klienta.
  </Card>

  <Card title="Webhooki" icon="webhook" href="/webhooks">
    Szczegóły konfiguracji powiadomień o płatnościach.
  </Card>
</CardGroup>
