> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://docs.paymentkit.com/guides/integration/sdk-reference/payment-kit-js/buy-now-pay-later/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.paymentkit.com/_mcp/server. # Buy now, pay later PaymentKit.js supports three buy now, pay later (BNPL) payment methods: **Klarna**, **Afterpay**, and **Affirm**. All three share the same flow — there are no input elements to mount. When you submit, PaymentKit.js opens a secure popup where the customer completes approval with the BNPL provider, then polls for the result and resolves your `onSuccess` or `onError` callback. # Setup Register only the BNPL methods you plan to offer — each one is independent. #### CDN ```html ``` #### NPM/ES Modules ```typescript import PaymentKit from '@payment-kit-js/vanilla'; import KlarnaPaymentMethod from '@payment-kit-js/vanilla/payment-methods/klarna'; import AfterpayPaymentMethod from '@payment-kit-js/vanilla/payment-methods/afterpay'; import AffirmPaymentMethod from '@payment-kit-js/vanilla/payment-methods/affirm'; const paymentKit = PaymentKit({ environment: 'sandbox', secureToken: 'aBcDeFgHiJkLmNoPqRsTuVwXyZ123456', // From your backend paymentMethods: [KlarnaPaymentMethod, AfterpayPaymentMethod, AffirmPaymentMethod] }); ``` # Submit payment Like PayPal, BNPL methods don't need input elements. Submit directly when the customer clicks your Klarna, Afterpay, or Affirm button, passing the matching `paymentMethod` name — `'klarna'`, `'afterpay'`, or `'affirm'`: ```typescript document.getElementById('klarna-button').addEventListener('click', () => { paymentKit.submit({ fields: { customer_name: 'Jane Smith', customer_email: 'jane@example.com', customer_country: 'US', customer_zip_code: '94102' }, paymentMethod: 'klarna', // or 'afterpay' / 'affirm' options: { processorId: 'proc_abc123', customerInfo: { first_name: 'Jane', last_name: 'Smith', email: 'jane@example.com' } }, onSuccess: (result) => { console.log('ID:', result.id); console.log('Checkout Attempt ID:', result.checkoutAttemptId); console.log('Checkout Session ID:', result.checkoutSessionId); console.log('State:', result.state); window.location.href = '/success'; }, onError: (errors) => { console.error(errors); } }); }); ``` PaymentKit.js opens the provider's approval page in a 600×700 popup window and polls the payment status every 2 seconds. If the customer closes the popup without completing approval, the submission fails with a "popup closed by user" error after a short grace period. > **Warning** > > The popup must be triggered by a direct user action (button click) to avoid being blocked by popup blockers. > **Info** > > Need to run your own validation before the charge? Pass `beforeConfirm` in `options`. The popup opens synchronously on tap, but PaymentKit waits for `beforeConfirm` before navigating it to the provider — reject and the popup closes without the provider ever seeing the request. See [Pre-charge validation](/guides/integration/sdk-reference/payment-kit-js/pre-charge-validation). # Options | Option | Type | Required | Methods | Description | | ------------------------- | --------- | -------- | ----------- | ----------------------------------------------------------------------------- | | `processorId` | `string` | Yes | All | Your processor ID (BNPL payments are processed through your Stripe processor) | | `customerInfo.first_name` | `string` | Yes | All | Customer's first name | | `customerInfo.last_name` | `string` | Yes | All | Customer's last name | | `customerInfo.email` | `string` | No | All | Customer's email address | | `setupFutureUsage` | `boolean` | No | Klarna only | Save the Klarna payment method for future use | # Saving Klarna for future payments Klarna supports saving the payment method during checkout for future off-session charges (for example, subscriptions). Pass `setupFutureUsage: true` in the submit options: ```typescript paymentKit.submit({ fields: { /* ... */ }, paymentMethod: 'klarna', options: { processorId: 'proc_abc123', customerInfo: { first_name: 'Jane', last_name: 'Smith' }, setupFutureUsage: true }, onSuccess: (result) => { /* ... */ }, onError: (errors) => { /* ... */ } }); ``` > **Info** > > `setupFutureUsage` is only supported by Klarna. Afterpay and Affirm do not support saving the payment method — their TypeScript submit option types omit the field, and the SDK never sends it for those methods. # Error handling Each BNPL method returns its errors under its own key — `errors.klarna`, `errors.afterpay`, or `errors.affirm` — as descriptive strings. Validation errors use `errors.processor_id` and `errors.customer_name`: ```typescript onError: (errors) => { if (errors.processor_id) { // Missing processor ID console.error(errors.processor_id); // "Processor ID is required" } else if (errors.customer_name) { // Missing customer name console.error(errors.customer_name); // "Customer first and last name are required" } else if (errors.klarna) { // Klarna-specific errors (same patterns apply to errors.afterpay / errors.affirm) if (errors.klarna.includes('popup')) { // Popup was blocked or closed alert('Please allow popups for this site'); } else if (errors.klarna.includes('cancelled')) { // Customer cancelled in the provider's flow } console.error(errors.klarna); } } ``` The table below uses Klarna as the example; for Afterpay and Affirm the error field is `afterpay` / `affirm` and the provider name in the message changes accordingly: | Error Field | Error Message | Cause | | --------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------- | | `processor_id` | `Processor ID is required` | Missing `processorId` in options | | `customer_name` | `Customer first and last name are required` | Missing `customerInfo.first_name` or `customerInfo.last_name` | | `klarna` | `Failed to start Klarna checkout ({status})` | The start request failed (the backend error detail is used when available) | | `klarna` | `Failed to open Klarna popup. Please allow popups for this site.` | Browser blocked the popup window | | `klarna` | `Klarna popup closed by user` | Customer closed the popup without completing payment | | `klarna` | `Failed to check Klarna status` | Error while polling for payment completion | | `klarna` | `Klarna checkout cancelled` | Payment was cancelled by the customer | | `klarna` | `Klarna checkout failed` | Payment failed on the provider's side | | `klarna` | `Polling error: {error}` | Network error during status polling | | `klarna` | `Klarna checkout error: {error}` | General checkout error | > Accept Klarna, Afterpay, and Affirm payments using a popup-based approval flow.