Integrate PayParts (Quickstart)
Add PayParts to any webstore checkout. This guide is self-contained: it gives you everything needed to get a working integration end-to-end — a backend call to create a session, then a frontend that renders the payment components. It is framework-agnostic, so it maps to any stack (Node, Medusa.js, Shopify backend, etc.). For the deep detail on any step, follow the links at the bottom of each section.
How it works (30 seconds)
- Your backend calls
POST /v1/session/startwith the order amount and gets back asessionToken. - Your frontend loads the PayParts SDK, initializes it with that
sessionToken, and mounts the payment components (card form, wallets, method list, etc.). - Payment completes — the consumer finishes on the payment page, you confirm via the
returnUrl/onSuccessevent, and your backend is notified via theexchangeUrlwebhook.
Consumer ──▶ Your Checkout (PayParts components)
│
├─ POST /v1/session/start ──▶ PayParts backend ──▶ sessionToken
│
└─ SDK init(sessionToken) ──▶ render + complete payment ✦
│
returnUrl / onSuccess ◀─┘
exchangeUrl webhook ──▶ your backend
1. Environments
PayParts has two environments. Use the matching one on backend and frontend.
| Production | Zero (pre-production) | |
|---|---|---|
| API base URL | https://parts.pay.nl | https://zero-parts.pay.nl |
| SDK script | https://parts.pay.nl/sdk/payparts-sdk.js | https://zero-parts.pay.nl/sdk/payparts-sdk.js |
Zero can contain features not yet on Production (e.g. session updates). Verify each feature is available in the environment you target.
2. Backend: create a session
Your backend creates the session before the checkout page loads. This is a simple authenticated REST call.
PayParts ≠ the classic Pay Payment API. PayParts sessions usePOST /v1/session/start(and/v1/session/{id}/pay). This is not Pay's legacy card API (/v1/Payment/authenticate,/authorize,/getAuthenticationStatus). If an AI/agent tool surfaces endpoints like/v1/Payment/*, ignore them — those belong to the older hosted/classic Pay products, not PayParts. Only/v1/session/*is used for PayParts.
Authentication
POST /v1/session/start uses HTTP Basic auth with your Sales Location credentials:
Authorization: Basic <base64(sl-code:secret)>Minimal request
curl -X POST 'https://parts.pay.nl/v1/session/start' \
-H 'Authorization: Basic <base64(sl-code:secret)>' \
-H 'Content-Type: application/json' \
-d '{
"returnUrl": "https://example.com/return",
"exchangeUrl": "https://example.com/exchange",
"amount": {
"value": 1,
"currency": "EUR"
}
}'curl -X POST 'https://zero-parts.pay.nl/v1/session/start' \
-H 'Authorization: Basic <base64(sl-code:secret)>' \
-H 'Content-Type: application/json' \
-d '{
"returnUrl": "https://example.com/return",
"exchangeUrl": "https://example.com/exchange",
"amount": {
"value": 1,
"currency": "EUR"
}
}'Successful response
{
"sessionId": "a3fc44c532784ce1b8ac2f2a003e1239",
"sessionToken": "st_..."
}Take the sessionToken — you pass it to the SDK on the frontend. Never expose your Sales Location secret to the browser; keep this call on the backend.
Amounts are in cents.amount.value(and everyproducts[].price.value) is the smallest currency unit —€0.01 = 1(one cent).valuemust be≥ 1. Never send decimal euros.
Order-based / redirect methods (e.g. iDEAL) need more. The minimal call above only covers methods paid inline. When the payer can pick a redirect method such as iDEAL, the session must also contain the fullorder(line items withvatPercentage, plus delivery and invoice addresses) and thecustomer— otherwisePOST /v1/session/{id}/payfails with422 PAY-2880 requiredDataMissing. See Create a session from your backend for the full request.
3. Frontend: add the SDK
On your checkout page, load the SDK, initialize it with the sessionToken, and mount the components you want.
Load the SDK
<script type="module" src="https://parts.pay.nl/sdk/payparts-sdk.js"></script><script type="module" src="https://zero-parts.pay.nl/sdk/payparts-sdk.js"></script>Initialize and mount components
const checkout = await window.PayPartsSDK.init({
sessionToken: '<sessionToken from your backend>', // never store this in source
apiUrl: 'https://parts.pay.nl', // match your environment
country: 'NL',
language: 'en',
});
// Mount the components where you want them on the page
const card = checkout.prepare('card-payments'); // card form
await card.bind('#payparts-card');
const express = checkout.prepare('checkout-buttons'); // Apple Pay / PayPal / Google Pay
await express.bind('#payparts-express');
const methods = checkout.prepare('methods'); // full payment method list
await methods.bind('#payparts-methods');The component names are lowercase strings: 'card-payments', 'checkout-buttons', 'methods', 'giftcard', 'applepay', 'paypal', 'googlepay', 'click-to-pay-methods', 'user-optimized'.
4. Finish the payment
How the flow completes depends on the method:
- Redirect methods (e.g. iDEAL): the payer is sent to a payment page. Return them to your
returnUrlwhen done, where you confirm the outcome. - Inline methods (cards, wallets): confirm via the SDK
onSuccessevent. - Your backend is always notified by the webhook sent to your
exchangeUrlwith the payment result.
A minimal event handler:
checkout.events = {
onSuccess: (event) => {
// payment succeeded — show confirmation / complete the order
console.log('Order:', event.orderId);
event.resolve();
},
onError: (event) => {
console.error('Payment failed:', event.error, event.errors);
event.resolve(); // allow retry
},
};See Handle events for the full event model.
5. Gotchas that break integrations
These are the most common reasons a first integration fails. Handle them up front:
| Symptom | Cause / fix |
|---|---|
| Money is 100× off | Amounts are in cents, not euros. €0.01 = 1 (one cent); minimum is 1. |
422 PAY-2880 requiredDataMissing on pay | Session created without full order + customer. Add them (required for iDEAL etc.). |
422 PAY-1422 reference: Deze waarde is niet geldig. | reference contains invalid chars (commonly _, e.g. a cart_... id). Send a plain order number. |
410 Gone | Session expired. Set a longer expire or create a fresh session. |
| Component not appearing | Check the browser console / that the target element exists before bind(). |
Webhook signature: PAY.Parts exchanges are signed using the Service Location secret. Verify the HMAC with the secret belonging to theSL-XXXX-XXXXinsignature-keyid— not your merchant API token.
Next steps
Updated about 13 hours ago