> ## 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. Registering a webhook requires both `retries` and `expectedCode` - omitting either returns HTTP 500, and `retries: 0` means no retry at all. 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. If the merchant has no `productId` yet, do not stop at a placeholder: ask for the API key, the `partnerId` and the shop name, propose defaults for `location` and `timezone` (`Europe/Warsaw`), offer the optional fields once, then create the product for them with `POST /api/product/pay` and take `productId` from `id` in the response. Do the same for the webhook: register it, then assign it to the product. Guided procedure: https://docs.paymove.io/skill.md (Steps 1-2).
> 15. 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

# Start

> paymove payment button - a widget that embeds the payment flow for your product on your website with a single script. No backend and no API key.

The payment button is a widget you embed on your website with a single `sdk.js` file. A floating paymove button appears on your page - the customer clicks it and - without leaving the page - goes through the whole payment flow for your product in a modal: from the choice, through the payment, to the confirmation.

The widget works with a product configured in paymove. The flow steps, prices and payment methods come from the product configuration - the customer sees in the modal exactly what they would see on the payment page opened from a link or a QR code.

<Frame caption="The widget button with a bubble in the bottom-right corner of the page">
  <img src="https://mintcdn.com/paymove/ztZW1d97Uc2P1tsz/images/app-widget/en/page.png?fit=max&auto=format&n=ztZW1d97Uc2P1tsz&q=85&s=c8a9e2c2b5cb5d11d30dca0a8af247ee" alt="A page with the “Pay online” widget button and the “Quick payment” bubble in the bottom-right corner" width="2200" height="1280" data-path="images/app-widget/en/page.png" />
</Frame>

## Quick start

<Steps>
  <Step title="Get the product ID">
    You need the `productId` of an existing product - the UUID from the create-product response (the `id` field) or from the [paymove panel](https://panel.paymove.io). Instead of the UUID you can use the product's public alias (`shortName`).
  </Step>

  <Step title="Paste the script into your page">
    One tag in `<head>` or at the end of `<body>`. Pick the script URL for your product from the [Script URLs](#script-urls) table:

    ```html theme={"theme":{"light":"one-dark-pro","dark":"one-dark-pro"}}
    <script
      defer
      src="https://pay.sandbox.paymove.io/v1/sdk.js"
      data-product-id="d0a834f9-94a4-4b3a-aa10-27d911e3633f"
      data-button-label="Pay online"
      data-locale="en"
    ></script>
    ```

    You don't need a container in `<body>` - the widget adds the button to the bottom-right corner of the screen by itself.
  </Step>

  <Step title="Test a payment">
    Click the button, go through the flow in the modal and pay with BLIK. In sandbox any 6-digit code starting with `777` works, e.g. `777123`.
  </Step>

  <Step title="Switch to production">
    Replace the script URL with the production one and use the product ID from production. Sandbox and production products are separate - an ID from one environment won't work in the other.
  </Step>
</Steps>

## Script URLs

The script URL depends on the product type and the environment. The path is always the same: `/v1/sdk.js`.

| Product | Sandbox | Production |
| - | - | - |
| Payments for services and documents, donations and fundraisers | `https://pay.sandbox.paymove.io/v1/sdk.js` | `https://pay.paymove.io/v1/sdk.js` |
| Tickets for events and attractions | `https://go.sandbox.paymove.io/v1/sdk.js` | `https://go.paymove.io/v1/sdk.js` |
| Parking | `https://park.sandbox.paymove.io/v1/sdk.js` | `https://park.paymove.io/v1/sdk.js` |

* `/v1/` is the version of the widget interface. Breaking changes will go under a new path, so an existing integration won't break after an update.
* Load the script directly from the paymove domain - don't copy it to your own server. The script recognises itself by its own URL and won't start from another domain.
* Not sure which URL to use for your product? Write to [integration@paymove.io](mailto:integration@paymove.io).

## What the customer sees

1. A **button** with the paymove signet and your text, pinned to a corner of the screen. Optionally a **bubble** with a title and description appears above the button - on hover or during keyboard navigation.
2. After the click: a **modal** with a loading screen, which goes straight into the payment page - without reloading your page.
3. The product's **payment flow** - with the same steps, prices and forms as outside the widget. The widget cannot change amounts or steps.
4. A **confirmation screen** - the modal stays open so the customer can see the confirmation. The **"End"** button closes the modal.

Closing the modal mid-flow discards the entered data - opening it again starts from the first step. It does not, however, cancel a payment the customer has already started, e.g. by confirming BLIK in their banking app.

## Payment methods

The modal offers **BLIK**, **Google Pay** and **Apple Pay** - to the extent enabled for your product. Amounts are always in PLN. Apple Pay appears only in browsers that support it (Safari on Apple devices), and in a modal on your domain it requires a one-time domain registration - see [Apple Pay activation](/en/app-widget/apple-pay).

<Warning>
  The `paymove:completed` browser event is a signal for the UI, not an accounting confirmation. Base settlement and order fulfilment on [webhooks](/en/products/docpay/rest-api#6-webhooks) or on data from the [paymove panel](https://panel.paymove.io).
</Warning>

## What's next

<CardGroup cols={2}>
  <Card title="Configuration" icon="sliders-horizontal" href="/en/app-widget/configuration">
    All `data-*` attributes: button text, bubble, language, position, several widgets on one page.
  </Card>

  <Card title="Controlling the widget" icon="code" href="/en/app-widget/javascript-api">
    Custom buttons, opening from code and the after-payment event.
  </Card>

  <Card title="Apple Pay activation" icon="apple" href="/en/app-widget/apple-pay">
    `merchantDomain` in the product and the Apple verification file on your domain.
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.