Integrate
@phala/ pay- react
React components for Phala Pay. The framework-free checkout core is @phala/pay; the merchant backend is @phala/.
npm install @phala/pay-react @phala/pay react react-dom viemImport PaymentElement, Checkout, DepositAddress, Payment, NetworkIcon, AssetIcon, and hooks from @phala/pay-react, and include @phala/pay-react/styles.css once. React 18 and 19 are supported; React is a peer dependency.
PaymentElement is the whole payment, like Stripe's Payment Element with a deferred intent: the
payer picks a token and network, and only on submit does your create
create the quote on your backend, with that key as its Idempotency-Key (resolving with
pay.); the element then shows its
checkout, with a way back to the options. Payment is that choice on its own: a
"Token" field, then a "Network" field of the networks that take it, each a select-only combobox.
<PaymentElement options={paymentOptions(config.assets)} amount={2500} createQuote={(option, { idempotencyKey }) => api.createTopUp({ chain_id: option.chain_id, asset: option.asset, idempotencyKey }) } onSuccess={() => router.refresh()}/>See the payment element guide for its other props, your own submit button, and the deposit-address path.
Checkout and DepositAddress retain their last view and payer selection during temporary
network/5xx outages, show a neutral reconnecting message, and resume polling without creating a
new payment. Invalid secrets and mismatched addresses retain their normal failure handling.
See planned upgrades.
Both components poll about every three seconds (pollInterval, in milliseconds), with uniform
±20% jitter on intervals and failure backoff, respecting Retry-After. Polling pauses in hidden
tabs and reads immediately when they become visible. Checkout stops after three consecutive
non-terminal 4xx responses other than 408/429, using its existing error state. Success or another
kind of failure resets the count; 404 still stops immediately as invalid_.
After ten minutes without an observed public-view change, DepositAddress uses a base interval
of max(poll milliseconds, so a longer configured interval stays unchanged.
Any change or visibility regain restores the normal interval and resets the idle window.
Its optional onChange?: (state: Client follows Checkout's callback naming:
it receives the full public view after the first successful read and then once per content change,
including payment confirmations and network credit times. Unlike <Checkout onChange>, it does
not report loading. Unchanged polls, reconnecting alone, and replacing the
callback do not notify or restart polling. This callback is for your UI; fulfil from your
deposit.credited webhook.
<DepositAddress depositAddress={{ address, networks }} clientSecret={clientSecret} apiBase="https://pay.example.com" pollInterval={3000} onChange={(state) => setPayments(state.payments)}/>See the component guide for the other props.