Wallet & Payments
Prepaid workspace balance with multiple payment providers and auto-recharge
Every organization has a prepaid wallet. Admins add funds under Settings → Wallet with any payment provider the server has enabled, and can turn on auto-recharge so a saved card tops the balance up whenever it drops below a threshold.
Payment providers
Section titled “Payment providers”Several providers can be enabled at once, so top-ups never depend on a single gateway. Each one is offered as soon as its keys are in the [payments] config (see config.example.toml).
| Provider | What customers pay with | Saved cards / auto-recharge | Test mode |
|---|---|---|---|
| Sandbox (built in) | Test card numbers on an in-app page | Yes | Always; no keys needed |
| Stripe | Cards on Stripe Checkout | Yes | sk_test_… key, card 4242 4242 4242 4242 |
| Razorpay | UPI, cards, netbanking, wallets (Payment Links) | No | rzp_test_… keys |
| PayPal | PayPal balance or cards (Orders v2) | No | live = false (sandbox), sandbox buyer accounts |
Sandbox test cards
Section titled “Sandbox test cards”| Card | Result |
|---|---|
4242 4242 4242 4242, 5555 5555 5555 4444 |
Succeeds |
4000 0000 0000 0002 |
Declined |
4000 0000 0000 9995 |
Insufficient funds |
4000 0000 0000 0341 |
Succeeds now; later auto-recharges with it fail |
Any future expiry date and any 3-digit CVC work.
How a top-up completes
Section titled “How a top-up completes”POST /api/wallet/topupcreates a pending payment and returns the provider’s page.- The customer pays and is sent back to
/settings/wallet?payment=…, which asks the server to confirm with the provider. - Provider webhooks (
/api/wallet/webhooks/{stripe|razorpay|paypal}) and a background check every few minutes also confirm payments, so a closed tab never loses a payment.
The server always reads the result back from the provider’s API with its own keys; a webhook body is never trusted for amounts. Each payment credits the wallet at most once.
Cancellation and expiry
Section titled “Cancellation and expiry”A top-up is only marked canceled once the provider confirms its checkout can no longer be paid: the Stripe Checkout Session is expired, the Razorpay Payment Link is cancelled (links are also created with a 24-hour expire_by), and a PayPal order is simply never captured. If the provider can’t be reached, or reports the payment was made a moment earlier, the payment stays pending or is credited. This happens when the customer backs out of the provider page, and in the background for top-ups still pending after 24 hours; age alone never cancels a payment.
If a provider ever reports a canceled or failed payment as paid, it is credited then, still at most once.
Auto-recharge
Section titled “Auto-recharge”Choose a threshold, an amount and a saved card. When the balance falls below the threshold (after any debit, or at the next once-a-minute check) the card is charged off-session. Only an explicit decline counts as a failed attempt: if the outcome is unknown (timeout, lost response, provider error) the attempt stays pending and is resolved with the same payment id, by replaying it under the same idempotency key or searching Stripe for it, before any new charge is made. Attempts are at least 10 minutes apart, at most 5 succeed or run per 24 hours, the threshold can’t exceed the maximum top-up, and auto-recharge turns itself off after 3 failures in a row, showing the last error on the wallet page.
Charging the wallet
Section titled “Charging the wallet”Server code debits the wallet through wallet.Service.Debit, which records a ledger entry (give each one a stable reference such as message:<id> so a retried event is charged once), refuses to go below zero unless told to, and starts an auto-recharge when needed. Super admins can also credit or debit any workspace by hand under Platform → Organizations → (org) → Wallet (POST /api/admin/organizations/{id}/wallet/adjust).
Permissions
Section titled “Permissions”The billing resource controls access: billing:read to see the wallet, billing:write to add funds and manage auto-recharge and cards. Admin roles get both.