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-testexists only on thedevenvironment 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
- Open
https://checkout-test.devfluveo.com/checkout-test. A red banner reads TEST MODE ONLY — dev environment. - Paste a merchant test secret key into the password field. Live keys
(
sk_live_…,rk_live_…) and processor credentials are refused with400 live_key_not_allowedbefore any upstream call. - Enter an amount (decimal, e.g.
42.42) inusdand optionally a description.
Gerar transação (a real test payment)
- 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.
- Optionally pick Manual capture, a receipt e-mail, a statement descriptor suffix and up to 20 metadata pairs.
- Gerar transação sends
POST /v1/payment_intentswithconfirm=true. The result panel shows thepi_id, status, amount, capture method, latest charge, the failure reason (a decline, or the payment’slast_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. - 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.
- 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 sameIdempotency-Key, so it cannot refund twice. The same holds after no answer, a409, any5xx, 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).
Outros testes (Checkout Session and Payment Link)
- Optionally add a customer e-mail. Pay on checkout creates
POST /v1/checkout/sessions(mode=payment, oneline_items[0]from your amount) and redirects the browser to the returned hosted checkout URL onpay.devfluveo.com. Pay there with a test card. - 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 thecs_id, thepi_id (and ach_id when an event exposes one),amount_total,currency,statusandpayment_status, followed by that payment’s events fromGET /v1/events?limit=100(matched ondata.object.id, since Stripe’s list has no per-object filter). - Generate Payment Link posts the same amount and currency as one Stripe
line_items[0](its product name is the description, orPayment link test) toPOST /v1/payment_linksand shows the returned shareableurlwith a copy chip. - 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: Bearertoapi.devfluveo.comand 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
sessionStoragebetween 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.comwithFLUVEO_CHECKOUT_PLAYGROUND=1; ondashboard.devfluveo.comand on any deployment without the flag every/checkout-testpath is a404. 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/eventsis shown as “Events unavailable”, never as a failure of the session read. Thech_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_limitedwithretry-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 oncheckout-test.devfluveo.com. Each is self-gated (credentials, dashboard session cookie, proxy scope) and carries no privilege beyond whatdashboard.devfluveo.comalready exposes; the assets under/_nextand/faviconare 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 CNAMEcheckout-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 upstream401is shown asupstream_error.
The decision record is ADR 0058 (checkout test playground) in the repository.