Appearance
Example
End-to-end Exchange Pay deposit with React hooks: pick a provider, create an order, show checkout, wait for completion.
Requires SwappedConnectProvider, ExchangePayContextProvider, and a loaded active session. After a completed payment, call restartSession() before starting again.
Use state hooks for UI. Use event hooks only for side effects (navigate, toast).
1. Providers + session
tsx
import { createSwappedConnectClient } from '@swapped/connect-sdk'
import {
ExchangePayContextProvider,
SwappedConnectProvider,
} from '@swapped/connect-sdk/react'
const client = createSwappedConnectClient({
sessionId: 'your-session-id', // from your backend
})
void client.loadSession()
function App() {
return (
<SwappedConnectProvider client={client}>
<ExchangePayContextProvider>
<ExchangePayFlow />
</ExchangePayContextProvider>
</SwappedConnectProvider>
)
}2. Pick an Exchange Pay provider
tsx
import { useExchangePayProviders } from '@swapped/connect-sdk/react'
function ProviderPicker() {
const {
paymentMethods,
selectedProvider,
selectProvider,
isLoading,
} = useExchangePayProviders()
if (isLoading) return <p>Loading…</p>
return (
<ul>
{paymentMethods.map(method => (
<li key={method.id}>
<button
type="button"
aria-pressed={selectedProvider === method.provider}
onClick={() => selectProvider(method.provider)}
>
{method.name}
</button>
</li>
))}
</ul>
)
}3. Deposit form — currency + amount + create order
tsx
import {
useExchangePayAmount,
useExchangePayCurrency,
useExchangePayOrder,
useExchangePaySelection,
useExchangePaySupportedCurrencies,
} from '@swapped/connect-sdk/react'
function DepositForm({ onCreated }: { onCreated: () => void }) {
const { currencies, isLoading, error: currenciesError } =
useExchangePaySupportedCurrencies()
const { selectedCurrency, selectCurrency, isCurrencySelected } =
useExchangePayCurrency()
const {
amount,
setAmount,
setTouched,
error: amountError,
formattedMinAmount,
} = useExchangePayAmount()
const { canSubmit } = useExchangePaySelection()
const { createOrder, isCreating, error: orderError } = useExchangePayOrder()
async function onSubmit() {
setTouched(true)
if (!canSubmit) return
await createOrder()
onCreated() // navigate to checkout screen
}
if (isLoading) return <p>Loading currencies…</p>
return (
<div>
{(currenciesError || amountError || orderError) && (
<p>
{(currenciesError?.message ??
amountError?.message ??
orderError?.message)}
</p>
)}
<ul>
{currencies.map(currency => (
<li key={`${currency.symbol}:${currency.blockchain}`}>
<button
type="button"
aria-pressed={isCurrencySelected(currency)}
onClick={() => selectCurrency(currency)}
>
{currency.symbol} on {currency.blockchain}
</button>
</li>
))}
</ul>
<input
value={amount}
onChange={event => setAmount(event.target.value)}
onBlur={() => setTouched(true)}
inputMode="decimal"
placeholder="0.00"
/>
{selectedCurrency && (
<p>Minimum ${formattedMinAmount}</p>
)}
<button
type="button"
disabled={isCreating || !canSubmit}
onClick={() => void onSubmit()}
>
{isCreating ? 'Creating…' : 'Create order'}
</button>
</div>
)
}4. Checkout — QR, links, countdown
tsx
import {
useExchangePayOrder,
useExchangePayOrderCountdown,
useOnExchangePayOrderCompleted,
useOnExchangePayOrderExpired,
useSwappedConnectClient,
} from '@swapped/connect-sdk/react'
function Checkout({ onExpired }: { onExpired: () => void }) {
const client = useSwappedConnectClient()
const { order, status, isCompleted, closeOrder } = useExchangePayOrder()
const { hours, minutes, seconds } = useExchangePayOrderCountdown()
// Side effects only — keep display data on state hooks
useOnExchangePayOrderCompleted(() => {
// e.g. navigate to success; summary is available via the summary hook
})
useOnExchangePayOrderExpired(() => {
// e.g. toast.info('Order expired, please try again')
onExpired() // navigate back to deposit form
})
if (!order) return <p>No active order</p>
if (isCompleted) return <p>Payment received</p>
const { qr, url, mobileUrl } = order.checkout
return (
<div>
<p>
Status: {status ?? 'PENDING'}
{/* PENDING | PAY_SUCCESS | AWAITING_PROVIDER_FUNDS */}
</p>
<p>
Time left: {hours}h {minutes}m {seconds}s
</p>
{qr?.type === 'image' && <img src={qr.value} alt="Pay QR" />}
{qr?.type === 'url' && <p>Encode as QR: {qr.value}</p>}
{url && (
<a href={url} target="_blank" rel="noreferrer">
Open checkout
</a>
)}
{/* mobileUrl is for mobile devices only */}
{mobileUrl && mobileUrl !== url && <a href={mobileUrl}>Open in app</a>}
{/* If canClose is true, show Cancel — or call closeOrder() when the user goes back */}
{order.canClose && (
<button type="button" onClick={() => void closeOrder()}>
Cancel order
</button>
)}
{isCompleted && (
<button type="button" onClick={() => void client.restartSession()}>
Start another payment
</button>
)}
</div>
)
}5. Success summary
tsx
import {
useExchangePayCompletedTransactionSummary,
useSwappedConnectClient,
} from '@swapped/connect-sdk/react'
function Success() {
const client = useSwappedConnectClient()
const summary = useExchangePayCompletedTransactionSummary()
if (!summary) return null
const network = summary.amount.network ?? summary.receive?.network
return (
<div>
<p>Provider: {summary.provider}</p>
<p>
Paid {summary.amount.amount} {summary.amount.currency}
</p>
{/* Swap legs when the deposit involved a conversion */}
{summary.isSwap && summary.send && (
<p>
Send {summary.send.amount} {summary.send.currency}
</p>
)}
{summary.isSwap && summary.receive && (
<p>
Receive {summary.receive.amount} {summary.receive.currency}
</p>
)}
{network && <p>Network: {network}</p>}
{summary.fee && (
<p>
Fee {summary.fee.amount} {summary.fee.currency}
</p>
)}
{summary.merchantFee && (
<p>
Merchant fee {summary.merchantFee.amount}{' '}
{summary.merchantFee.currency}
</p>
)}
{summary.destinationAddress && (
<p>Deposit address: {summary.destinationAddress}</p>
)}
{summary.transactionHash && <p>Tx: {summary.transactionHash}</p>}
{summary.status && <p>Status: {summary.status}</p>}
{summary.sessionId && <p>Session: {summary.sessionId}</p>}
{summary.from && <p>From: {summary.from}</p>}
{summary.to && <p>To: {summary.to}</p>}
<button type="button" onClick={() => void client.restartSession()}>
Start another payment
</button>
</div>
)
}useExchangePayCompletedTransactionSummary and the useOnExchangePay* event hooks are provider-free and can mount outside ExchangePayContextProvider.
Putting it together
Wire the steps as screens (or steps in one page):
- Wrap with
ExchangePayContextProvider ProviderPicker→ user picks an exchangeDepositForm→ amount + currency →createOrder()→ go to checkoutCheckout→ QR / links + countdown → completed / expired- Show summary →
restartSession()before another deposit
Deep dives: ExchangePayContextProvider, useExchangePayProviders, useExchangePaySupportedCurrencies, useExchangePayCurrency, useExchangePayAmount, useExchangePaySelection, useExchangePayOrder, Expiry & countdown, Summary, Event hooks, Errors.