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

# SDK

> The @paymove-io/sdk package for Node.js — install, configure the client and create a payment.

The SDK allows the merchant to create payments within the main product. Each payment carries an amount, an external identifier (`externalId`), a return URL after payment (`returnUrl`) and optional customer data.

After creating a payment, the SDK returns a `redirectUrl` to the checkout page where the customer can complete the payment.

<Warning>
  Before integrating via SDK, make sure you have configured your product and webhook. Go to [Configuration](/en/webhooks) to complete the required steps.
</Warning>

<Warning>
  Call the SDK server-side only. The API key must never reach frontend code, and a browser call would be rejected by CORS anyway.
</Warning>

## 1. Installation

```bash theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
npm install @paymove-io/sdk
```

Do not pin a version - install the latest.

## 2. Example call

```javascript theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
import { PaymoveClient } from "@paymove-io/sdk";

const client = new PaymoveClient({
  apiKey: process.env.PAYMOVE_API_KEY, // sk_test_... (sandbox) or sk_live_... (production)
  productId: "2f6c19e8-84a7-4f50-b950-8d5a05e0bbf2",
  environment: "sandbox",
});

const response = await client.createPayment({
  amount: 1000, // integer in grosze: 1000 = 10.00 PLN
  currency: "PLN", // required by the SDK, ignored by the API - always "PLN"
  externalId: "order-123",
  returnUrl: "https://merchant-shop.com/payment/success",
  productName: "Sports T-shirt",
  customerId: "user-567",
  email: "customer@example.com",
  locale: "pl-PL", // optional, overrides the locale from the main product
  triggerPayment: "BLIK", // optional, checkout starts this method right away
  qrStepEnabled: true, // optional, on desktop the checkout opens on a QR code
  autoclose: 10000, // optional, ms — the checkout returns to returnUrl on its own
});

console.log(response.redirectUrl);
```

### Configuration parameters

| Parameter     | Type     | Description                                |
| ------------- | -------- | ------------------------------------------ |
| `apiKey`      | `string` | Authorization key for the API / SDK        |
| `productId`   | `string` | UUID identifying your product (store)      |
| `environment` | `enum`   | Environment: `"sandbox"` or `"production"` |

### `createPayment` parameters

| Parameter        | Type      | Description                                                                                                                                                                                    |
| ---------------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `amount`         | `integer` | Amount in grosze as an **integer** (e.g. 1000 = 10.00 PLN)                                                                                                                                     |
| `currency`       | `string`  | Required by the SDK but ignored by the API - the gateway settles in PLN only. Pass `"PLN"`                                                                                                     |
| `externalId`     | `string`  | Unique payment identifier on the merchant side                                                                                                                                                 |
| `returnUrl`      | `string`  | Customer return URL after payment                                                                                                                                                              |
| `productName`    | `string`  | Product name displayed on checkout                                                                                                                                                             |
| `customerId`     | `string`  | Free-form passthrough value - the checkout does not display it                                                                                                                                 |
| `email`          | `string`  | Optional -  customer email, prefilled on checkout                                                                                                                                              |
| `locale`         | `string`  | Optional -  overrides the locale from the main product                                                                                                                                         |
| `triggerPayment` | `enum`    | Optional -  payment method preselected in the checkout (Google Pay and Apple Pay also start automatically)                                                                                     |
| `qrStepEnabled`  | `boolean` | Optional -  QR code on desktop, the customer finishes the payment on their phone                                                                                                               |
| `autoclose`      | `integer` | Optional -  milliseconds after a successful payment before the checkout returns to `returnUrl` on its own (countdown inside the "Back to store" button, cancelled by any customer interaction) |

All parameters are passed flat, on a single level -  the SDK assembles the `details` object sent to the API from them. Any extra fields are passed through along with the rest.

<Warning>
  `amount` must be an integer. The SDK only checks that the value is a number greater than zero, so `49.99` passes validation and the API then **silently truncates it to 49 grosze**. Convert złoty amounts with `Math.round(amount * 100)`.
</Warning>

### Starting a payment method automatically

With `triggerPayment` you preselect a payment method in the checkout, so the customer does not have to find it in the list.

| Value         | Method        |
| ------------- | ------------- |
| `GPAY`        | Google Pay    |
| `APAY`        | Apple Pay     |
| `BLIK`        | BLIK          |
| `CARD`        | Card payment  |
| `TRANSFER`    | Bank transfer |
| `PAY_BY_LINK` | Pay by Link   |
| `PAYPO`       | PayPo         |

Any other value throws a `PaymoveValidationError` (field `triggerPayment`). Omitting the field or passing `null` means the standard checkout with method selection.

<Warning>
  Auto-start - opening the payment as if the customer pressed **Pay** - applies to **Google Pay and Apple Pay only**. For the other five methods the tile is merely preselected.
</Warning>

<Info>
  Even for the wallets, auto-start only works when the checkout knows the customer email -  pass it in `email`. It also does not run inside the [embedded widget modal](/en/sdk/widget), or when the method is not available for your product.
</Info>

The auto-start applies to the first display of the checkout only. Once the customer picks a method themselves, it does not run again -  until the page is reloaded.

<Warning>
  Google Pay and Apple Pay open a native browser sheet that normally requires a customer gesture -  the automatic start may be blocked by the browser. The customer then sees the standard payment screen and pays manually.
</Warning>

### QR code step

With `qrStepEnabled` the checkout opens on an extra screen on desktop: instead of the payment form the customer sees a QR code, scans it with their phone and finishes the payment there. Useful for BLIK and for wallets that only exist on mobile.

The step is opt-in -  omit the field or pass `false` and the checkout goes straight to the payment form.

<Info>
  The QR code appears on desktop only. On mobile, and whenever `triggerPayment` is set, the checkout skips the step regardless of this field.
</Info>

<Info>
  `qrStepEnabled` reached the SDK types after `0.2.0` was published. On the older version the field still works (the SDK forwards unknown keys), but TypeScript will not suggest it -  update the package to the latest version.
</Info>

### Automatic return to the store

After a successful payment the checkout shows a confirmation screen with a "Back to store" button. The `autoclose` field (in milliseconds) makes that return happen on its own: the remaining seconds are shown inside the button (`Back to store (10s)`) and the customer lands on `returnUrl` once the countdown ends. Inside the widget the modal closes instead and `onComplete` fires.

Any interaction from the customer -  a click, a tap, a key press -  cancels the countdown for good, so nobody is pulled away mid-action, for example while downloading the confirmation PDF. Omit the field and the confirmation stays on screen until the customer leaves it themselves.

<Info>
  `autoclose` reached the SDK types in release `0.3.0`. On older versions the field still works (the SDK forwards unknown keys), but TypeScript will not suggest it.
</Info>

### Response

| Field         | Type     | Description                                                                   |
| ------------- | -------- | ----------------------------------------------------------------------------- |
| `redirectUrl` | `string` | Checkout address -  redirect the customer to this URL to complete the payment |

## 3. Redirect the customer

```javascript theme={"theme":{"light":"one-light","dark":"one-dark-pro"}}
window.location.href = response.redirectUrl;
```

Once the payment completes, the checkout shows a confirmation screen with a "back to shop" button that takes the customer to the address provided in `returnUrl`. The entire payment process is fully managed by Paymove -  checkout and processing - while you fulfil the order after a verified [webhook](/en/webhook-signature), not after the customer returns.

<Info>
  Instead of redirecting, you can open the same URL in a modal on your own page — see the [Browser widget](/en/sdk/widget), which also ships a ready-made button and a payment-methods bar.
</Info>
