docs

@xpayeg/sdk

The browser JavaScript SDK. Reference for every public export, method signature, and option type.

The XPay JavaScript SDK is a tiny CDN-loaded library that gives you a typed XPayInstance from your publishable key. From there you can mount payment UI as Elements, open a drop-in checkout, or run a single-call initCheckout and confirm a payment.

pnpm add @xpayeg/sdk
# or: npm install @xpayeg/sdk / yarn add @xpayeg/sdk

The package ships ESM and CJS builds, plus full TypeScript types. The runtime itself is loaded on demand from https://checkout.xpay.app/v1/sdk.js.

There are two ways to load the SDK, and they expose the factory under different names. This is the single most common integration mistake, so get it right up front:

// Import loadXPay and await it. This is the path the rest of this page documents.
import { loadXPay } from "@xpayeg/sdk";

const xpay = await loadXPay("pk_test_...");
<!-- Drop the CDN script tag, then call the global XPay() factory the runtime
     puts on window. No import, no await, no build step. -->
<script src="https://checkout.xpay.app/v1/sdk.js"></script>
<script>
  const xpay = XPay("pk_test_...");
</script>

loadXPay exists only in the npm package; the CDN runtime exposes only the global XPay. They are not interchangeable: calling loadXPay(...) after a script tag throws ReferenceError: loadXPay is not defined, and the global XPay isn't importable. Pick the row that matches your setup; everything after you obtain the xpay instance is identical. This page documents the npm entry point (loadXPay). On the plain-HTML path, swap await loadXPay(k) for XPay(k) in any example below.

See it running: Vanilla HTML example

Every pattern on this page as runnable plain-HTML files: CDN script tag + global XPay(), a tiny Express server, no build step. Clone, add your test keys, open in a browser.

This page is the reference for every public export. For walkthroughs, see Drop-in and Elements under Integrate.

Use this package when

@xpayeg/sdk is the vanilla SDK and is the right pick whenever you're not on React. That covers:

  • Plain HTML and static sites (CDN script tag + the global XPay() factory; see the two load paths above).
  • Vue, Svelte, Solid, Angular, Lit, Qwik, Astro, and any other framework. Call the SDK from inside that framework's component lifecycle (mount, onMount, useEffect equivalent, etc.).
  • React Native via WebView until a native package ships.
  • Server-side scripts that need to type their clientSecret payloads (the package's TypeScript types are framework-agnostic).

For React, @xpayeg/react is a thin convenience wrapper on top of this package. It builds on loadXPay, xpay.initCheckout, and xpay.elements, so anything you read here applies under the hood there too.

Quick start

The recommended path is loadXPay at module level, then xpay.initCheckout() once you have a clientSecret from your server.

import { loadXPay } from "@xpayeg/sdk";

// Module-level: call once, share across the app.
const xpayPromise = loadXPay("pk_test_...");

async function startCheckout() {
  const xpay = await xpayPromise;
  if (!xpay) return; // SSR returns null on the server

  // initCheckout accepts a Promise<string> too, so you can pass the fetch directly.
  const checkout = await xpay.initCheckout({
    clientSecret: fetch("/api/create-checkout", { method: "POST" })
      .then((r) => r.json())
      .then((d) => d.clientSecret as string),
  });

  // Mount the payment element.
  const elements = checkout.getElements();
  const paymentElement = elements.create("payment");
  paymentElement.mount("#payment-element");

  // Listen for state changes (promo codes, quantity updates, fee recalcs).
  checkout.on("change", (session) => {
    document.getElementById("total")!.textContent =
      `${session.currency} ${(session.amountTotal / 100).toFixed(2)}`;
  });

  // Confirm when the customer submits.
  document.getElementById("pay")!.addEventListener("click", async () => {
    const result = await checkout.confirm({
      customerDetails: { email: "customer@example.com", name: "Aya Hassan" },
    });
    if (result.type === "error") {
      console.error(result.error.message);
      return;
    }
    window.location.href = "/thank-you";
  });
}

Exports at a glance

ExportKindWhat it is
loadXPay(publishableKey)functionLoads the SDK from CDN and returns an XPayInstance (or null on the server during SSR).
XPayInstanceinterfaceFactory for Elements, drop-in Checkout, confirmPayment, and initCheckout.
ElementsinterfaceManages payment elements (PaymentElement) and the session lifecycle.
PaymentElementinterfaceMountable payment-method selector with the card form, BNPL, kiosk, and wallet methods.
ConfirmPaymentOptionsinterfaceShape passed to xpay.confirmPayment() and checkout.confirm().
CheckoutOptionsinterfaceConfiguration for xpay.checkout(). Drop-in modal or inline embed.
CheckoutInstanceinterfaceDrop-in checkout handle: open(), close(), destroy(), plus events.
CheckoutCompleteResultinterfacePayload for the drop-in onComplete callback.
InitCheckoutOptionsinterfaceOptions for xpay.initCheckout(). The modern single-call API.
InitCheckoutResulttypeSession data merged with action methods (confirm, promo codes, etc.).
CheckoutActionsinterfaceThe action methods that ride on the checkout object.
CheckoutSessiontypeThe session data shape: amount, currency, status, payment methods, line items.
ActionResulttypeTagged union: { type: "success", session } or { type: "error", error }.
XPayErrorinterfaceUnified error type across every SDK action.
AppearancetypeBranding overrides for the embedded UI (color mode, border style, colors, font).
PaymentElementChangeEventinterfacePayload for PaymentElement change events.
ElementsUpdateOptionsinterfaceOptions for elements.update() in deferred mode: amount, currency.
ElementsReadyEventinterfacePayload for the Elements "ready" event.
ElementsLoadErrorEventinterfacePayload for the Elements "loaderror" event.
CustomerDetails, AddressinterfaceShapes for customer fields collected by your form and passed at confirm time.
PaymentMethodInfotypeInformation about an available payment method on the session.
SessionStatustypeTagged union: open, expired, or complete.

loadXPay(publishableKey)

Loads the SDK runtime from CDN and returns an XPayInstance. Call this once at module level, not inside a component.

import { loadXPay } from "@xpayeg/sdk";

const xpayPromise = loadXPay("pk_test_...");
// later, when you have a clientSecret:
const xpay = await xpayPromise;
const elements = xpay.elements({ clientSecret: "cs_test_..." });
ParameterTypeDescription
publishableKeystringYour publishable API key (pk_test_... or pk_live_...).

Returns Promise<XPayInstance | null>. null is returned during server-side rendering (no window). The XPayProvider from @xpayeg/react handles null gracefully by retrying on the client.

The runtime is fetched once; subsequent loadXPay calls reuse the same script tag. If a script with sdk.js is already on the page, it isn't re-injected.

XPayInstance

The factory you get back from loadXPay. Four methods.

interface XPayInstance {
  elements(options: ElementsOptions): Elements;
  checkout(options: CheckoutOptions): CheckoutInstance;
  confirmPayment(options: ConfirmPaymentOptions): Promise<ActionResult>;
  initCheckout(options: InitCheckoutOptions): Promise<InitCheckoutResult>;
}

elements(options)

Creates an Elements instance for custom checkout UI. Pass it the clientSecret from a Checkout Session.

const elements = xpay.elements({
  clientSecret: "cs_test_...",
  appearance: { colorMode: "dark", borderStyle: "rounded" },
  locale: "en",
});

ElementsOptions accepts one of two forms. Session-first, with a clientSecret:

FieldTypeDescription
clientSecretstring | Promise<string>Required. The Checkout Session client secret. Can be a Promise.
appearanceAppearanceOptional. UI overrides merged with the session's server-side branding.
locale"en" | "ar"Optional. Defaults to "en".

Or deferred, with no session yet:

const elements = xpay.elements({
  mode: "payment",
  amount: 149900, // minor units
  currency: "EGP",
});
FieldTypeDescription
mode"payment"Required. Deferred mode.
amountnumberRequired. The amount to display and charge, in minor units. Integer above zero.
currencystringRequired. Three letter currency code (e.g. "EGP").
paymentMethodTypesstring[]Optional. Render only these method types. Narrow-only: intersected with your account's enabled methods; an empty intersection fails with loaderror. Fixed for the element's lifetime.
appearanceAppearanceOptional. UI overrides.
locale"en" | "ar"Optional. Defaults to "en".

The two forms are mutually exclusive. In deferred mode your server creates the session when the customer pays, and you pass its clientSecret to confirmPayment({ elements, clientSecret }). The session total must equal the displayed amount, or the payment is refused with amount_reconfirmation_required and nothing is charged. See Elements: deferred mode.

checkout(options)

Creates a drop-in checkout instance. See CheckoutOptions below.

confirmPayment(options)

Submits a payment using the data collected by Elements. See ConfirmPaymentOptions below.

initCheckout(options)

Modern single-call API: returns the session data and action methods together. See InitCheckoutOptions below.

Elements

Returned by xpay.elements(). Manages one shared payment iframe and exposes session-mutation methods.

interface Elements {
  create(type: "payment", options?: PaymentElementOptions): PaymentElement;
  getElement(type: "payment"): PaymentElement | null;
  fetchPaymentMethods(): Promise<PaymentMethodInfo[]>;

  on(event: "ready" | "change" | "loaderror" | "error", handler): void;
  off(event: string, handler): void;

  applyPromotionCode(code: string): Promise<ActionResult>;
  removePromotionCode(): Promise<ActionResult>;
  updateLineItemQuantity(args: { lineItem: string; quantity: number }): Promise<ActionResult>;
  submit(): Promise<{ error?: XPayError; selectedPaymentMethod?: string }>;
  fetchUpdates(): Promise<ActionResult>;
  changeAppearance(appearance: Appearance): void;
  update(options: ElementsUpdateOptions): Promise<void>;
  destroy(): void;
}

Events

EventHandler signatureFires when
"ready"(data: ElementsReadyEvent) => voidSession is loaded and elements are mountable. Fires immediately if already loaded.
"change"(session: CheckoutSession) => voidSession data changes (payment method selection, promo codes, fee recalculations).
"loaderror"(data: ElementsLoadErrorEvent) => voidSession fails to load (network error, invalid client secret, API error).
"error"(error: XPayError) => voidUnsolicited error not triggered by a merchant action (e.g. session expired during a fee recalculation).

Methods

  • create("payment", options?): returns a PaymentElement you can mount. options.layout picks the chooser presentation, see Layout.
  • getElement("payment"): returns the existing element or null if none was created.
  • fetchPaymentMethods(): returns the list of PaymentMethodInfo the session supports.
  • applyPromotionCode(code): applies a promotion code, returns ActionResult.
  • removePromotionCode(): removes the applied code, returns ActionResult.
  • updateLineItemQuantity({ lineItem, quantity }): updates a line item, returns ActionResult.
  • submit(): validates element fields. Returns either an error or the selectedPaymentMethod type string.
  • fetchUpdates(): re-fetches the session from the server and resolves with the fresh session data. Use it after a server-side session change.
  • changeAppearance(appearance): updates branding at runtime without recreating elements.
  • update({ amount, currency }): deferred mode only. Updates the displayed amount when your cart total changes. Throws on instances created with a clientSecret; session amounts are server-owned.
  • destroy(): tears down the instance and frees resources.

PaymentElement

The full payment-method selector with the card form, BNPL, kiosk, and wallet methods. Get one from elements.create("payment").

interface PaymentElement {
  mount(container: string | HTMLElement): void;
  unmount(): void;
  destroy(): void;
  focus(): void;
  blur(): void;
  collapse(): void;
  update(options: { layout?: "accordion" | "tabs" }): void;

  on(event: "ready" | "loaderstart" | "loaderror", handler): void;
  off(event: string, handler): void;
}

Layout

elements.create("payment", { layout }) controls how the element presents the payment method chooser:

  • "accordion" (default): a vertical list, one row per method, with the selected method's form expanding under its row. With exactly one method the chooser disappears and the element shows a static header (logo and name, no radio) above the content. Card is the exception: its form fields already identify the method, so a single card method renders the bare form.
  • "tabs": a grid of tiles with the selected method's form below it. Tiles share each row equally and wrap to a new row when they no longer fit, so every method stays visible. With exactly one method the tile grid disappears and only the content renders. Use this when your own page already shows the method's logo and name, for example a per-gateway row in a plugin checkout.
const paymentElement = elements.create("payment", { layout: "tabs" });

Layout is updatable after creation with paymentElement.update({ layout: "accordion" }). Both layouts keep every method's form mounted, so a typed card number survives switching methods and back.

PaymentElementChangeEvent is the payload for the parent Elements "change" event when triggered by a payment-method change:

FieldTypeDescription
elementType"payment"Always "payment" for this element.
emptybooleanWhether all card fields are empty.
completebooleanWhether the form is complete and ready to submit.
collapsedbooleanWhether the method selector is collapsed (no method selected).
value{ type: string }The currently selected payment method type.
sessionCheckoutSessionLatest session snapshot.

ConfirmPaymentOptions

Shape passed to xpay.confirmPayment() and checkout.confirm().

FieldTypeDescription
elementsElementsRequired (for xpay.confirmPayment). The Elements instance managing the form. checkout.confirm injects this for you.
clientSecretstringRequired in deferred mode: the secret of the session your server created at pay time. A plain string. Ignored for session-first elements.
customerDetailsCustomerDetailsOptional. Customer fields collected by your form.
customFieldsRecord<string, string | number | boolean>Optional. Custom-field values for the session.
deviceFingerprint{ visitorId: string; confidence?: number }Optional. Device fingerprint for fraud detection.
paymentMethodstringOptional. Override the selected payment method type.
redirect"if_required" | "always"Default "if_required". Controls post-payment navigation.

redirect semantics:

  • "if_required" returns the result to your code; only redirects when the payment method requires it (e.g. 3-D Secure, BNPL).
  • "always" always redirects to the session's afterCompletion.redirect.url after payment. The page navigates away and the function never returns on success.

The destination is the session's afterCompletion.redirect.url, set when your server creates the session. XPay navigates to it unchanged.

In deferred mode, the session total must equal the amount the element displays. A different total fails the confirmation with amount_reconfirmation_required and nothing is charged: update the element's amount and confirm again.

On retries, pass the same clientSecret: one session per checkout keeps the whole transaction lifecycle on one Payment Intent. If the total changed, your server updates the existing session first. Create a fresh session only when the previous one died, which the confirm reports as checkout_session_expired. See Elements: retries.

CheckoutOptions

Configuration for the drop-in xpay.checkout().

FieldTypeDescription
clientSecretstringRequired. The Checkout Session client secret.
mode"modal" | "inline""modal" (default) renders an overlay; "inline" embeds in container.
containerstring | HTMLElementRequired for inline mode. CSS selector or DOM node to embed in.
appearanceAppearanceUI overrides merged with the session's branding.
locale"en" | "ar"UI locale. Defaults to "en".
onComplete(result: CheckoutCompleteResult) => voidCalled when payment completes successfully.
onClose() => voidCalled when the modal closes.
onReady(session: CheckoutSession) => voidCalled when the session loads and the UI is ready.
onConfirmed() => voidCalled when the customer confirms payment, before the final result.
onError(error: CheckoutError) => voidCalled when an error occurs.

CheckoutInstance

Returned by xpay.checkout(). Imperative handle.

interface CheckoutInstance {
  open(): void; // modal mode
  close(): void;
  destroy(): void;
  on(event: "complete" | "close" | "ready" | "confirmed" | "error", handler): void;
  off(event: string, handler): void;
}

CheckoutCompleteResult (the payload for onComplete):

FieldTypeDescription
status"succeeded"Always "succeeded" for this callback.
paymentIntentIdstringThe Payment Intent ID for server-side verification.
chargeIdstringOptional. The Charge ID, when available.
redirectUrlstringOptional. The session's after-completion redirect URL.

InitCheckoutOptions

Modern single-call API. Combines elements() with session loading and exposes session data plus action methods on a single object.

const checkout = await xpay.initCheckout({ clientSecret: "cs_test_..." });
console.log(checkout.amountTotal); // session field
const result = await checkout.confirm({ customerDetails: { email } });
FieldTypeDescription
clientSecretstring | Promise<string>Required. The session client secret.
appearanceAppearanceOptional. UI overrides.
locale"en" | "ar"Optional. Defaults to "en".

InitCheckoutResult is CheckoutSession & CheckoutActions. The action methods on CheckoutActions:

MethodWhat it does
confirm(options?)Confirms the payment. Returns ActionResult.
applyPromotionCode(code)Applies a code. Returns ActionResult.
removePromotionCode()Removes the applied code. Returns ActionResult.
updateLineItemQuantity({ lineItem, quantity })Updates a line item quantity. Returns ActionResult.
submit()Validates fields. Returns { error?, selectedPaymentMethod? }.
fetchUpdates()Re-fetches the session from the server and resolves with the fresh data. Returns ActionResult.
changeAppearance(appearance)Updates branding at runtime.
on(event, handler)Listen for "change", "error", or a custom event.
getElements()Returns the underlying Elements for element creation.

CheckoutSession (data type)

Session data exposed to the merchant. Derived from the server's response, trimmed to the merchant-facing fields.

FieldTypeDescription
idstringThe cs_* session ID.
amountSubtotalnumberSubtotal in minor units (e.g. 50000 for 500.00 EGP).
amountTotalnumberTotal in minor units after fees, taxes, and discounts.
currencystringISO currency code (e.g. "EGP").
merchantNamestringYour business display name.
livemodebooleantrue for live keys; false for test mode.
expiresAtstringISO timestamp when the session expires.
statusSessionStatusTagged union: open, expired, complete.
canConfirmbooleanWhether the session can currently be confirmed.
paymentMethodsPaymentMethodInfo[]Available payment methods on this session.
presentmentDetailsobjectOptional. Customer-facing amounts when the store prices in another currency. See below.
lineItemsCheckoutLineItem[]Line items.
totalDetailsCheckoutTotalDetailsSubtotal, tax, shipping, discount breakdown.
feesCheckoutFeesFee breakdown when feesPassThrough is on.
discountsCheckoutDiscount[]Applied promotion codes.

presentmentDetails

Present only when the store prices in a currency other than the processing currency. It carries the customer-facing amounts: amountTotal, amountSubtotal, amountDiscount, currency, and the locked exchange rate. Read amounts presentment-first: use presentmentDetails.amountTotal and presentmentDetails.currency when the field is present, the top-level amountTotal and currency otherwise.

SessionStatus:

type SessionStatus =
  | { type: "open" }
  | { type: "expired" }
  | { type: "complete"; paymentStatus: "paid" | "unpaid" | "no_payment_required" };

PaymentMethodInfo:

FieldTypeDescription
typestringMethod type ("card", "valu", "fawry").
displayNamestringLocalized label ("Card", "ValU", "Fawry").
category"card" | "bnpl" | "kiosk" | "wallet"Bucket for UI grouping.
iconstringOptional icon URL.
nextActionTextstringOptional. Description of the next step shown to the customer.

ActionResult and XPayError

Every action that mutates the session returns an ActionResult. It's a tagged union, so narrow by result.type before reading the payload.

type ActionResult<E = XPayError> =
  | { type: "success"; session: CheckoutSession }
  | { type: "error"; error: E };

XPayError is the unified error shape. Payment-specific fields are null for non-payment errors.

FieldTypeDescription
typestringError category: "card_error", "invalid_request_error", "api_error".
codestring | nullMachine-readable code ("card_declined", "promotion_code_not_found").
messagestringHuman-readable message.
paramstring | nullParameter that caused the error (e.g. "promotionCode").
docUrlstring | nullDocumentation URL for this error code.
declineCodestring | nullDecline detail like "insufficient_funds". Null for non-payment errors.
adviceCodestring | nullRetry advice: "try_again_later", "do_not_try_again", "confirm_card_data".
chargeIdstring | nullFailed charge ID.
paymentMethodIdstring | nullFailed payment method ID.
paymentMethodTypestring | nullPayment method type ("card", "valu").
paymentMethodRecord<string, unknown> | nullPayment method snapshot at failure time.

For a guide on handling API errors and the three error code spaces, see Errors, API error codes, Payment error codes, and Decline codes.

Appearance

Branding overrides for the embedded UI. Mirrors a subset of the merchant-default fields you set in Branding settings.

FieldValues
colorMode"system" | "light" | "dark"
borderStyle"rounded" | "sharp" | "pill"
spacing"condensed" | "normal" | "spacious"
inputSize"small" | "medium" | "large"
inputStyle"outlined" | "flat" | "filled"
formLayout"compact" | "spacious"
colorsObject with twelve semantic tokens (primary, foreground, background, muted, accent, border, input, ring, destructive, plus the foreground pairs).
fontFamilyCSS font-family string.

Pass an Appearance to xpay.elements(), xpay.checkout(), or xpay.initCheckout(). Or call elements.changeAppearance() / checkout.changeAppearance() to update at runtime.

CustomerDetails and Address

Shapes for customer fields you collect on your form and pass at confirm time.

interface CustomerDetails {
  email?: string;
  name?: string;
  phone?: string;
  billingDetails?: { name?: string; email?: string; phone?: string; address?: Address };
  shipping?: { name?: string; phone?: string; address?: Address };
}

interface Address {
  line1?: string;
  line2?: string;
  city?: string;
  state?: string;
  postalCode?: string;
  country?: string;
}

For the developer-side cross-reference on what each Checkout Session field controls (collection toggles, custom fields, prefill priority), see Customer lifecycle.

Where to next

On this page