Test playground (dev only)

A team-only page on the dev environment that runs Checkout Sessions and Payment Links with a merchant test key.

Status: internal, dev only. https://checkout-test.devfluveo.com/checkout-test exists only on the dev environment and accepts only test keys (sk_test_… / rk_test_…). It is not part of the public API, it is not promoted to production, and it may change or disappear without notice.

The playground is the fastest way for the team to see the real hosted checkout end to end: paste a merchant test key, type an amount, pay on the hosted page, and read back what the API says about the resulting session. Nothing here bypasses the public /v1 API — the page is a thin, host-gated relay inside the dashboard app that makes the same calls you would make from your own server.

How the team uses it

  1. Open https://checkout-test.devfluveo.com/checkout-test. A red banner reads TEST MODE ONLY — dev environment.
  2. Paste a merchant test secret key into the password field. Live keys (sk_live_…, rk_live_…) and processor credentials are refused with 400 live_key_not_allowed before any upstream call.
  3. Enter an amount (decimal, e.g. 42.42), pick the currency (usd, brl, eur, gbp), optionally add a description and a customer e-mail.
  4. Pay on checkout creates POST /v1/checkout/sessions (mode=payment, one line_items[0] from your amount) and redirects the browser to the returned hosted checkout URL on pay.devfluveo.com. Pay there with a test card.
  5. Fluveo sends you back to /checkout-test/return?session_id=cs_…. The return page re-reads the session (GET /v1/checkout/sessions/{id}) and shows the cs_ id, the pi_ id (and a ch_ id when an event exposes one), amount_total, currency, status and payment_status, followed by GET /v1/events?object=pi_… for that payment.
  6. Generate Payment Link posts the same amount/currency/description to POST /v1/payment_links and shows the returned shareable url with a copy chip.
  7. Press Forget key when you are done. Closing the tab has the same effect.

Where the key goes — and where it never goes

  • The key travels once per action from the browser to a route inside the dashboard app on this hostname, which forwards it as Authorization: Bearer to api.devfluveo.com and then drops it. The server does not store it: no cookie, no session, no database, no log line, no URL. Upstream error bodies are redacted before they are logged or shown.
  • Between the checkout redirect and the return page the key lives only in the browser tab’s sessionStorage. It dies with the tab and is never sent implicitly. If the tab lost it, the return page asks you to type it again.
  • Responses are whitelisted projections of the API objects; the page never echoes request fields back.

Limits (read before relying on it)

  • Dev only, test keys only. The routes and pages answer solely on checkout-test.devfluveo.com with FLUVEO_CHECKOUT_PLAYGROUND=1; on dashboard.devfluveo.com and on any deployment without the flag every /checkout-test path is a 404. Removing the infra opt-in is a complete kill switch.
  • Events may be unavailable. Merchant-public Events are documented as unavailable (see Checkout overview). Any non-200 answer from GET /v1/events is shown as “Events unavailable”, never as a failure of the session read. The ch_ id depends on those events and may be missing.
  • Rate limit 10/min per client, per pod. The dashboard runs two replicas, so a single client may get up to 20 requests per minute cluster-wide before seeing 429 rate_limited with retry-after. Clients without the edge attestation share one bucket.
  • The playground hostname is not a fully separate site. Its middleware host gate 404s the dashboard pages and every other matched path, but the dashboard’s middleware matcher never sees /api/auth/login, /api/auth/signup, /api/fluveo/* or paths containing a dot, so those handlers also answer on checkout-test.devfluveo.com. Each is self-gated (credentials, dashboard session cookie, proxy scope) and carries no privilege beyond what dashboard.devfluveo.com already exposes; the assets under /_next and /favicon are served as well.
  • Reachability is an operator step. The HTTPRoute ships with the dev edge and the Terraform-owned ACM certificate (*.devfluveo.com, infra/terraform/envs/dev/alb.tf) already covers the hostname; only the Cloudflare CNAME checkout-test.devfluveo.com → <gateway-alb-hostname> is created by an operator. Until then the hostname does not resolve; the app-side flag makes that harmless.
  • rk_test_ keys are accepted by the page; whether the API issues them is an upstream question, and an upstream 401 is shown as upstream_error.

The decision record is ADR 0058 (checkout test playground) in the repository.