Skip to content

Integrations

On this page

Hosted Checkout

Redirect customers to Hosted Checkout or embed the payment page in a restricted iframe, then fulfil orders from the signed confirmation webhook.

Use the payment_url returned by POST /v1/invoices as the canonical checkout URL. The same URL supports two presentation modes — full-page redirect and embedded iframe.

Redirect mode

Redirect mode is the simplest integration. Send the customer to payment_url after your server creates the invoice:

<a href="https://checkout.paymos.io/invoice/inv_xxx">Pay invoice</a>

The customer completes the payment on the Paymos-hosted page. Your server fulfills the order only after receiving and verifying the signed webhook event.

Embed mode

Embed mode uses the same payment_url with ?embed=true. Use this when your server already creates invoices and you only want the hosted checkout UI inside your own page:

<iframe
  src="https://checkout.paymos.io/invoice/inv_xxx?embed=true"
  title="Paymos checkout"
  allow="clipboard-write"
  sandbox="allow-scripts allow-same-origin allow-forms"
  style="display:block;width:100%;max-width:460px;height:min(90vh,900px);border:0;"
></iframe>

Embed events

The checkout iframe sends postMessage transport events to the parent page:

Event Meaning
paymos:succeeded Checkout reached paid or paid_over.
paymos:failed Checkout reached a terminal failure state such as underpaid, expired, or cancelled.
paymos:close Customer clicked a close/back action inside the embedded checkout.

Parent pages must validate both the sender origin and the iframe source:

const iframe = document.querySelector('#paymos-checkout');

window.addEventListener('message', (event) => {
  if (event.origin !== 'https://paymos.io') return;
  if (event.source !== iframe.contentWindow) return;
  if (!event.data || typeof event.data !== 'object') return;

  switch (event.data.type) {
    case 'paymos:succeeded':
      console.log('Payment UI succeeded:', event.data.invoice_id);
      break;
    case 'paymos:failed':
      console.warn('Payment UI failed:', event.data.reason);
      break;
    case 'paymos:close':
      iframe.remove();
      break;
  }
});

Treat these events as UX hints, not proof of payment. Drive spinners, redirects, and analytics from them — but never ship goods, credit balances, or mark an order paid based on a postMessage. A postMessage is trivial to spoof: any page sharing the customer's origin can post a fake paymos:succeeded. Settlement is confirmed only by the signed server-side webhook — the one source of truth for fulfillment.

Security model

The recommended iframe sandbox is deliberately small:

  • allow-scripts is required for checkout interactivity, API calls, timers, copy buttons, and live status updates.
  • allow-same-origin lets the Paymos-hosted checkout run with its own origin instead of an opaque sandbox origin.
  • allow-forms allows normal form interactions inside checkout.
  • allow-top-navigation is omitted so the iframe cannot navigate the merchant page.
  • allow-popups is omitted because the checkout does not need to open extra browser windows.

When to use Widget SDK instead

Use Widget SDK when you want Paymos to create invoices from browser-side config, render the payment button, manage the modal lifecycle, validate iframe messages, and dispatch paymos:* CustomEvents for you.

Use manual iframe embed when your backend already owns invoice creation and you only need to place the hosted checkout UI inside your page.