Skip to content

Embedded Checkout

Embedded Checkout is a first-class integration in which your page frames a Maxana-owned checkout experience.

  1. Your server creates a checkout session and sets the merchant pages that are permitted in allowed_origins.

  2. Your page frames the response’s embed_url and delegates browser payment permission to the cross-origin frame:

    <iframe
      src="https://maxanapay.com/embed/cs_abc123"
      title="Secure checkout"
      style="width: 100%; height: 640px; border: 0"
      allow="payment"
    ></iframe>
  3. Your page listens for checkout messages from the frame. The iframe does not navigate your parent page when its state changes, so these messages are the parent UI’s completion and cancellation signal.

  4. Your server verifies the durable payment outcome.

Every message has the envelope shown in the listener below.

  • maxana.checkout.ready means the checkout is loaded and ready for the buyer.
  • maxana.checkout.completed means the payment was captured. Update the buyer experience, but wait for server-side verification before fulfilment.
  • maxana.checkout.pending means the payment is still processing or awaiting a final outcome. Do not fulfil the order yet.
  • maxana.checkout.cancelled means the buyer cancelled the checkout. You may close the frame or offer another way to pay.
  • maxana.checkout.error means the attempted payment failed. Keep the checkout available so the buyer can retry or choose another method.

Outcome messages can include paypalOrderId, amount, currency, or a human-readable message when those values are available. Do not require an optional field before handling the event.

Before trusting a message, compare event.origin with the origin of the session’s returned embed_url. Also require the Maxana source value and the session identifier you created:

type MaxanaCheckoutMessage = {
  source: 'maxana.checkout';
  type:
    | 'maxana.checkout.ready'
    | 'maxana.checkout.completed'
    | 'maxana.checkout.pending'
    | 'maxana.checkout.cancelled'
    | 'maxana.checkout.error';
  sessionId: string;
  paypalOrderId?: string;
  amount?: number;
  currency?: string;
  message?: string;
};

type CheckoutSession = {
  checkout_session_id: string;
  embed_url: string;
};

type CheckoutHandlers = {
  ready: (message: MaxanaCheckoutMessage) => void;
  completed: (message: MaxanaCheckoutMessage) => void;
  pending: (message: MaxanaCheckoutMessage) => void;
  cancelled: (message: MaxanaCheckoutMessage) => void;
  error: (message: MaxanaCheckoutMessage) => void;
};

export function listenForCheckout(
  session: CheckoutSession,
  handlers: CheckoutHandlers,
): () => void {
  const checkoutOrigin = new URL(session.embed_url).origin;
  const receiveMessage = (event: MessageEvent) => {
    if (event.origin !== checkoutOrigin) return;
    if (event.data?.source !== 'maxana.checkout') return;
    if (event.data?.sessionId !== session.checkout_session_id) return;

    const message = event.data as MaxanaCheckoutMessage;
    switch (message.type) {
      case 'maxana.checkout.ready':
        handlers.ready(message);
        break;
      case 'maxana.checkout.completed':
        handlers.completed(message);
        break;
      case 'maxana.checkout.pending':
        handlers.pending(message);
        break;
      case 'maxana.checkout.cancelled':
        handlers.cancelled(message);
        break;
      case 'maxana.checkout.error':
        handlers.error(message);
        break;
    }
  };

  window.addEventListener('message', receiveMessage);
  return () => window.removeEventListener('message', receiveMessage);
}

Origins are exact security inputs, not display metadata. Register only pages that actually host checkout, keep production origins narrow, and test the final HTTPS origin in sandbox before launch. Do not use a wildcard to avoid managing the list.

Embedding provider-hosted fields may reduce the card data your systems touch, but it does not determine your PCI obligations. Confirm your integration and reporting scope with your PCI assessor.

Embedded Checkout remains a Maxana-owned document inside an iframe. It cannot run from an origin absent from the session’s exact allowed_origins, and a wildcard is not accepted. Your page can size and position the frame but does not own the checkout document’s internal markup or payment fields.