Managed Pass-Through Billing (Payment Provider)
How operator-managed pass-through billing funds workspace wallets via Stripe
Esta página aún no está disponible en tu idioma.
This document describes how operator-managed pass-through billing (PTB) takes real money through a payment provider to fund workspace wallets. It is only needed when an operator runs managed pass-through billing; self-hosted bring-your-own-key deployments do not use any of it.
The payment provider is Stripe. Operators may run it in test mode for evaluation.
Configuration
Section titled “Configuration”Set these in the .env next to the justfile at the repository root (the justfile loads it for the compose stack):
| Variable | Purpose |
|---|---|
NEXUS_STRIPE_API_KEY |
Stripe secret key (sk_test_... in test mode). Server-side only. |
NEXUS_STRIPE_WEBHOOK_SECRET |
Webhook signing secret (whsec_...) used to verify webhook authenticity. |
NEXUS_STRIPE_PUBLISHABLE_KEY |
Stripe publishable key (pk_test_...). Served to the console at GET /api/payments/config so the billing page can mount Stripe.js. Browser-safe. |
When NEXUS_STRIPE_API_KEY is unset, the control plane disables top-ups and the
managed billing surface stays inactive; BYOK traffic is unaffected.
Local webhook forwarding
Section titled “Local webhook forwarding”The Stripe CLI forwards events to the local control plane and prints the signing
secret to put in .env:
cd nexus && just stripe-webhookThis runs stripe listen --forward-to localhost:14451/api/payments/webhooks/stripe
and warns if the printed secret has drifted from NEXUS_STRIPE_WEBHOOK_SECRET.
In test mode, complete the Payment Element with card 4242 4242 4242 4242, any
future expiry, and any CVC.
Money flow
Section titled “Money flow”flowchart LR client[Payment client] -->|"POST wallet/topups"| cp[control-plane] cp -->|"create PaymentIntent"| stripe[Stripe] stripe -->|"client_secret"| client client -->|"confirm intent"| stripe stripe -->|"payment_intent.succeeded webhook"| cp cp -->|"credit wallet once"| ledger[(workspace_wallet_transactions)]- A workspace manager opens the billing page and clicks Add to credit
balance, enters an amount, and the console calls
POST .../wallet/topups. The control plane creates a Stripe payment intent tagged with the workspace id, records apendingworkspace_wallet_topupsrow, and returns the intent’sclient_secret. - The console mounts Stripe’s Payment Element with that
client_secret(using the publishable key fromGET /api/payments/config) and confirms the payment. - Stripe delivers a signed
payment_intent.succeededwebhook. The control plane verifies the signature, records the event once (unique on(provider, provider_event_id)), and credits the wallet with atopuptransaction for the recorded top-up amount, keyed on the event id. - A duplicate webhook delivery is dropped once the event is processed, so the wallet is credited exactly once.
Every money-moving action emits an audit event: wallet.topup.succeeded,
wallet.refund.created, and wallet.reconciliation.mismatch.
See Threat Model rows T4 and I6 for the webhook-forgery and secret-key-leak mitigations covering this flow.
Operator grants
Section titled “Operator grants”An authenticated platform administrator can issue operator-funded workspace credit through the admin control plane:
POST /api/workspaces/{workspace_id}/wallet/grantsContent-Type: application/json
{ "amount_usd": "100.00", "reason": "Pilot credit", "idempotency_key": "pilot-credit-001"}The amount must be positive, and the reason and idempotency key are required.
The operation increases the workspace wallet balance, records a grant
transaction, and emits wallet.grant.created with the authenticated admin as
the actor.
Repeating the same idempotency key for the workspace returns the original
transaction without adding credit again.
Unlike a top-up, a grant does not collect a payment; it authorizes
operator-funded PTB usage.
Refunds
Section titled “Refunds”A manager can refund a settled payment via POST .../wallet/refunds with the
payment intent id and amount. The control plane issues the Stripe refund and
records a refund transaction (idempotent on the refund id) plus an audit event.
Reconciliation
Section titled “Reconciliation”reconcile_workspace_topups compares settled top-ups against wallet credit
transactions and emits wallet.reconciliation.mismatch for any settled top-up
without a matching credit. The gateway separately reconciles stale escrow
reservations.
CSV export
Section titled “CSV export”GET .../wallet/transactions/export.csv streams the wallet ledger as CSV for
tenant audit. The console exposes this as an export button on the billing page.
Tax boundary
Section titled “Tax boundary”nexus is not the system of record for tax. When the payment provider
calculates tax, the provider’s receipt/invoice is canonical; nexus stores a
minimal receipt reference in workspace_billing_receipts. The CSV export is the
tenant-facing audit trail for wallet movements. Multi-currency wallets,
provider-issued invoices, and automated tax computation are out of scope for the
current implementation.
Auto-refill
Section titled “Auto-refill”Auto-refill settings (trigger and amount) are stored per workspace but are not
acted on: nothing charges a saved payment method when the balance crosses the
trigger. The workspace_payment_methods table holds saved-method references,
but no capture flow is implemented against them.
