Table of Contents
- Installation
- Basic Setup
- Provider Configuration
- Components
- Hooks
- Payment Flow
- Payment Confirmation
- Multi-Currency Plans & Top-Ups
- Credit Top-Ups & Auto-Recharge
- Purchase Management
- Complete Example
Installation
Install the required packages:Peer Dependencies
SolvaPay React requires:react^18.2.0 || ^19.0.0react-dom^18.2.0 || ^19.0.0@stripe/react-stripe-js(for payment forms)@stripe/stripe-js(for Stripe integration)
Basic Setup
1. Wrap Your App with Provider
TheSolvaPayProvider is required to use SolvaPay hooks and components:
2. Zero-Config Usage
By default,SolvaPayProvider uses these API endpoints:
/api/check-purchase- Check purchase status/api/create-payment-intent- Create payment intents/api/process-payment- Process payments
Provider Configuration
Custom API Routes
If your backend uses different API routes, configure them:With Supabase Authentication
Use the Supabase auth adapter for automatic user ID extraction:Custom Authentication Adapter
Create a custom auth adapter for other authentication systems:Components
PaymentForm
A complete payment form component with Stripe integration:PaymentForm Props
planRef(required) - Plan reference to subscribe toproductRef(optional) - Product reference for usage trackingonSuccess- Callback when payment succeedsonError- Callback when payment failsreturnUrl- Optional return URL after paymentsubmitButtonText- Custom submit button text (default: “Pay Now”)className- Custom CSS class for form containerbuttonClassName- Custom CSS class for submit button
PricingSelector
Select a pricing option from available options using the render-prop API:PurchaseGate
Conditionally render content based on purchase status using the compound primitive. Match byproductRef (any plan of that product) or planRef
(specific plan). Both together require they match on the same active
purchase.
ProductBadge
Display product subscription information using the render-prop pattern:TopupForm
A compound component for one-off credit top-ups. It wrapsuseTopup and
Stripe Elements, so you compose the parts you need instead of accepting a
fixed layout:
Compound parts
TopupForm.Root— provides context and drives the top-up. Accepts the props below.TopupForm.PaymentElement— renders the Stripe Payment Element.TopupForm.SubmitButton— confirms the payment; disabled until the card input is complete.TopupForm.Loading— shown while the payment intent and Stripe are initializing.TopupForm.Error— renders the current error, if any.TopupForm.AmountPicker— optional in-place amount picker (re-exported fromAmountPicker).TopupForm.LegalFooter— mandate / legal copy footer.
Root props
amount(required) — top-up amount in minor units (e.g.1000=$10.00).currency— ISO 4217 currency code (default:USD).autoRecharge— optionalAutoRechargeInputto enable auto-recharge in the same payment (see Credit Top-Ups & Auto-Recharge).onSuccess(paymentIntent, extras?)— fires once the customer is fully credited.extras.creditsAddedcarries the wallet delta when the backend reports it.onError(error)— called when the payment fails.returnUrl— return URL used for redirect-based payment methods (defaults to the current URL).className/buttonClassName— styling hooks.
/api/process-topup-payment route, TopupForm waits for that round-trip so
onSuccess only fires once the credit has actually landed. Transports without
that route fall back to firing on Stripe confirmation.
AutoRecharge
A drop-in component that lets a customer turn on automatic credit top-ups. It renders a summary card with a trigger that opens a modal for configuring a balance threshold, a fixed top-up amount, and an optional monthly spend cap (under Advanced). When enabled, SolvaPay charges the saved card off-session and mints credits whenever the balance falls below the threshold — no manual checkout.AutoRecharge Props
currency— ISO 4217 currency code (default:USD).defaultThresholdAmountMajor— pre-fills the threshold field (form default:5).defaultTopupAmountMajor— pre-fills the top-up field (form default:10).deferCardSetup— whentrue, saving stages the config without creating a Stripe SetupIntent and firesonPendingConfiginstead. Use this to arm auto-recharge inside a top-up payment (see below).onPendingConfig(payload)— receives theAutoRechargeInputto forward intoTopupFormwhendeferCardSetupis set.onSetupRequired(result)— called when a separate card-setup step is required (non-deferred flow).onSaved(result)/onDisabled()— lifecycle callbacks.className— styling hook.
enabled: false by default). Amounts can be entered in
either display currency or credits; the component handles the Stripe SetupIntent
and the 3DS redirect return internally. Set an optional maxMonthlySpendMajor cap
(blank = unlimited) in the form. The summary card shows current-period
spend when a cap is configured, and a Monthly spend limit reached status when
the next recharge would exceed the cap. If a series of off-session charges keeps
declining, the card shows a text-only “payment failed” status — see the
failed-recharge guidance for building a card-update prompt.
For full layout control, compose the primitive from @solvapay/react/primitives:
AutoRecharge.MaxMonthlySpendField,
AutoRecharge.MonthlySpend, and AutoRecharge.Status (cap-reached and failed badges).
Hooks
usePurchase
Check purchase status and access purchase data:usePurchase Return Values
loading- Boolean indicating the first purchase check for this user is in progress. Staysfalseon background refetches — gate your initial skeleton on this.isRefetching- Boolean indicating a background refetch is in progress (first fetch already completed). Use for subtle “refreshing” indicators that shouldn’t remount the UI.purchases- Array of purchase objectsactivePurchase- The active purchase (or null)hasPaidPurchase- Boolean indicating if user has paid purchaseactivePaidPurchase- The active paid purchase (or null)hasPurchase(criteria?)- Predicate with AND semantics. Pass{ productRef },{ planRef }, or both (both must match the same active purchase). Call with no arguments to check for any active purchase. Mirrors<PurchaseGate.Root>prop shape.refetch- Function to manually refetch purchase status
useCheckout
Programmatic checkout flow:useCustomer
Access customer information:usePlans
Fetch available plans:useSolvaPay
Access all SolvaPay functionality:useTopup
Manage a credit top-up flow programmatically. Handles payment-intent creation and Stripe initialization — the top-up analogue ofuseCheckout:
useTopup Options
amount(required) — top-up amount in minor units (e.g.1000=$10.00).currency— ISO 4217 currency code (default:USD).autoRecharge— optionalAutoRechargeInputto enable auto-recharge in the same payment.
useTopup Return Values
loading—truewhile the payment intent is being created.error— the last error, ornull.stripePromise— the resolved Stripe instance for mounting Elements.clientSecret— the payment-intent client secret.startTopup()— creates the payment intent and initializes Stripe.reset()— clears state to start over.
useAutoRecharge
Read and manage the customer’s auto-recharge configuration. Backed by a short-lived module cache so multiple components share one request:useAutoRecharge Return Values
config— the currentAutoRechargeConfig, ornullif none exists.loading/saving/disabling— in-flight state flags.error— the last error, ornull.refresh(force?)— re-fetch the config.save(input)— set or update the config. AcceptsSaveAutoRechargeInput(see the top-up section for themaxMonthlySpendMajorcap anddeferSetupIntent).disable()— turn auto-recharge off.
config.status is active, disabled, pending_setup, or failed. Read
config.failureCount alongside status === 'failed' to decide when to prompt
the customer to update their card.
Payment Flow
Simple Payment Flow
UsePaymentForm for a complete payment flow:
Custom Payment Flow
Build a custom payment flow with hooks:Payment Confirmation
Payment success is gated on real confirmation from Stripe — a form only reports success once the payment intent actually succeeded (and, where the backend route is wired, once SolvaPay has processed it). Two pieces make this work: theconfirmPayment utility and the built-in
return-path resume.
Payment Element is the default
PaymentForm and TopupForm render the Stripe Payment Element by default, which supports
cards plus redirect- and async-based payment methods (SEPA, iDEAL, and others).
confirmPayment
If you build your own submit handler, useconfirmPayment instead of calling Stripe directly. It
wraps Elements submission and confirmation, and returns a discriminated result:
pending maps to Stripe’s processing state. The built-in forms surface it as a message and keep
the customer on the form — onSuccess never fires from a pending payment.
Return-path resume
Redirect-based payment methods send the customer away and back.PaymentForm and TopupForm
handle the return automatically: on mount they read the payment_intent_client_secret query
parameters Stripe appends to the returnUrl, retrieve the intent, strip the parameters from the
URL, and resume — running handleNextAction if 3DS is still required, showing the pending message
while processing, and firing onSuccess once the intent succeeded and the payment was processed.
You only need to make sure returnUrl points at the page that renders the form.
Multi-Currency Plans & Top-Ups
Plans can carry per-currency pricing through apricingOptions array (currency, price,
optional basePrice / setupFee, one entry marked default). Single-currency plans without the
array behave as before.
Plan checkout
PlanSelector shows a currency switcher when the product’s plans span more than one currency, and
each plan card can render its per-currency price. The selected currency flows through checkout
automatically — useCheckout passes it when creating the payment. For custom UIs, resolve prices
with the helpers:
pricingOptions entry for, checkout rejects with
an explicit “currency not supported” error listing the available currencies.
Top-up currencies
Credit top-ups use a separate currency list: the provider’sdefaultCurrency plus any
additional supportedTopupCurrencies configured in the SolvaPay Console. Plan pricing currencies
are never used for top-ups. useCheckoutFlow exposes topupCurrencies, topupCurrency, and
setTopupCurrency for building a currency picker on the amount step; pass the chosen currency to
TopupForm.Root / useTopup.
Credit Top-Ups & Auto-Recharge
Credit-based products let customers buy a balance and spend it as they use your API. SolvaPay supports both one-off top-ups and auto-recharge, where the balance is refilled automatically once it drops below a threshold. All three flows below are powered by@solvapay/next route helpers.
Backend routes
Add these API routes once — the React components and hooks call them through the provider. All are one-liners over@solvapay/next:
/api/process-topup-payment route is what lets TopupForm confirm that the
credit actually landed before firing onSuccess. The three auto-recharge verbs
share a single route and a single provider key, api.autoRecharge (default
/api/auto-recharge).
Standalone top-up
RenderTopupForm with the amount in minor units. Credits are booked by the
webhook handler, and — because the /api/process-topup-payment route is wired —
onSuccess fires only once the credit has landed:
Enable auto-recharge in the same payment
You can arm auto-recharge as part of the initial top-up charge — there’s no separate card-setup step. Render<AutoRecharge deferCardSetup> on the amount
step, capture the pending config, and pass it into TopupForm.Root:
deferCardSetup, saving the AutoRecharge form sends deferSetupIntent: true
(no inline SetupIntent) and fires onPendingConfig. The backend creates the
payment intent with setup_future_usage: 'off_session', saves the card on that
same charge, and activates the config (pending_setup → active) when the
webhook lands — so the customer configures and funds auto-recharge in one step.
Managing auto-recharge on its own
Outside checkout, drop<AutoRecharge> into an account or settings page. It
reads and writes through the /api/auto-recharge route:
Reacting to auto-recharge on usage
When you calltrackUsage (via /api/track-usage), the response’s
creditDebit.autoRecharge.triggered flag signals that an off-session charge was
initiated — it does not mean the credits are in that same response. Call
balance.reconcileAfterUsageDebit({ expectIncrease: true }) only when
triggered is true; it polls the balance (~32s grace) and counts back-to-back
recharges so the badge converges once the credit lands:
Configuration, limits, and pricing. Auto-recharge uses a balance
threshold plus a fixed top-up amount. When enabled, both must be greater
than zero, the top-up must be at least the threshold, and both must be at most
10,000 major units. The top-up amount must also clear Stripe’s per-currency
minimum (for example $0.50 for USD/EUR/CHF/CAD/AUD, £0.30 for GBP,
30 kr for SEK/NOK, 2.50 kr for DKK), and the currency must be one of the
provider’s supported top-up currencies.An optional maxMonthlySpendMajor cap limits how much auto-recharge spend is
allowed per UTC calendar month. Set it in the <AutoRecharge> form under
Advanced (or pass it through useAutoRecharge().save({ ..., maxMonthlySpendMajor })
and the /api/auto-recharge route). When the cap is hit mid-month, the config
stays active and charges resume automatically next UTC month. The drop-in
component shows a spend line ($45 / $100 this month) and a Monthly spend limit
reached status when applicable; read config.monthlySpendMinor and
config.monthlySpendPeriod if you build a custom UI.Thresholds and amounts are stored in display-currency minor units and
re-resolved to credits against live FX at trigger time (no fixed credit
threshold is stored). When a config has a display block, render its
display.formatted values verbatim rather than re-deriving them from minor
units.Migrating early-adopter route keys. The separate
api.getAutoRecharge,
api.saveAutoRecharge, and api.disableAutoRecharge provider keys were
collapsed into a single api.autoRecharge key (default /api/auto-recharge).
If you set any of the old keys, rename them to api.autoRecharge.Purchase Management
Check Purchase Status
Display Purchase Details
Refresh Purchase Status
Complete Example
Here’s a complete React application with SolvaPay integration:Styling
SolvaPay components are headless and don’t include default styles. Style them to match your design system:Best Practices
-
Provider Placement: Place
SolvaPayProviderat the root of your app, above all routes. - Error Handling: Always handle errors from hooks and components.
- Loading States: Show loading states while purchase checks are in progress.
-
Refetch After Payment: Call
refetch()after successful payment to update purchase status. - Type Safety: Use TypeScript for better type safety and autocomplete.
- Custom Styling: Style components to match your design system.
Next Steps
- Next.js Integration Guide - Learn Next.js-specific integration
- Purchase management - Cancel, reactivate, switch plans, and wire up top-ups
- Auto-recharge - Full auto-recharge stack: routes, server helpers, monthly spend cap
- Business checkout (B2B) - Collect business details and tax IDs at checkout
- Custom Authentication Adapters - Build custom auth adapters
- Usage Events - Track purchase and usage activity
- API Reference - Full API documentation