Appearance
Example
End-to-end Cash App deposit with React hooks: pick a destination asset, create an order, show checkout, wait for completion.
Requires SwappedConnectProvider, CashAppProvider, 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 {
CashAppProvider,
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}>
<CashAppProvider>
<CashAppFlow />
</CashAppProvider>
</SwappedConnectProvider>
)
}2. Deposit form — asset + amount + create order
tsx
import {
useCashAppAmount,
useCashAppAsset,
useCashAppOrder,
useCashAppSelection,
useCashAppSupportedAssets,
} from '@swapped/connect-sdk/react'
import { getCashAppAssetKey } from '@swapped/connect-sdk'
function DepositForm({ onCreated }: { onCreated: () => void }) {
const { assets, isLoading, error: assetsError } =
useCashAppSupportedAssets()
const { selectedAsset, selectAsset, isAssetSelected } = useCashAppAsset()
const {
amount,
setAmount,
setTouched,
error: amountError,
formattedMinAmount,
} = useCashAppAmount()
const { canSubmit } = useCashAppSelection()
const { createOrder, isCreating, error: orderError } = useCashAppOrder()
async function onSubmit() {
setTouched(true)
if (!canSubmit) return
await createOrder()
onCreated() // navigate to checkout screen
}
if (isLoading) return <p>Loading assets…</p>
return (
<div>
{(assetsError || amountError || orderError) && (
<p>
{(assetsError?.message ??
amountError?.message ??
orderError?.message)}
</p>
)}
<ul>
{assets.map(asset => (
<li key={getCashAppAssetKey(asset)}>
<button
type="button"
aria-pressed={isAssetSelected(asset)}
onClick={() => selectAsset(asset)}
>
{asset.asset} on {asset.chain}
</button>
</li>
))}
</ul>
<input
value={amount}
onChange={event => setAmount(event.target.value)}
onBlur={() => setTouched(true)}
inputMode="decimal"
placeholder="0.00"
/>
{selectedAsset && (
<p>Minimum ${formattedMinAmount}</p>
)}
<button
type="button"
disabled={isCreating || !canSubmit}
onClick={() => void onSubmit()}
>
{isCreating ? 'Creating…' : 'Create order'}
</button>
</div>
)
}3. Checkout — QR, links, countdown
tsx
import {
useCashAppOrder,
useCashAppOrderCountdown,
useOnCashAppOrderCompleted,
useOnCashAppOrderExpired,
useOnCashAppOrderFailed,
useSwappedConnectClient,
} from '@swapped/connect-sdk/react'
function Checkout({
onExpired,
onFailed,
}: {
onExpired: () => void
onFailed: () => void
}) {
const client = useSwappedConnectClient()
const { order, status, isCompleted, isFailed } = useCashAppOrder()
const { hours, minutes, seconds } = useCashAppOrderCountdown()
// Side effects only — keep display data on state hooks
useOnCashAppOrderCompleted(() => {
// e.g. navigate to success; summary is available via the summary hook
})
useOnCashAppOrderFailed(() => {
onFailed() // navigate back to deposit form
})
useOnCashAppOrderExpired(() => {
onExpired() // navigate back to deposit form
})
if (!order) return <p>No active order</p>
if (isCompleted) return <p>Payment received</p>
if (isFailed) return <p>Payment failed</p>
return (
<div>
<p>
Status: {status ?? 'pending'}
{/* pending | success | failure */}
</p>
<p>
Time left: {hours}h {minutes}m {seconds}s
</p>
{/* Encode cashAppUrl as a Cash App QR, or open it */}
<a href={order.cashAppUrl} target="_blank" rel="noreferrer">
Open Cash App
</a>
{order.shortUrl && (
<a href={order.shortUrl} target="_blank" rel="noreferrer">
Open link
</a>
)}
{/* Encode order.invoice as a Lightning QR */}
{isCompleted && (
<button type="button" onClick={() => void client.restartSession()}>
Start another payment
</button>
)}
</div>
)
}4. Success summary
tsx
import {
useCashAppCompletedTransactionSummary,
useSwappedConnectClient,
} from '@swapped/connect-sdk/react'
function Success() {
const client = useSwappedConnectClient()
const summary = useCashAppCompletedTransactionSummary()
if (!summary) return null
return (
<div>
<p>Provider: {summary.provider}</p>
<p>
Paid {summary.send.amount} {summary.send.currency}
</p>
<p>
Receive {summary.receive.amount} {summary.receive.currency}
</p>
{summary.receive.network && <p>Network: {summary.receive.network}</p>}
{summary.fees.map((fee, index) => (
<p key={index}>
{fee.type === 'merchant' ? 'Merchant fee' : 'Provider fee'}{' '}
{fee.formatted.amount || fee.amount}
</p>
))}
{summary.from && <p>From: {summary.from}</p>}
{summary.to && <p>To: {summary.to.formatted ?? summary.to.address}</p>}
{summary.transaction && <p>Tx: {summary.transaction.hash}</p>}
{summary.sessionId && <p>Session: {summary.sessionId}</p>}
<button type="button" onClick={() => void client.restartSession()}>
Start another payment
</button>
</div>
)
}useCashAppCompletedTransactionSummary and the useOnCashApp* event hooks are provider-free and can mount outside CashAppProvider.
Putting it together
Wire the steps as screens (or steps in one page):
- Wrap with
CashAppProvider DepositForm→ amount + asset →createOrder()→ go to checkoutCheckout→ Cash App / Lightning + countdown → completed / failed / expired- Show summary →
restartSession()before another deposit
Deep dives: CashAppProvider, useCashAppSupportedAssets, useCashAppAsset, useCashAppAmount, useCashAppSelection, useCashAppOrder, Expiry & countdown, Summary, Event hooks, Errors.