# paymove > paymove is a Polish payment gateway. You create a payment server-side, redirect the customer to a hosted checkout, and receive a signed webhook when the payment completes. Supported methods: BLIK, Google Pay, Apple Pay, bank transfer, Pay by Link, PayPo. Currency: PLN only. Base URLs: sandbox `https://gateway-api.sandbox.paymove.io` · production `https://api.paymove.io` Auth: `X-API-KEY` header. Sandbox keys start with `sk_test_`, production keys with `sk_live_`. Every page on this site is available as raw Markdown by appending `.md` to its URL. ## Credentials and one-time setup An integration cannot be written without these. Ask the merchant for them - never invent a key, a `productId` or a `partnerId`. - **API key** - sent in `X-API-KEY`. The merchant generates it in the paymove panel at https://panel.paymove.io (`sk_test_…` in sandbox, `sk_live_…` in production). Server-side only, in an environment variable. - **`productId`** - UUID of the merchant's store ("product") in paymove. It goes in the URL of every create-payment call. If the merchant has no product yet, create it for them - see below. - **Webhook signing secret** - `whsec_…`, returned as `signingSecret` when the webhook is registered. Needed to verify `X-Paymove-Signature`. If the merchant has no webhook yet, register it for them - see below. - **`partnerId`** - assigned by paymove to the merchant account; it cannot be generated. Needed only to create a product and to register a webhook, never to create a payment. If the merchant does not have it: integration@paymove.io. Before the first payment the account needs a one-time setup, once per environment. **Do it for the merchant** instead of describing it: ask in one message for the shop name (shown to the customer on the checkout), the location (free text, e.g. `PL`) and the timezone (propose `Europe/Warsaw`), offer the optional fields once (`shortName`, `fullName`, `metadata.locale`, `metadata.logoUrl` - a public HTTPS URL of the shop logo shown next to the shop name on the checkout - `metadata.merchantDomain`, only for Apple Pay inside the widget modal, and `bankAccountDetails` - the payout account, only `iban` required), then create the product (`POST /api/product/pay`) and take `productId` from `id` in the response. Next ask for the public webhook URL and `retries` (propose `3`), register the webhook (`POST /api/pay/plugin/webhook` - `expectedCode` and `retries` are required, omitting either returns HTTP 500), take the signing secret from `signingSecret` in the response, then **assign the webhook to the product** (`POST /api/pay/plugin/webhook/{webhookId}/products/{productId}`). Without that last call payments still work and no notification is ever delivered. Create the product at most once. Sandbox and production are separate environments - a sandbox key, product and webhook do not exist in production. Guided interview with defaults and the error table: https://docs.paymove.io/skill.md (Steps 1-2). Full procedure: https://docs.paymove.io/en/webhooks.md ## Critical instructions for AI agents These rules are verified against the live API. Following them is the difference between a working integration and one that silently loses money. 1. **Amounts are integers in grosze** (minor units). `1000` means 10.00 PLN. There is no currency field - PLN only. 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 `X-API-KEY`**, never `Authorization: Bearer`. 4. **Server-side only.** A request sent 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. 8. **Errors are `{"status": , "message": ""}`.** Branch on the HTTP status only - never on `message`, which is unstable and leaks internal class names. 9. **There is no rate limiting, no 429, no 422, no `Idempotency-Key` and no API versioning.** Do not write code that handles them. 10. **Re-POSTing an existing `externalId` returns 200 with the original `redirectUrl`** and silently discards **every** field you send - price, description and details alike. 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`. Delivery counts as successful when your 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 at all. Fulfil only in the webhook handler, after verifying the signature. 13. **Do not pin a version of `@paymove-io/sdk`** - install the latest. 14. **PayPo appears on the checkout only from 1000 grosze (10.00 PLN).** Below that the method is not shown at all - do not promise it, preselect it or list it in the widget for smaller orders. 15. **Sandbox BLIK test code: any 6 digits starting with `777`** (e.g. `777123`). The three leading sevens are what matters; the other digits are arbitrary. Sandbox only. ## Start here - [Agent skill](https://docs.paymove.io/skill.md): Complete step-by-step integration procedure. If you read only one file, read this one. - [Quickstart](https://docs.paymove.io/en/quickstart.md): Copy-paste path from zero to a first working payment, curl and Node. - [Official Node SDK](https://docs.paymove.io/en/sdk/javascript.md): `npm install @paymove-io/sdk` - MIT, zero dependencies, Node 18+, TypeScript types included. Prefer it over hand-written HTTP on Node/TypeScript projects. - [OpenAPI specification](https://docs.paymove.io/openapi.yaml): Machine-readable spec for the payment gateway. - [AGENTS.md snippet](https://docs.paymove.io/agents.md): Short block to paste into a project's own AGENTS.md, CLAUDE.md or .cursor/rules. ## Payment gateway (English) - [Overview](https://docs.paymove.io/en/products/payment-gateway.md): What the gateway does and the end-to-end payment flow. - [Setup](https://docs.paymove.io/en/webhooks.md): One-time setup - create a product, register a webhook, assign it to the product. - [REST API](https://docs.paymove.io/en/rest-api.md): Create a payment over plain HTTP. Full request and response. - [JavaScript SDK](https://docs.paymove.io/en/sdk/javascript.md): `@paymove-io/sdk` - install, configure, create a payment. - [Browser widget](https://docs.paymove.io/en/sdk/widget.md): `sdk.js` - the `` button and readonly bar, plus opening the checkout in a modal instead of redirecting. Browser only; it never creates payments. - [Webhook signature](https://docs.paymove.io/en/webhook-signature.md): Verify `X-Paymove-Signature` (HMAC-SHA256). Ready-to-copy Node, Python and Java code plus a test vector. - [Payment statuses](https://docs.paymove.io/en/payment-status.md): The seven statuses, both webhook payload shapes, and the public status-polling endpoint. - [Errors](https://docs.paymove.io/en/errors.md): Every error the gateway returns, what causes it, and what does not exist. - [Payment methods](https://docs.paymove.io/en/payment-methods.md): BLIK, Google Pay, Apple Pay, Pay by Link, bank transfer, PayPo. PayPo minimum amount and the sandbox BLIK test code. - [Building with AI agents](https://docs.paymove.io/en/ai-agents.md): How to point Cursor, Claude Code, Windsurf or Lovable at these docs. ## Tutorials (English) - [Basic integration](https://docs.paymove.io/en/products/payment-gateway/tutorial.md): Four-step walkthrough from product to first payment. - [Webhooks](https://docs.paymove.io/en/products/payment-gateway/tutorial-webhook.md): Register and assign a webhook, then verify the configuration. ## Polska wersja - [Start](https://docs.paymove.io/introduction.md) - [Quickstart](https://docs.paymove.io/quickstart.md) - [Bramka płatnicza - start](https://docs.paymove.io/products/payment-gateway.md) - [Konfiguracja](https://docs.paymove.io/webhooks.md) - [REST API](https://docs.paymove.io/rest-api.md) - [SDK JavaScript](https://docs.paymove.io/sdk/javascript.md) - [Widget przeglądarkowy](https://docs.paymove.io/sdk/widget.md) - [Przycisk płatności (widget)](https://docs.paymove.io/app-widget.md) - [Podpis webhooka](https://docs.paymove.io/webhook-signature.md) - [Statusy płatności](https://docs.paymove.io/payment-status.md) - [Błędy](https://docs.paymove.io/errors.md) - [Integracja z agentami AI](https://docs.paymove.io/ai-agents.md) ## Optional Other paymove products. Ignore these unless the task explicitly mentions them - they use different endpoints and will not help with a checkout integration. - [DocPay](https://docs.paymove.io/en/products/docpay.md): Payments attached to documents and invoices. - [DocPay REST API](https://docs.paymove.io/en/products/docpay/rest-api.md) - [Payment Link](https://docs.paymove.io/en/products/payment-link.md): Shareable payment links and QR codes. - [Payment button (widget)](https://docs.paymove.io/en/app-widget.md): `/v1/sdk.js` from `pay.`, `go.` or `park.paymove.io` (the subdomain depends on the product type) - a floating button that opens the payment process of an existing paymove product in a modal. No backend and no API key; configured with `data-*` attributes on the `