Skip to content

The xMoney.js v2 SDK (xmoney.js) powers Embedded Checkout, allowing you to securely embed a payment form — or individual payment components — directly into your website. It handles input validation, secure card data collection, and payment processing without sensitive data ever touching your servers.

Embedded Checkout keeps customers on your site throughout the payment process, unlike Hosted Checkout which redirects to xMoney's payment page.

Installation

Include the v2 SDK script on every page where you want to render a payment component.

<!-- Production -->
<script src="https://secure.xmoney.com/sdk/v2/xmoney.js"></script>

<!-- Staging -->
<script src="https://secure-stage.xmoney.com/sdk/v2/xmoney.js"></script>

The SDK exposes a global window.XMoney object that contains all payment factories:

window.XMoney.paymentForm(config)
window.XMoney.paymentCard(config)
window.XMoney.applePay(config)
window.XMoney.googlePay(config)
window.XMoney.savedCardPayment(config)
window.XMoney.getPaymentMethodCapabilities()

About Embedded Checkout

Embedded Checkout is xMoney's solution for accepting payments directly on your website. Payment UI is embedded in your page using iframes, providing a seamless checkout experience without redirecting customers away from your site.

Key Benefits:

  • Customers stay on your website throughout checkout
  • Full control over checkout page design and flow
  • PCI DSS compliant — card data never touches your servers
  • Supports cards, Google Pay, Apple Pay, and saved cards
  • Compose a single full-featured form or build a custom multi-method layout

For redirect-based checkout flows, see Hosted Checkout.

SDK Methods Overview

The v2 SDK is modular: each payment method has its own factory. All factories return a Promise that resolves to an instance with lifecycle methods.

MethodPurposeHas container
XMoney.paymentForm()Full-featured form with card input, Google Pay, and Apple PayYes
XMoney.paymentCard()Standalone card input element for custom checkout layoutsYes
XMoney.applePay()Native Apple Pay buttonYes
XMoney.googlePay()Native Google Pay buttonYes
XMoney.savedCardPayment()Headless payment with a previously saved cardNo
XMoney.getPaymentMethodCapabilities()Detect Apple Pay / Google Pay availability on the user's device

Integration Flow

Integrating the v2 SDK into your application involves these steps:

  1. Load the SDK — Add the xmoney.js v2 script tag to your HTML
  2. Create a Payment Intent — Call your backend to obtain orderPayload and orderChecksum
  3. Initialize a payment method — Call the appropriate window.XMoney.*() factory
  4. Handle callbacks — Implement onReady, onPaymentComplete, onPaymentProcessing, onError
  5. Cleanup — Call instance.destroy() when your component unmounts

Checking Payment Method Capabilities

Before rendering Apple Pay or Google Pay buttons, check whether the browser/device supports them:

const capabilities = await window.XMoney.getPaymentMethodCapabilities()

if (capabilities.googlePay.supported) {
  // Render Google Pay button
}

if (capabilities.applePay.supported) {
  // Render Apple Pay button
}

Payment Form

XMoney.paymentForm() renders a full-featured payment form with card input, Google Pay, and Apple Pay in a single embedded component.

Initialization

const paymentForm = await window.XMoney.paymentForm({
  // Required
  container: '#payment-form-container', // element id, CSS selector, or HTMLElement
  orderPayload: '...',                  // Received from your server
  orderChecksum: '...',                 // Received from your server
  publicKey: 'pk_test_12345',

  // Card configuration
  card: {
    validationMode: 'onChange', // 'onSubmit' | 'onChange' | 'onBlur' | 'onTouched'
    savedCards: {
      enabled: true,       // Show saved cards for returning customers
      optInVisible: false, // Show the save-card checkbox
    },
    submitButton: {
      visible: true,
      type: 'pay', // 'book' | 'buy' | 'checkout' | 'donate' | 'order' | 'pay' | 'subscribe' | 'topUp'
    },
  },

  // Digital wallets
  paymentMethods: {
    googlePay: { enabled: true },
    applePay: { enabled: true },
  },

  // Appearance & locale
  options: {
    locale: 'en-US', // 'en-US' | 'el-GR' | 'ro-RO'
    appearance: {
      theme: 'custom', // 'light' | 'dark' | 'custom'
      variables: {
        colorPrimary: '#6366f1',
      },
    },
  },

  // Lifecycle callbacks (all optional)
  onReady: () => console.log('Form is ready'),
  onError: (err) => console.error('Form error', err),
  onPaymentProcessing: (isProcessing) => console.log('Processing...', isProcessing),
  onPaymentComplete: (result) => console.log('Payment completed', result),
})

Configuration Properties

PropertyTypeRequiredDescription
containerstring | HTMLElementYesDOM element ID, CSS selector, or HTMLElement for the form host.
publicKeystringYesYour Site ID public key (pk_test_... or pk_live_...).
orderPayloadstringYesBase64-encoded encrypted order data from your backend.
orderChecksumstringYesHMAC signature of the payload from your backend.
cardobjectNoCard input configuration (see below).
paymentMethodsobjectNoEnable/configure Google Pay and Apple Pay.
optionsobjectNoLocale and appearance customization.
onReady() => voidNoCalled when the form is ready.
onError(err) => voidNoCalled on initialization errors.
onPaymentProcessing(isProcessing: boolean) => voidNoCalled when the submission state changes.
onPaymentComplete(data) => voidNoCalled when payment completes (success or failure).

Card Options

OptionTypeDefaultDescription
card.validationMode'onSubmit' | 'onChange' | 'onBlur' | 'onTouched''onChange'When validation triggers.
card.savedCards.enabledbooleantrueDisplay saved cards for returning customers.
card.savedCards.optInVisiblebooleantrueShow the save-card checkbox on new-card entry.
card.submitButton.visiblebooleantrueIf false, you must call instance.submit() manually.
card.submitButton.type'book' | 'buy' | 'checkout' | 'donate' | 'order' | 'pay' | 'subscribe' | 'topUp''pay'Label on the built-in submit button.
card.cardHolderVerificationobjectOptional cardholder name verification handler.

Instance Methods

// Update order details (e.g., after cart total changes)
paymentForm.updateOrder({
  orderPayload: 'new-base64-payload',
  orderChecksum: 'new-checksum',
})

// Update appearance at runtime
paymentForm.updateAppearance({
  theme: 'dark',
  variables: { colorPrimary: '#ff5722' },
})

// Change language
paymentForm.updateLocale('el-GR')

// Programmatically submit (useful when submitButton.visible is false)
paymentForm.submit()

// Validate without submitting
const { isValid, errors } = paymentForm.validate()

// Cleanup — always call on unmount
paymentForm.destroy()

Payment Card

XMoney.paymentCard() renders only the card input element. Use it when building a custom multi-method checkout where you control the surrounding layout and additional payment buttons.

const paymentCard = await window.XMoney.paymentCard({
  container: '#payment-card-container',
  orderPayload: payload,
  orderChecksum: checksum,
  publicKey: 'pk_test_your_key',

  card: {
    validationMode: 'onChange',
    savedCards: {
      enabled: false,
      optInVisible: true,
    },
    submitButton: {
      visible: true,
      type: 'pay',
    },
  },

  options: {
    locale: 'en-US',
    appearance: {
      theme: 'dark',
      variables: { colorPrimary: '#1976d2' },
    },
  },

  onReady: () => console.log('Payment card ready'),
  onError: (err) => console.error('Payment card error:', err),
  onPaymentProcessing: (isProcessing) => console.log('Processing:', isProcessing),
  onPaymentComplete: (result) => console.log('Payment successful:', result),
})

paymentCard.submit()
const { isValid, errors } = await paymentCard.validate()
paymentCard.destroy()

The paymentCard config is identical to paymentForm, except:

  • No paymentMethods option (card only — add Apple Pay / Google Pay separately via their own factories).
  • submitButton.visible controls whether the built-in submit button is shown.

Apple Pay

Renders a native Apple Pay button. Always check capabilities first — the button should only be shown if the user's device and browser support Apple Pay.

const applePay = await window.XMoney.applePay({
  container: '#apple-pay-button',
  orderPayload: payload,
  orderChecksum: checksum,
  publicKey: 'pk_test_your_key',

  options: {
    locale: 'en-US',
    appearance: {
      style: 'black', // 'white' | 'black' | 'white-outline'
      radius: 12,
      type: 'pay',
    },
  },

  onReady: () => console.log('Apple Pay button ready'),
  onError: (err) => console.error('Apple Pay error:', err),
  onPaymentProcessing: (isProcessing) => console.log('Processing:', isProcessing),
  onPaymentComplete: (result) => console.log('Apple Pay completed:', result),
})

applePay.updateOrder({ orderPayload: 'new-payload', orderChecksum: 'new-checksum' })
applePay.destroy()

Apple Pay button types: 'add-money', 'book', 'buy', 'checkout', 'contribute', 'continue', 'donate', 'order', 'plain', 'pay', 'reload', 'rent', 'set-up', 'subscribe', 'support', 'tip', 'top-up'.

Google Pay

Renders a native Google Pay button.

const googlePay = await window.XMoney.googlePay({
  container: '#google-pay-button',
  orderPayload: payload,
  orderChecksum: checksum,
  publicKey: 'pk_test_your_key',

  options: {
    locale: 'en-US',
    appearance: {
      color: 'black',          // 'white' | 'black'
      radius: 12,
      type: 'pay',             // 'book' | 'buy' | 'checkout' | 'donate' | 'order' | 'plain' | 'pay' | 'subscribe'
      borderType: 'no_border', // 'default_border' | 'no_border'
    },
  },

  onReady: () => console.log('Google Pay button ready'),
  onError: (err) => console.error('Google Pay error:', err),
  onPaymentProcessing: (isProcessing) => console.log('Processing:', isProcessing),
  onPaymentComplete: (result) => console.log('Google Pay completed:', result),
})

googlePay.updateOrder({ orderPayload: 'new-payload', orderChecksum: 'new-checksum' })
googlePay.destroy()

Saved Card Payment

XMoney.savedCardPayment() is headless — it has no container and no UI. Use it to trigger a charge against a previously saved card by its ID (for example, from a one-click "Pay with saved card" button you render yourself).

const savedCard = await window.XMoney.savedCardPayment({
  orderPayload: payload,
  orderChecksum: checksum,
  publicKey: 'pk_test_your_key',

  onReady: () => console.log('Saved card payment ready'),
  onError: (err) => console.error('Saved card payment error:', err),
  onPaymentProcessing: (isProcessing) => console.log('Processing:', isProcessing),
  onPaymentComplete: (result) => console.log('Payment successful:', result),
})

// Trigger payment with a saved card
savedCard.pay({ cardId: 42 })

savedCard.destroy()

Common Instance Methods

All SDK instances returned by the factories share these base methods:

updateOrder({ orderPayload, orderChecksum }): void

Updates transaction details (e.g., when the user changes the cart total) without reloading the iframe.

instance.updateOrder({
  orderPayload: newPayload,
  orderChecksum: newChecksum,
})

Use cases: cart updates, discount codes, shipping changes, dynamic pricing.

Note: XMoney.savedCardPayment() does not support updateOrder — create a fresh instance for a new order instead.

destroy(): void

Removes the iframe and cleans up event listeners. Always call this when the component unmounts to prevent memory leaks.

// React useEffect cleanup example
useEffect(() => {
  let instance
  (async () => {
    instance = await window.XMoney.paymentForm({ /* ... */ })
  })()

  return () => {
    instance?.destroy() // Critical: prevents memory leaks
  }
}, [])

paymentForm / paymentCard extras

  • submit() — Programmatically submit the form.
  • validate() — Run validation without submitting. Returns { isValid, errors }.
  • updateAppearance(appearance) — Update theme/variables at runtime.
  • updateLocale(locale) — Change the form language at runtime.

applePay / googlePay extras

  • updateOrder({ orderPayload, orderChecksum }) — Refresh the order for the button.

savedCardPayment extras

  • pay({ cardId }) — Trigger a charge against a saved card.

Customization (Theming)

paymentForm and paymentCard support full appearance customization via options.appearance.

options: {
  locale: 'en-US', // 'en-US' | 'el-GR' | 'ro-RO'
  appearance: {
    theme: 'custom', // 'light' | 'dark' | 'custom'
    variables: {
      colorPrimary: '#0d9488',         // Brand color (focus, buttons)
      colorDanger: '#ef4444',          // Error text
      colorBackground: '#ffffff',      // Form background
      colorText: '#1f2937',            // Primary text
      colorTextSecondary: '#6b7280',   // Labels / hints
      colorTextPlaceholder: '#bdbdbd', // Placeholder text
      colorBorder: '#e5e7eb',          // Input borders
      colorBorderFocus: '#0d9488',     // Active input border
      colorBackgroundFocus: '#f0fdfa', // Focused input background
      fontFamily: 'Inter, system-ui, sans-serif',
      borderRadius: '8px',
    },
    rules: {
      '.xmoney-input:hover': {
        'box-shadow': '0 2px 4px rgba(0,0,0,0.1)',
      },
    },
  },
}

Theme Options:

  • 'light' — Default light theme (no variables needed).
  • 'dark' — Dark theme optimized for dark backgrounds.
  • 'custom' — Use your own color variables.

Best Practice: When using 'custom', provide all color variables for consistency. The SDK uses sensible defaults for any missing variables.

Button Customization

Apple Pay and Google Pay buttons support their own appearance options:

// Apple Pay
options: {
  appearance: {
    style: 'black', // 'white' | 'black' | 'white-outline'
    radius: 12,
    type: 'pay',
  },
}

// Google Pay
options: {
  appearance: {
    color: 'black',          // 'white' | 'black'
    radius: 12,
    type: 'pay',             // 'book' | 'buy' | 'checkout' | 'donate' | 'order' | 'plain' | 'pay' | 'subscribe'
    borderType: 'no_border', // 'default_border' | 'no_border'
  },
}

Callbacks

All callbacks are optional. They are accepted by every factory (the saved-card factory omits only those that are UI-specific).

CallbackTypeDescription
onReady()() => voidFired when the embedded UI has loaded and is interactive.
onError(err)(err: { code: number; message: string } | string) => voidFired when initialization fails (e.g., invalid configuration, network errors). Not fired for transaction errors — those are delivered via onPaymentComplete.
onPaymentProcessing(isProcessing)(isProcessing: boolean) => voidFired when the component starts (true) or ends (false) network activity. Use it to show/hide loading spinners.
onPaymentComplete(data)(data: TransactionDetails) => voidFired when payment completes, regardless of success or failure. Inspect the transactionStatus field to determine the outcome.
Important security note for onPaymentComplete

While onPaymentComplete is useful for updating your UI (e.g., redirecting to a "Success" page), you must not use it as the sole confirmation to release goods or services.

Always validate the final transaction status server-side by listening to the Instant Payment Notification (IPN) or querying the xMoney API. Server-to-server communication is signed and secure, ensuring the transaction status has not been altered by the client.

Example Usage

Basic Integration

const paymentForm = await window.XMoney.paymentForm({
  container: '#payment-form-widget',
  publicKey: 'pk_test_your_key',
  orderPayload: payload,
  orderChecksum: checksum,

  onReady: () => {
    console.log('Form is ready')
    document.getElementById('payment-form-widget').style.opacity = '1'
  },
  onError: (error) => {
    console.error('Form initialization error:', error)
    alert('Failed to load payment form. Please refresh the page.')
  },
  onPaymentComplete: (data) => {
    console.log('Payment completed:', data)
    // Always confirm the final status with a backend call.
    if (data.transactionStatus === 'complete-ok') {
      window.location.href = '/checkout/success'
    } else {
      alert('Payment failed. Please try again.')
    }
  },
})

With Custom Theme

const paymentForm = await window.XMoney.paymentForm({
  container: '#payment-form-widget',
  publicKey: 'pk_test_your_key',
  orderPayload: payload,
  orderChecksum: checksum,
  options: {
    appearance: {
      theme: 'custom',
      variables: {
        colorPrimary: '#10b981',
        colorDanger: '#ef4444',
        borderRadius: '12px',
      },
    },
  },
})

Custom Multi-Method Checkout

Compose your own layout by mounting each payment method separately:

const capabilities = await window.XMoney.getPaymentMethodCapabilities()

const card = await window.XMoney.paymentCard({
  container: '#card',
  orderPayload: payload,
  orderChecksum: checksum,
  publicKey: 'pk_test_your_key',
  onPaymentComplete: (data) => handleResult(data),
})

if (capabilities.applePay.supported) {
  await window.XMoney.applePay({
    container: '#apple-pay',
    orderPayload: payload,
    orderChecksum: checksum,
    publicKey: 'pk_test_your_key',
    onPaymentComplete: (data) => handleResult(data),
  })
}

if (capabilities.googlePay.supported) {
  await window.XMoney.googlePay({
    container: '#google-pay',
    orderPayload: payload,
    orderChecksum: checksum,
    publicKey: 'pk_test_your_key',
    onPaymentComplete: (data) => handleResult(data),
  })
}

TypeScript Support

The v2 SDK ships with full TypeScript definitions:

import type {
  XMoneyPaymentFormConfig,
  XMoneyPaymentFormInstance,
} from './types/xmoney-sdk/payment-form-sdk.types'
import type { TransactionDetails } from './types/checkout.types'

const config: XMoneyPaymentFormConfig = {
  container: '#payment-form',
  orderChecksum: checksum,
  orderPayload: payload,
  publicKey: PUBLIC_KEY,
  onPaymentComplete: (result: TransactionDetails) => {
    console.log(result.id, result.transactionStatus)
  },
}

const instance: XMoneyPaymentFormInstance = await window.XMoney.paymentForm(config)

Backend API Integration

To initialize Embedded Checkout, create a payment intent on your server that returns the encrypted payload and checksum.

Backend Example

const crypto = require('crypto')

function getBase64JsonRequest(orderData) {
  const jsonText = JSON.stringify(orderData)
  return Buffer.from(jsonText).toString('base64')
}

function getBase64Checksum(orderData, secretKey) {
  const hmacSha512 = crypto.createHmac('sha512', secretKey)
  hmacSha512.update(JSON.stringify(orderData))
  return hmacSha512.digest('base64')
}

// API route handler (Next.js, Express, etc.)
export default async function handler(req: any, res: any) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' })
  }

  const { amount, currency, description, publicKey } = req.body

  // API key is stored server-side (e.g., environment variable)
  const apiKey = process.env.XMONEY_SECRET_KEY

  if (!publicKey || !apiKey) {
    return res.status(400).json({ error: 'Missing credentials' })
  }

  const orderData = {
    publicKey: publicKey,
    customer: {
      identifier: 'customer-123', // Your internal customer ID
      firstName: 'John',
      lastName: 'Doe',
      country: 'RO', // ISO 3166-1 alpha-2
      city: 'Bucharest',
      email: 'john.doe@example.com',
    },
    order: {
      orderId: `order-${Date.now()}`,
      description: description,
      type: 'purchase',
      amount: amount, // Amount in smallest currency unit (cents)
      currency: currency, // ISO 4217 code (e.g., 'EUR', 'USD')
    },
    cardTransactionMode: 'authAndCapture',
    backUrl: 'https://yoursite.com/checkout/result',
  }

  try {
    const payload = getBase64JsonRequest(orderData)
    const checksum = getBase64Checksum(orderData, apiKey)

    res.json({ payload, checksum })
  } catch (error) {
    console.error('Error creating payment intent:', error)
    res.status(500).json({ error: 'Internal server error' })
  }
}

Frontend Integration

async function initializeCheckout() {
  const response = await fetch('/api/create-payment-intent', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      amount: 10000, // Amount in cents
      currency: 'EUR',
      description: 'Order #12345',
      publicKey: 'pk_test_your_key',
    }),
  })

  const { payload, checksum } = await response.json()

  const paymentForm = await window.XMoney.paymentForm({
    container: '#payment-form-widget',
    publicKey: 'pk_test_your_key',
    orderPayload: payload,
    orderChecksum: checksum,
    onReady: () => console.log('Form ready'),
    onPaymentComplete: (data) => {
      console.log('Payment completed:', data)
    },
  })
}

Important: The secret key (sk_...) should always be stored server-side (e.g., environment variables) and never exposed to the frontend.

Browser Support

  • Chrome / Edge (latest)
  • Firefox (latest)
  • Safari (latest)
  • Apple Pay requires Safari on macOS or iOS
  • Google Pay requires Chrome or other supported browsers

Security

  • All payment data is handled securely by the xMoney SDK
  • Order checksums validate request integrity
  • No sensitive card data is stored in your application
  • PCI DSS compliant integration