Skip to content

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>
  )
}
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):

  1. Wrap with CashAppProvider
  2. DepositForm → amount + asset → createOrder() → go to checkout
  3. Checkout → Cash App / Lightning + countdown → completed / failed / expired
  4. Show summary → restartSession() before another deposit

Deep dives: CashAppProvider, useCashAppSupportedAssets, useCashAppAsset, useCashAppAmount, useCashAppSelection, useCashAppOrder, Expiry & countdown, Summary, Event hooks, Errors.