# Payments Module — Finradar API > Version: 3.61.0 | Generated: 2026-06-20 | Content Hash: c7239c80 > Fetch this file at: https://uat.finradarapi.com/llms/payments-module.txt ## Authentication All endpoints require an API key. Pass it via query parameter `?apiKey=YOUR_KEY` or header `X-API-Key: YOUR_KEY`. WebSocket endpoints accept the key in the `token` auth payload or query parameter. --- ## Payments Module Programmatic balance management for API usage. ### POST /api/v1/payments/create-intent Create a Stripe Payment Intent for a one-time top-up of the USD `credit_balance` wallet. EXEMPT (`cost: 0`). Returns the client secret — pass to Stripe Elements client-side to collect card details. Minimum top-up: $5. **Token cost:** 0 (EXEMPT — auth / billing / account / admin / health) **Response fields:** - `status` (string): ApiResponse envelope status — `success` on 200, `error` on 4xx/5xx. Always present in the standardised `ApiResponse` wrapper used across `/api/v1/*`. - `request_id` (string (nullable)): Per-request correlation ID (set by Flask `g.request_id`). Pass in support tickets to look up the call in server logs. - `timestamp` (string): ISO-8601 UTC timestamp the response was generated. - `data` (object): Payload object containing the Stripe client secret. - `data.clientSecret` (string): Stripe Payment Intent client secret in `pi_XXXXXXX_secret_YYYYYYY` format. Pass to `stripe.confirmCardPayment(clientSecret)` client-side via Stripe Elements. Single-use — once confirmed (success or failure) it cannot be re-used; create a new intent for a retry. Embeds the user's `uuid` and `type=TOPUP` in Stripe-side metadata so the webhook handler (`_fulfill_topup`) can credit the correct wallet on success. **Since:** v1.0.0 **Utility:** Server-side wrapper for `stripe.PaymentIntent.create` — initialises a one-time top-up of the user's USD credit balance (the `users.credit_balance` wallet, NOT the token ledger). EXEMPT (`cost: 0`). Returns a Stripe `client_secret` which the frontend passes to Stripe Elements (`stripe.confirmCardPayment(clientSecret)`) to collect card details client-side without the card data ever touching our servers (PCI scope-out). On successful charge, Stripe webhooks `payment_intent.succeeded` to /api/v1/payments/webhook, which calls `_fulfill_topup` to credit the wallet. Use [POST /api/v1/payments/verify](/docs/account/payments-module/post-payments-verify) as a fallback when webhook delivery is delayed/lost. NB: this endpoint creates a Stripe Customer record on first use (idempotent — `stripe_customer_id` is cached on the user row). **Use case:** Frontend 'Top up balance' modal: user enters $25 → POST here → receives `client_secret` → Stripe Elements form mounted with the secret → user enters card → `confirmCardPayment` fires → Stripe processes → webhook fulfils → modal polls /api/v1/account/balance to show updated balance. **Parameters:** - `amount` (body, required): Top-up amount in USD (NOT in cents — server multiplies by 100 internally to send `amount_cents` to Stripe). Minimum 5; below 5 returns 400 with `Minimum top-up is $5`. Float accepted (e.g. 12.50) — fractional cents are rounded by `int(amount_dollars * 100)`. **Sample response:** ```json { "status": "success", "request_id": "req_3OqK2jK9L8pQ4xZ1", "timestamp": "2026-05-02T15:51:00.000Z", "data": { "clientSecret": "pi_3OqK2jK9L8pQ4xZ1_secret_xVZq8nL7aP2kT6sB1mDfHj9vC" } } ``` ### POST /api/v1/payments/verify Manually verify a Stripe Payment Intent and fulfill the top-up if it succeeded. EXEMPT (`cost: 0`). Webhook-fallback path — use when /payments/webhook delivery was delayed or lost. Idempotent: a Transaction already linked to the intent is treated as already-processed. **Token cost:** 0 (EXEMPT — auth / billing / account / admin / health) **Response fields:** - `status` (string): ApiResponse envelope status — `success` on 200, `error` on 4xx/5xx. - `request_id` (string (nullable)): Per-request correlation ID. Pass in support tickets. - `timestamp` (string): ISO-8601 UTC timestamp the response was generated. - `data` (object): Payload — verification result + post-fulfilment balance. - `data.status` (string): `verified` when the intent was found, security-checked, and (if not yet processed) fulfilled. Returns 400 with `Payment not successful or pending` if the Stripe intent's status is not `succeeded` (e.g. still `processing` or `requires_action`). - `data.balance` (number): Current `users.credit_balance` in USD AFTER any newly-applied top-up (or unchanged if the verify was a no-op due to prior fulfilment). Use to update the UI balance chip without a separate /account/balance call. **Since:** v1.0.0 **Utility:** Manual fallback for the webhook fulfilment path. EXEMPT (`cost: 0`). Calls `stripe.PaymentIntent.retrieve(payment_intent_id)` server-side; if the intent's `metadata.user_uuid` matches the authenticated user (security check) AND the status is `succeeded` AND no `Transaction` row already references this intent (idempotency), then `_fulfill_topup` runs to credit the wallet — same code path as the webhook handler, so the result is identical. Use this when Stripe webhook delivery was delayed or lost (network blip, infrastructure outage, webhook secret mismatch); the frontend can poll this endpoint for ~30 seconds after `confirmCardPayment` resolves to give the webhook a chance to fire first, then fall back to verify. **Use case:** Frontend top-up flow: after `stripe.confirmCardPayment` resolves with `paymentIntent.status === 'succeeded'`, poll /api/v1/account/balance every 2 seconds for up to 30 seconds to see if the webhook credited the wallet. If still not credited, call POST /payments/verify to force-credit. UI shows 'Confirming top-up...' during the poll. **Parameters:** - `payment_intent_id` (body, required): Stripe Payment Intent ID returned from POST /payments/create-intent (the `pi_XXXXXX` portion of the `clientSecret`). Must belong to the authenticated user — server cross-checks against the intent's `metadata.user_uuid` and returns 500 with `Payment Intent does not belong to user` on mismatch. **Sample response:** ```json { "status": "success", "request_id": "req_3OqK2jK9L8pQ4xZ2", "timestamp": "2026-05-02T15:51:00.000Z", "data": { "status": "verified", "balance": 41.5 } } ```