Paycose Docs
Checkout SDK

SDK Configuration

Reference for EmbeddedCheckoutConfig, EmbeddedCheckoutOptions, configure(), styles, and the SDK error types.

The shape of a call

Everything the SDK needs is split in two: config (who you are, where the API is) and the options around it (what to pay for, where the frame lives, what to call back).

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

const handle = mountEmbeddedCheckout('#checkout', {
  source: { kind: 'checkout', session: 'cs_live_123' },
  config: {
    apiEndpoint: 'https://api.example.com',
    publishableKey: 'pk_live_abcdef0123456789',
  },
  frameUrl: 'https://pay.example.com/embed',
  returnUrl: location.href,
  onSuccess: () => console.log('paid'),
})

// later
handle.destroy()

EmbeddedCheckoutConfig

The config object. Every field is optional in the type, but apiEndpoint and publishableKey must come from somewhere — the call site or configure().

Prop

Type

apiEndpoint is required. There is no fallback to the page origin — that would post the checkout session token to your own server instead of to Lodash. Omitting it throws MissingEndpointError.

The same six fields are accepted by <Checkout config={…}> in React and by new CheckoutClient(…) (minus locale and colorScheme, which are UI-only).


EmbeddedCheckoutOptions

The second argument to mountEmbeddedCheckout(), and the prop set of every framework EmbeddedCheckout component.

Prop

Type

mountEmbeddedCheckout() returns an EmbeddedCheckoutHandle with a single method, destroy(), which unmounts the frame and removes the 3DS-return message listener. Always call it when the container goes away.


frameUrl is a base, not a page

frameUrl points at the directory the frame assets are served from. Internally the SDK runs checkoutFrameUrl(), which appends checkout.html (and is idempotent if you already did).

import { checkoutFrameUrl, resolveFrameUrl } from '@checkout/sdk'

checkoutFrameUrl('https://pay.example.com/embed')
// → 'https://pay.example.com/embed/checkout.html'

checkoutFrameUrl(resolveFrameUrl()) // uses configure({ frameUrl }) when no argument is given

checkout.html is the only frame page (plus checkout-return.html, which the SDK builds for you via buildCheckoutReturnUrl()).


Publishable key rules

assertPublishableKey() runs before anything is mounted, so a bad key fails loudly at the call site instead of as a 401 mid-payment. The rule: pk_test_ or pk_live_ followed by at least four alphanumeric characters, and no longer than 48 characters overall.

ValueResult
pk_test_abcdef0123456789accepted
pk_live_rejected — prefix only
pk_test_ab_cdrejected — underscore in the body
sk_test_…rejected — secret key, never put one in a browser
pkey_…rejected — an Omise public key
pk_p256_…rejected — a service-account encryption key
a long pk_live_… from another processorrejected — Stripe shares the prefix; the SDK wants your Lodash key

You can run the same check yourself:

import { assertPublishableKey, PublishableKeyError } from '@checkout/sdk'

try {
  assertPublishableKey(process.env.NEXT_PUBLIC_LODASH_PK, 'boot')
} catch (err) {
  if (err instanceof PublishableKeyError) console.error(err.message)
}

Redirects and 3DS

Omitting onRedirect is the correct default. The SDK navigates the top window itself via an internal navigateTop helper that is written to work from inside a cross-origin iframe. If you pass a handler, you own the navigation — one that only logs will strand the customer mid-3DS on a blank frame.

// Correct — the SDK performs the redirect
mountEmbeddedCheckout('#checkout', { source, config, frameUrl })

// Only when you must route it yourself
mountEmbeddedCheckout('#checkout', {
  source,
  config,
  frameUrl,
  onRedirect: (url) => { window.top.location.href = url },
})

Note that window.top.location.assign() throws a SecurityError from a cross-origin iframe — a cross-origin Location exposes only the href setter. Assign to top.location.href.


configure()

Set the values once at boot instead of repeating them at every call site. configure() is exported from the root entry and from every framework subpath.

import { configure } from '@checkout/sdk'

configure({
  apiEndpoint: 'https://api.example.com',
  vaultEndpoint: 'https://vault.example.com',
  frameUrl: 'https://pay.example.com/embed',
  publishableKey: 'pk_live_abcdef0123456789',
  country: 'TH',
})

SDKDefaults

Prop

Type

Precedence

A value passed at the call site always wins; configure() only fills the gaps. configure() replaces the whole defaults object, so call it once with everything.

configure({ apiEndpoint: 'https://api.example.com', publishableKey: 'pk_live_abcdef0123456789' })

mountEmbeddedCheckout('#checkout', {
  source: { kind: 'checkout', session: 'cs_live_123' },
  config: { apiEndpoint: 'https://api-staging.example.com' }, // wins
  frameUrl: 'https://pay.example.com/embed',
})
// → apiEndpoint: https://api-staging.example.com
// → publishableKey: satisfies the parent-side key check from configure()

configure() does not cross the frame boundary. The config object you pass at the call site is what is serialised into the checkout frame. configure() supplies frameUrl and satisfies the parent-side publishable-key check, but the frame itself never sees those defaults — so keep apiEndpoint and publishableKey in config for the embedded flow.

You can ask for the resolved value directly — each of these throws MissingEndpointError if neither source supplied one:

import { resolveApiEndpoint, resolveVaultEndpoint, resolveFrameUrl } from '@checkout/sdk'

resolveApiEndpoint()                              // from configure()
resolveApiEndpoint('https://api.example.com')     // explicit wins

Color scheme

config.colorScheme overrides the SDK's CSS custom properties. All fields are optional strings and accept any CSS colour value.

Prop

Type

<Checkout
  source={{ kind: 'checkout', session: 'cs_live_123' }}
  config={{
    apiEndpoint: 'https://api.example.com',
    publishableKey: 'pk_live_abcdef0123456789',
    locale: 'th',
    colorScheme: { primary: '#6366f1', error: '#dc2626' },
  }}
/>

To apply the same palette to your own markup, the SDK exports both serialisers:

import { generateCSSVariables } from '@checkout/sdk'          // inline style string
import { generateCSSVarsObject } from '@checkout/sdk/react'   // React style object

generateCSSVariables({ primary: '#6366f1' })
// → '--checkout-primary: #6366f1'

Styles

styles.css is not auto-injected. Import it once per integration or the checkout renders unstyled.

import '@checkout/sdk/styles.css'

Errors

ErrorThrown when
MissingEndpointErrorNo apiEndpoint, vaultEndpoint or frameUrl from either the call site or configure(). The message names the missing key.
PublishableKeyErrorThe key is missing, malformed, a bare prefix, a secret key, or another processor's key.
UnsafeRedirectErrorA redirect target that is not http: or https: — an unparseable URL, or a javascript: / data: scheme.

MissingEndpointError and PublishableKeyError are exported and can be tested with instanceof. UnsafeRedirectError is raised inside the SDK's redirect path and is not exported — match on err.name.

import { mountEmbeddedCheckout, MissingEndpointError, PublishableKeyError } from '@checkout/sdk'

try {
  mountEmbeddedCheckout('#checkout', { source, config, frameUrl })
} catch (err) {
  if (err instanceof MissingEndpointError) reportConfigBug(err.message)
  else if (err instanceof PublishableKeyError) reportConfigBug(err.message)
  else if (err instanceof Error && err.name === 'UnsafeRedirectError') reportConfigBug(err.message)
  else throw err
}

All three are configuration mistakes, not runtime payment failures — they should surface in your build or smoke test, never to a customer. Payment failures arrive through onError instead.


On this page