Transparent checkout with Stripe Elements
With a transparent checkout, the card form is on your page: no redirect to a hosted page, no dashboard. Your customer types the card into fields rendered by Stripe; Pilot Status credits the wallet or saves the card. Use it to fund a workspace — for example, each end customer’s workspace when you resell — from inside your own product. The endpoints are described in Wallet and card via API. This guide shows how the pieces fit.How it works
- Your server asks Pilot Status for an intent, with the API key.
- Your page loads Stripe.js with the
publishableKeyfrom the answer, mounts the Payment Element with theclientSecret, and confirms with Stripe. - Your server calls
sync, which reads the result from Stripe and credits the wallet (or saves the card) at once.
publishableKey and the intents are ours.
1. Your server: create the intent
Expose one route on your backend that calls Pilot Status. With an account key, addx-workspace-id to fund a specific workspace.
server.js (Node 18+)
Idempotency-Key: generate one id per checkout attempt in the browser and reuse it on retries. A retry within 24 hours gets the same intent back instead of a second order.503 PAYMENTS_NOT_CONFIGURED: card payments are unavailable on our side. Show another option — on BRL workspaces, Pix.
2. Your page: mount the Payment Element and confirm
checkout.html
3. Your server: sync and react
sync answers with the result straight away:
orderStatus: "COMPLETED"— the wallet has the money.creditedistrueon the call that credited it, andbalanceis the new balance.orderStatus: "PENDING"— Stripe has not confirmed yet. Do nothing: when Stripe confirms, we credit the wallet on our own.orderStatus: "FAILED"— the payment was cancelled. Start a new attempt with a newIdempotency-Key.
sync or by our own notification from Stripe. To learn about it on your side without polling, subscribe a workspace webhook to wallet.topup_paid: it fires once per credited top-up, for Pix and card alike.
Browser error but charged
stripe.confirmPayment can come back with a network error even when Stripe has already charged the card — the connection dropped after the charge and before the answer reached the browser. This happened in production on 2026-10-29.
Never show “payment failed” on that error alone. For an error of type api_connection_error (or any error that is not a card decline), check with the server before telling the customer anything:
- Poll
POST /v1/billing/payment-intents/{paymentIntentId}/sync— orGET /v1/billing/topups/{orderId}with theorderIdfrom step 1 — every 2–3 seconds for about 30 seconds. - Stop as soon as
syncanswersorderStatus: "COMPLETED"(or the top-up answersstatus: "paid"): the payment went through. - If it is still pending after ~30 s, tell the customer the payment is being confirmed, not that it failed.
syncansweringorderStatus: "FAILED"(or the top-upstatus: "failed") is the only failure you show.
Saving a card without charging
The flow is the same with a setup intent:- Server:
POST /v1/billing/card/setup-intentwith no body →clientSecret,setupIntentId,publishableKey. - Page:
stripe.elements({ clientSecret }), mount"payment", thenstripe.confirmSetup({ elements, redirect: "if_required" }). - Server:
POST /v1/billing/card/setup-intent/{setupIntentId}/sync→saved: trueand thecard(brand, last 4 digits, expiry).
Testing
Use the Stripe test cards only in an environment whosepublishableKey starts with pk_test_. In production (pk_live_) every confirmed payment is a real charge.