Skip to content

@movmo/express-checkout-button

@movmo/express-checkout-button embeds the Movmo Express Checkout flow as a “Buy with Movmo” button on a partner page. When the user clicks it, the button persists the selected flight offer to Movmo and opens the Movmo checkout SPA in a full-screen iframe over the partner page. Payment, passenger collection, and the airline booking call all happen inside the iframe — the partner page stays in control of the surrounding URL.

It ships in two forms; both render the same button and the same checkout:

  • A React component (MovmoCheckoutButton) for React 16.8+ apps.
  • A self-contained script tag (since 1.9.0) for everything else — Vue, Angular, server-rendered templates, plain HTML. No build step, no framework, no npm install: the bundle carries React internally and injects its own styles.
Current version1.10.1
Peer dependenciesReact version — React 16.8+ (16.8 / 17 / 18). Script-tag version — none.
LicenseMIT
npm@movmo/express-checkout-button
CDN (script tag)https://cdn.e2e.movmo.io/sdk/express-checkout/v1/express-checkout.js
Terminal window
npm install @movmo/express-checkout-button

Import the package stylesheet once at app boot:

import "@movmo/express-checkout-button/dist/index.css";

The CSS does not include Tailwind’s preflight reset, so it will not interfere with your existing styles. It imports the Inter font from Google Fonts; override font-family if you serve fonts locally.

Add one script tag and mount the button into any element. This is the complete integration:

<div id="movmo-checkout-button" style="width: 240px; height: 44px"></div>
<script src="https://cdn.e2e.movmo.io/sdk/express-checkout/v1/express-checkout.js"></script>
<script>
MovmoExpressCheckout.mount("#movmo-checkout-button", {
iataCode: "PR",
apiKey: "YOUR_MOVMO_API_KEY",
providerID: "amadeus",
flightOffer: selectedOffer,
});
</script>

The /v1/ URL auto-updates across releases, which is why the example carries no integrity attribute — if your security policy requires script-integrity verification, use the version-pinned + SRI form below instead.

The bundle is fully self-contained (~66 KB gzipped): React is compiled in, and the button’s styles are injected at load — you do not need a <link> tag, a stylesheet import, or React on your page. If your page already runs React, the two copies do not interact; the button manages its own isolated root.

mount(target, options) takes a CSS selector or a DOM element, plus the same options as the React component’s props (below). It returns a handle:

const handle = MovmoExpressCheckout.mount(el, options);
handle.unmount(); // remove the button and clean up
MovmoExpressCheckout.version; // e.g. "1.10.1"

mount() validates its options at the call boundary and throws with a message naming exactly what is missing (apiKey, iataCode, providerID, and one of flightOffer/flightOfferID), and throws if the selector matches no element. Calling mount() a second time on the same element logs a warning and returns the existing handle instead of double-rendering.

The /v1/ URL above tracks the latest 1.x release — new releases apply automatically with no change on your side. If your security policy requires it (e.g. PCI DSS script-integrity controls), pin an exact version and verify it with SRI. Per-version bundles are immutable, and each release publishes its sha384 hash as a sidecar file next to the bundle — fetch https://cdn.e2e.movmo.io/sdk/express-checkout/{version}/express-checkout.js.sri (e.g. for 1.10.1) to get the exact integrity value:

<script
src="https://cdn.e2e.movmo.io/sdk/express-checkout/1.10.1/express-checkout.js"
integrity="sha384-keTPgRzv5TRDUPiYlyJDPj1qUHUWu0gbBYaB6j+Kqdzrp9HvvV8u1DX1NEe5mgiS"
crossorigin="anonymous"
></script>

An SRI-pinned tag does not auto-update — you take new releases by updating the URL and hash together.

import { MovmoCheckoutButton } from "@movmo/express-checkout-button";
<MovmoCheckoutButton
iataCode="PR"
apiKey={process.env.REACT_APP_MOVMO_API_KEY}
providerID="amadeus"
flightOffer={selectedOffer}
isProd={false}
/>;

On click, the component:

  1. Saves the offer to Movmo via createProviderOffer (POST /v1/flights/providers/{provider}/offers) — skipped if you pass a pre-saved flightOfferID instead of a raw flightOffer.
  2. Pushes a /movmo/checkout URL onto window.history so the partner page can route around the iframe.
  3. Mounts a full-screen <iframe> pointing at https://flights.movmo.io/index.html#/booking?... (or https://flights.e2e.movmo.io/... when isProd is false).
  4. Listens for a MOVMO_BOOKING_COMPLETE postMessage from the iframe origin and, when the booking lands, replaces the checkout history entry with /movmo/checkout/booking/{bookingId} (a replaceState, not a push — so when the traveler later clicks “Return to airline” inside the confirmation, a single back-navigation lands on your real pre-checkout page instead of a stale checkout entry).
PropTypeRequiredDescription
iataCodestringyesYour airline IATA code (e.g. "PR", "AA"). Sent to Movmo as movmo-customer-id.
apiKeystringyesYour Movmo API Gateway key. Sent as x-api-key. Provisioned at onboarding — see Authentication.
providerIDstringyesThe flight provider the offer came from. One of: amadeus, sabre, duffel, darwin, dohop.
flightOfferobjectconditionalThe raw flight offer object from your provider. Required if flightOfferID is not set.
flightOfferIDstringconditionalThe Movmo offer UUID returned by a prior createProviderOffer call. Required if flightOffer is not set.
isProdbooleannotrue targets flights.movmo.io and api.movmo.io; false (default) targets flights.e2e.movmo.io and e2e.api.movmo.io.
classNamestringnoAppended to the button’s class list. Useful for w-full and similar layout tweaks (React consumers — see Styling).
onExitfunctionnoCalled after the user leaves checkout. Receives { reason }: "search_again" or "offer_unavailable" when checkout ended without a booking, or "booking_confirmed" when the traveler clicked “Return to airline” on the confirmation screen (the booking succeeded — a MOVMO_BOOKING_COMPLETE was already delivered for it). The overlay is already removed and the URL restored when it fires. Use it to return the user to the right step of your flow.

Exactly one of flightOffer or flightOfferID must be set. The two props are mutually exclusive: pass the raw offer if you have not persisted it yet, or the saved offer ID if you already have. The script-tag mount() accepts exactly this options object.

No styling is required. The button ships with Movmo’s standard look (the “Express Checkout” label with the Movmo badge) fully self-contained — in React via the package stylesheet, in the script-tag build via auto-injected styles. Dropping it onto an unstyled page yields the finished, branded button; you only control the size of the element you mount it into.

To match your own design system:

  • React: pass utility classes via className — your app’s build compiles them, so anything goes.
  • Script tag: className only toggles classes that exist inside the bundle’s own stylesheet, so arbitrary utility classes from your page’s framework will not take effect. Style with plain CSS descendant selectors on your mount container instead:
#movmo-checkout-button button {
background: #fafaf9;
border: 1px solid #d5d7da;
border-radius: 8px;
color: #525961;
}

Keep the button’s “Express Checkout” label and Movmo badge visible — they are part of the checkout trust contract shown to travelers.

The partner page already has the user’s selected offer (e.g. from an Amadeus search response). Pass the raw object — the component persists it on click:

<MovmoCheckoutButton
iataCode="PR"
apiKey={process.env.REACT_APP_MOVMO_API_KEY}
providerID="amadeus"
flightOffer={{
offer: amadeusOffer,
dictionaries: amadeusDictionaries,
}}
/>

The component sends the object as-is to createProviderOffer. Movmo’s backend understands each provider’s native shape — no client-side normalization is required.

The partner has already saved the offer to Movmo (for example, after a server-side search) and only needs to launch checkout:

<MovmoCheckoutButton
iataCode="PR"
apiKey={process.env.REACT_APP_MOVMO_API_KEY}
providerID="amadeus"
flightOfferID="33333333-3333-3333-3333-333333333333"
/>

No persistence call is made — the iframe loads immediately.

Once mounted, the iframe overlay is full-screen (position: fixed, z-index: 9999). The Movmo checkout SPA inside handles passenger collection, payment-method selection, and the airline booking call.

On a successful booking, the iframe posts a message with this shape:

type MovmoBookingComplete = {
type: "MOVMO_BOOKING_COMPLETE";
bookingId: string;
airlineRef: string;
providerId: string;
};

The component listens only to messages from flights.movmo.io (or flights.e2e.movmo.io in pre-prod) and pushes /movmo/checkout/booking/{bookingId} onto the partner page’s history. If you want to redirect to your own confirmation page or fire analytics, listen to the same message on your end and intercept it before the component’s listener runs.

If checkout ends without a booking — the offer expired or became unavailable and the traveler chose to go back — the iframe posts MOVMO_BOOKING_FAILED with a reason. The button closes the overlay, restores the URL, and then invokes your onExit({ reason }) callback so you can return the traveler to the right step (your search results, your passenger form, etc.). If you don’t pass onExit, the overlay simply closes in place.

The button’s footprint on your page, the PCI boundary for card data, and Movmo’s data-handling practices are covered on the Security & data handling page — useful reading for your security team during integration review.

The SDK calls one OpenAPI operation in raw-offer mode:

OperationPath
createProviderOfferPOST /v1/flights/providers/{provider}/offers

In flightOfferID mode, no REST call is made from the partner page — the iframe handles all subsequent calls itself. Full schemas in the OpenAPI spec under tags: [Flights].