Paycose Docs
Checkout SDK

Checkout SDK

Embeddable checkout for React, Vue, Svelte, SvelteKit, Nuxt, Solid, Angular, Next.js, jQuery and vanilla JS — card entry stays inside a cross-origin iframe.

What it does

@checkout/sdk mounts a ready-made checkout into your page. The customer picks a payment method, enters a card, and completes 3DS — all inside a cross-origin iframe served from the Lodash frame origin, so raw PANs never touch your DOM or your JavaScript context.

You render a container. The SDK owns everything inside it.


Install

bun add @checkout/sdk      # or npm / pnpm / yarn

Framework peers (react, vue, svelte, solid-js, @angular/core, tailwindcss, …) are optional — install only the one you use.

styles.css is not auto-injected. Every integration must import it once: import '@checkout/sdk/styles.css'. Tailwind v4 users can instead @import "@checkout/sdk/tailwind.css" to pull the SDK theme layer into their own build.


Three things you must supply

FieldWhat it isWhy
source.sessionA one-time checkout token ({ kind: 'checkout', session: 'cs_…' })Minted by your server calling POST /checkouts with its secret key (sk_…). Never call that from the browser. Payment links use { kind: 'payment_link', token: '…' } instead.
config.apiEndpointThe Lodash API originRequired. The SDK will not guess it from window.location — that would send the session token to your own server. Omitting it throws MissingEndpointError.
config.publishableKeyYour client-safe pk_test_… / pk_live_… keyThe server checks that the key and the session belong to the same app, and that your page's Origin is allow-listed.

For the cross-origin embed you also pass frameUrl.

frameUrl is a base, not a page. The SDK appends /checkout.html itself via checkoutFrameUrl(). Pass https://pay.example.com/embed — not https://pay.example.com/embed/checkout.html.

A publishable key must be pk_test_ or pk_live_ followed by at least four alphanumerics. A bare prefix, an underscore in the body, sk_ secret keys, Omise pkey_ keys and pk_p256_ service-account keys are all rejected with PublishableKeyError.


Quick start

import { EmbeddedCheckout } from '@checkout/sdk/react'
import '@checkout/sdk/styles.css'

export function PayPage({ session }: { session: string }) {
  return (
    <EmbeddedCheckout
      source={{ kind: 'checkout', session }}
      config={{
        apiEndpoint: 'https://api.example.com',
        publishableKey: 'pk_test_abcd1234',
      }}
      frameUrl="https://pay.example.com/embed"
      returnUrl={window.location.href}
      onSuccess={() => console.log('paid')}
      onError={(err) => console.error(err)}
    />
  )
}

@checkout/sdk/next re-exports the same React surface with 'use client' already applied.

Do not pass onRedirect unless you mean it. Omitting it is the correct default — the SDK navigates the top window itself via navigateTop, which is written to survive being called from inside a cross-origin iframe. If you pass a handler, you own the navigation, and a handler that only logs will strand the customer mid-3DS on a blank frame.


Configure once instead of per call

import { configure } from '@checkout/sdk'

configure({
  apiEndpoint: 'https://api.example.com',
  frameUrl: 'https://pay.example.com/embed',
  publishableKey: 'pk_test_abcd1234',
})

Values passed at a call site always win over configure(). Full reference: Configuration.


Export map

SubpathContents
@checkout/sdkmountEmbeddedCheckout, configure, CheckoutClient, assertPublishableKey, resolveApiEndpoint / resolveVaultEndpoint / resolveFrameUrl / checkoutFrameUrl, createProvider / registerProvider / getAvailableProviders / LodashProvider, checkout-return helpers, formatters and validators
@checkout/sdk/reactCheckout, EmbeddedCheckout, PaymentElement, SetupIntentElement, BankAccountElement, useCheckout, resolveCheckoutGate, LinkBadge, useLink, CheckoutClient, applePay / googlePay / promptPay
@checkout/sdk/next./react re-exported with 'use client'
@checkout/sdk/vue ./svelte ./sveltekit ./nuxt ./angular ./solidThe shared framework-agnostic surface (the root list minus the React components). ./solid additionally exports its EmbeddedCheckout.
@checkout/sdk/vue/EmbeddedCheckout.vue ./svelte/EmbeddedCheckout.svelte ./solid/EmbeddedCheckout ./angular/EmbeddedCheckoutThe per-framework component. Angular's exports EmbeddedCheckoutComponent.
@checkout/sdk/vanillaIIFE for <script> tags — CheckoutSDK global — plus an ESM build for bundlers
@checkout/sdk/jqueryIIFE — CheckoutJQuery global — and installs $.fn.checkout(options) / $.fn.checkoutDestroy()
@checkout/sdk/pluginsWallet buttons: applePay, googlePay, promptPay
@checkout/sdk/frameThe frame-side host, used by the checkout page itself
@checkout/sdk/styles.css @checkout/sdk/tailwind.cssStylesheets

The framework components ship as source (.vue / .svelte / .tsx / .ts) and are compiled by your own toolchain — the same convention svelte-package uses. You need the matching Vite plugin (@vitejs/plugin-vue, @sveltejs/vite-plugin-svelte, vite-plugin-solid), which any app in that framework already has. Solid also ships a pre-compiled fallback for bundlers that do not apply the Solid Babel transform.


Which entry do I use

Your appImport fromMount with
React / Next.js@checkout/sdk/react (or /next)<EmbeddedCheckout>
Vue@checkout/sdk/vue/EmbeddedCheckout.vuethe SFC
Svelte / SvelteKit@checkout/sdk/svelte/EmbeddedCheckout.sveltethe component
Solid@checkout/sdk/solid<EmbeddedCheckout>
Angular@checkout/sdk/angular/EmbeddedCheckoutEmbeddedCheckoutComponent
No bundler, plain <script>@checkout/sdk/vanillaCheckoutSDK.mountEmbeddedCheckout(target, options)
Existing jQuery page@checkout/sdk/jquery$(el).checkout(options)

Every one of these is the same core: mountEmbeddedCheckout mounting the checkout frame. The framework wrappers only manage the container's lifecycle.


Errors worth handling

ErrorCause
MissingEndpointErrorNo apiEndpoint / frameUrl from either the call site or configure()
PublishableKeyErrorMissing key, a secret (sk_) or provider key, or a malformed one
ApiErrorA non-2xx response from the checkout API

Where to go next

On this page