Skip to navigation

Test playground (dev only)

A team-only page on the dev environment that makes real test payments, 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 make a real test payment and see it in the dashboard: paste a merchant test key, type an amount, pick a test card and press Gerar transação. It can also run the hosted checkout end to end. 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) in usd and optionally a description.

Gerar transação (a real test payment)

  1. Pick a test card by what it should do: Success, Declined (generic), Declined (insufficient funds) or 3D Secure required. These are Stripe’s published test cards; the playground is for US Stripe Connect stores, where they behave as named. The page never asks for a card number.
  2. Optionally pick Manual capture, a receipt e-mail, a statement descriptor suffix and up to 20 metadata pairs.
  3. Gerar transação sends POST /v1/payment_intents with confirm=true. The result panel shows the pi_ id, status, amount, capture method, latest charge, the failure reason (a decline, or the payment’s last_payment_error, for example after a failed 3D Secure), and an Open in the dashboard link to the payment. When a call fails, the page shows the API’s HTTP status and error code.
  4. With 3D Secure required the 3DS page opens in a new tab. Finish it there; that tab says “3D Secure finished” and this tab reads the payment again on its own. Refresh does the same by hand and also lists the payment’s events.
  5. The panel offers Capture and Cancel payment for a manual payment waiting for capture, Refund for a succeeded one (leave the amount empty for the full amount). If a refund says charge_not_settled, wait about a minute and press it again: the same request is sent with the same Idempotency-Key, so it cannot refund twice. The same holds after no answer, a 409, any 5xx, or a success answer the page cannot read: pressing the button again re-sends the same request with the same key. The amount field empties when the payment, its status or its refund changes.

The playground is for US Stripe Connect stores: the store’s Stripe Connect binding processes the payment. Other processors are out of scope for now (later branch).

  1. Optionally add a customer e-mail. 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.
  2. 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 that payment’s events from GET /v1/events?limit=100 (matched on data.object.id, since Stripe’s list has no per-object filter).
  3. Generate Payment Link posts the same amount and currency as one Stripe line_items[0] (its product name is the description, or Payment link test) to POST /v1/payment_links and shows the returned shareable url with a copy chip.
  4. On the return page, 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.
  • Gerar transação stores the key nowhere, browser included: it is read from the password field each time you press a button. The 3DS tab never sees it.
  • Only the Pay on checkout flow keeps the key in the browser tab’s sessionStorage between the checkout redirect and the return page. 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.
  • Payments, captures, cancels and refunds are real test-mode writes on the store the key belongs to. They appear in that store’s dashboard in test mode.
  • The 3DS page comes from Stripe Connect. If the API gives no 3DS page, the panel says so.
  • 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.