Skip to content
In this section

Embeddable checkout widget

Embed the Fynex checkout form as an inline component or popup overlay for test/demo-mode checkout sessions.

The Fynex checkout widget lets you embed the hosted checkout form directly on your own page for test/demo-mode checkout sessions. Instead of redirecting the customer to https://pay.fynex.ai/checkout/{session_id}, you load a small JavaScript SDK that renders the form inside an iframe — on your domain, in your layout.

Important

Widget iframe embedding is currently enabled only for test/demo-mode payments. Live cardholder checkout pages are protected with same-origin frame headers; use the full-page hosted checkout redirect for live payments.

The widget exposes two modes:

Mode Method Use case
Embed Fynex.embed() Inline form inside a container element on your page.
Popup Fynex.popup() Modal overlay on top of your page.

Note

For test/demo-mode sessions, the widget is an alternative to the full-page redirect described in Hosted Checkout. Both modes share the same checkout session lifecycle — you still create the session server-side and verify the outcome via polling or SSE. For live payments, use hosted checkout redirect.


How it works

Your backendFynex APICustomer browserPOSTsessionIdsessionIdthen verifyCreate sessionPOST /checkoutSession createdreturns sessionIdPass sessionIdto your frontendLoad widget.jsFynex.embed({ sessionId })Checkout iframe/checkout/{id}?mode=embedCustomer fills form3DS handled inside iframepostMessage to pageonSuccess(data), onFailure(data)Verify final stateGET …/payments/{payment_id}

Your backend verifies the final state with GET /payments-api/v1/payments/{payment_id}.


Step-by-step

  1. Create a test/demo checkout session from your backend

This is identical to the Hosted Checkout flow. POST /payments-api/v1/checkout with your Bearer token for a test/demo seller or terminal. See Hosted Checkout — Step 1 for the full request reference.

   curl -sS -X POST https://api.fynex.ai/payments-api/v1/checkout \
     -H "Authorization: Bearer $FYNEX_TOKEN" \
     -H "Content-Type: application/json" \
     -H "Idempotency-Key: $(uuidgen)" \
     -d '{
       "externalOrderRef": "ORDER-2099",
       "amount": 79.00,
       "currencyCode": "GBP",
       "countryCode": "GB",
       "returnUrls": {
         "success": "https://example.com/orders/2099/success",
         "failure": "https://example.com/orders/2099/failure"
       }
     }'
   # Response: { "sessionId": "6f9b84e1-...", "checkoutUrl": "...", "expiresAt": "..." }

Pass the sessionId to your frontend — do not expose your Bearer token to the browser.

  1. Include the widget script

Add the script tag to your page. It must load before you call Fynex.embed() or Fynex.popup(). The script is served with Cache-Control: public, max-age=3600 and Access-Control-Allow-Origin: *, so it can be loaded from any domain.

   <script src="https://api.fynex.ai/widget/checkout.js"></script>

No API key or auth header is needed for the script itself — it is a public static asset.

  1. Launch the widget

Choose embed (inline) or popup (modal) depending on your UX preference.

Embed mode — renders the checkout form inside a container element you control:

   <div id="payment-form"></div>

   <script>
     var checkout = Fynex.embed({
       sessionId: SESSION_ID,      // string — from your backend
       container: '#payment-form', // CSS selector or DOM element
       onSuccess: function(data) {
         console.log('Payment succeeded', data);
         // Do NOT fulfil the order here — verify server-side first
       },
       onFailure: function(data) {
         console.log('Payment failed', data);
       }
     });

     // To remove the widget later (e.g. after navigating away):
     // checkout.destroy();
   </script>

Popup mode — opens a modal overlay over your page:

   <button id="pay-btn">Pay now</button>

   <script>
     document.getElementById('pay-btn').addEventListener('click', function() {
       var popup = Fynex.popup({
         sessionId: SESSION_ID,
         onSuccess: function(data) {
           console.log('Payment succeeded', data);
         },
         onFailure: function(data) {
           console.log('Payment failed', data);
         },
         onClose: function() {
           console.log('Customer closed the popup');
         }
       });

       // To close programmatically:
       // popup.close();
     });
   </script>
  1. Verify the payment server-side

The onSuccess callback is a client-side signal only — it fires when the iframe posts a success message to your page. A determined user could fire a postMessage manually. Always verify the final payment state on your backend before fulfilling the order.

   // On your server, after onSuccess fires and the browser POSTs your endpoint:
   const res = await fetch(
     `${process.env.FYNEX_API}/payments-api/v1/payments/${encodeURIComponent(externalOrderRef)}`,
     { headers: { Authorization: `Bearer ${process.env.FYNEX_TOKEN}` } }
   );
   const payment = await res.json();
   if (payment.lifecycleStatus === 'captured') {
     fulfillOrder();
   }

See Polling & SSE for a complete server-side verification reference.


API reference

Fynex.embed(opts) → { destroy() }

Option Type Required Description
sessionId string Yes The checkout session ID returned by POST /payments-api/v1/checkout.
container string | Element Yes CSS selector (e.g. '#payment-form') or a DOM element reference.
onSuccess function(data) No Called when the payment completes successfully.
onFailure function(data) No Called when the payment fails.

Returns an object with a destroy() method that removes the iframe and cleans up event listeners.

Fynex.popup(opts) → { close() }

Option Type Required Description
sessionId string Yes The checkout session ID.
onSuccess function(data) No Called when the payment completes successfully.
onFailure function(data) No Called when the payment fails.
onClose function() No Called when the overlay is dismissed (close button or click-outside).

Returns an object with a close() method that dismisses the overlay programmatically.

Events (postMessage)

The widget communicates via window.postMessage. The widget SDK handles these internally; you interact through the callbacks above. Messages carry { type: 'fynex-checkout', event: 'success' | 'failure' | 'close' | 'resize', data: {} }.


Common pitfalls

Warning

Do not fulfil orders from onSuccess alone. The callback fires on a client-side postMessage. Always verify the payment state server-side — GET /payments-api/v1/payments/{payment_id} (your externalOrderRef) with your bearer token, or the GraphQL genericPayment query from a dashboard session — before dispatching goods or services.

Caution

Session expiry. Checkout sessions have a server-side TTL (returned as expiresAt). If the customer takes too long, the iframe will show an expiry error. Create a fresh session and re-initialise the widget.

  • container not found. Fynex.embed throws "Fynex.embed: container not found" if the selector matches no element. Ensure the DOM node exists before calling embed().
  • sessionId is required. Both embed and popup throw immediately if sessionId is missing or falsy.
  • Widget behind a strict CSP. The iframe loads https://api.fynex.ai. Ensure your Content-Security-Policy frame-src directive allows https://api.fynex.ai.
  • Live checkout sessions. Live payment pages are not iframe-embeddable by default. If you pass a live sessionId to the widget, browser frame protections can block the iframe. Use the hosted checkout redirect for live payments.
  • 3DS inside the iframe. For test/demo widget sessions, 3DS challenges are handled entirely within the embedded iframe — your page does not need to manage any redirect. The onSuccess / onFailure callback fires once the full payment flow (including any 3DS step) is complete.

See also