Fiat checkouttslive · you pay per call

Pay a merchant by card — no account

A shopper with no Nevermined account and no API key pays a merchant an arbitrary fiat amount by card via Stripe, in the browser — the Nevermined Orders flow. The merchant org creates the order server-side; the buyer just pays a hosted checkout embedded in the chat.

fiat-checkout-chat/
Nevermined OrdersStripeNext.jsReact 19TypeScript
1 What you'll learn

Take a fiat card payment from a buyer with no Nevermined account, no API key, no wallet.

  • Setup: the merchant must have a Nevermined organization account with a Stripe account linked to it (Stripe Connect) — that's what authorizes it to take card payments and receive the funds
  • The organization creates a payable Order server-side with its API key — POST /api/v1/orders
  • The org key stays in the merchant backend; the browser only ever calls your own /api/orders
  • The buyer pays a hosted Stripe checkout embedded as an iframe — no login, no crypto
  • The chat trusts the iframe's nvm:success message only after checking event.origin and the envelope version
2 How it works

One-time setup: the merchant registers a Nevermined organization and links a Stripe account to it (Stripe Connect onboarding), which is what lets the org take card payments and receive the funds.

Then, per purchase: the shopper picks a trip; the app calls its own backend, which calls the Nevermined Orders API with the organization's key and gets back an orderId (the price is looked up server-side, so a tampered client can't name its own amount). The chat mounts the hosted Stripe checkout for that order in an iframe. The buyer pays with a test card; the iframe postMessages nvm:success, the chat verifies event.origin and version === '1', and shows a booked confirmation. clientSecret is never forwarded to the browser — the hosted checkout fetches the order itself.

pick a trip"book the Barcelona trip"
POST /api/ordersbackend → Orders API (org key)
iframe checkouthosted Stripe · no account
nvm:successorigin + version verified → booked
3 Under the hood
3.1 Merchant backend

The only holder of the org key — a Next.js server route. The browser calls this, never the Orders API directly.

# src/app/api/orders/route.ts
export async function POST(req: Request) {
  const { packageId } = await req.json()
  const pkg = getPackage(packageId)            // price is OURS, not the client's
  const res = await fetch(`${NVM_API_BASE_URL}/api/v1/orders`, {
    method: 'POST',
    headers: {
      Authorization: `Bearer ${NVM_ORDER_API_KEY}`,  // secret — server only
      'Content-Type': 'application/json',
    },
    body: JSON.stringify({
      amountMinor: pkg.amountMinor, currency: 'usd', description: pkg.name,
    }),
  })
  const { orderId } = await res.json()         // clientSecret NOT forwarded
  return Response.json({ orderId })
}
3.2 Embed + verified success

Mount the hosted checkout in an iframe; trust nvm:success only from the embed origin and only version 1.

# src/app/chat.tsx
// the hosted Stripe checkout for this order, embedded inline
<iframe src={`${embedBase}/checkout/order/${orderId}` +
  `?parentOrigin=${encodeURIComponent(location.origin)}`} />

// the confirmation is gated on a verified message
window.addEventListener('message', (e) => {
  if (e.origin !== embedBase) return          // only the embed origin
  if (e.data?.type !== 'nvm:success') return  // only our event
  if (e.data?.version !== '1') return         // only the envelope we understand
  showBooked(e.data.payload)                  // { orderId, paymentIntent }
})
src/app/api/orders/route.tsmerchant backend — the only holder of the org key
src/app/chat.tsxthe chat UI, the checkout iframe, and the verified postMessage listener
src/lib/packages.tsserver-owned catalog — prices live here, not on the client
4 See it run
Acme Travel · card checkoutno account needed
Hi! I'm your Acme Travel concierge. Pick a trip and pay by card right here — no account, no login. Which one sounds good?
connecting to secure checkout…

Live Orders flow — selecting a trip sends a real POST /api/v1/orders to the backend (the org key stays server-side), then the hosted Stripe checkout runs in the iframe above. Pay with test card 4242 4242 4242 4242; no real money moves. Runs against the deployed Nevermined Orders sandbox (epic #3238) with an org key — no local stack needed. The full source is in fiat-checkout-chat/.