- Frontend
- A Next.js app that renders checkout in a cross-origin iframe, using server-side rendering to move some static rendering and heavy logic off the client.
- Backend-for-Frontend (BFF)
- A Node.js layer in the same service that orchestrates checkout: validation, subscription and entitlement context, country-specific tax context, and order flows, so the client doesn't call several backend services itself.
- Internal services
- The BFF talks to internal billing and entitlement services over native gRPC, with TypeScript contracts generated from Protocol Buffers.
- Payments
- Stripe Elements (the Payment Element) renders Stripe-hosted card fields inside checkout. The publishable key only identifies Miro's Stripe account, so it is safe in the browser; the client secret, returned by the BFF's init flow for one specific payment or setup, lets the browser confirm it directly with Stripe. Raw card details go to Stripe and stay outside Miro's application backend, which reduces the associated PCI DSS scope.
- Subscriptions
- Stigg handles the catalog, entitlements, and plan, seat, and add-on changes.
- Tax and currency
- Stripe calculates tax. The frontend and BFF stay agnostic to country-specific tax/VAT rule combinations, and checkout and order flows support multiple currencies.
- Asynchronous outcomes
- 3D Secure, webhooks, and delayed confirmations are handled as events: the UI listens across the iframe boundary and the BFF processes webhooks, rather than relying on a single request/response.
- Payment providers
- We prepared the system with a payment-provider abstraction layer designed to accommodate multiple PSP integrations, keeping provider-specific concerns separate from the core checkout flow.