> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://www.openfort.io/api/mcp` to find what you need.
>
> **Have feedback?** Use `submit_feedback` on the same MCP server.

# Funding with `useFunding`

For concepts (sessions, payment methods, statuses) see [Funding](https://www.openfort.io/docs/configuration/funding). This page covers the `useFunding` hook for a bespoke React Native Deposit UI, and the [fiat onramp](#fiat-onramp-apple-pay--google-pay) — Apple Pay / Google Pay purchases with `useWalletPayVerification` and `OnrampPaymentSheet`. To skip the SDK entirely, a [hosted deposit page in a WebView](#no-sdk-hosted-deposit-page-in-a-webview) is available as a secondary option.

## Custom UI with `useFunding`

Drive a session yourself with the `useFunding` hook from `@openfort/react-native`. It exposes the session state plus `fund` (create a session and set a payment method in one call), `createSession` (create a session for a destination, to set the payment method later), `payLink`, and `reset`.

```tsx
import { useFunding } from '@openfort/react-native'
import { View, Text, Pressable } from 'react-native'

function Deposit({ walletAddress }: { walletAddress: string }) {
  const { fund, createSession, session, status, loading, error, isAvailable, payLink, reset } = useFunding()

  async function depositFromPolygon() {
    await fund(
      // target — where funds land (USDC on Base)
      { chain: 'eip155:8453', currency: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', address: walletAddress },
      // source — what the user sends (USDC on Polygon)
      { type: 'evm', source: { chain: 'eip155:137', currency: '0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359', amount: '10000000' } }
    )
  }

  if (session?.paymentMethod) {
    return (
      <View>
        <Text>Status: {status}</Text>
        <Text>Send to: {session.paymentMethod.receiverAddress}</Text>
        {/* render session.paymentMethod.addressUri as a QR (e.g. react-native-qrcode-svg),
            or open session.paymentMethod.deeplinks with Linking.openURL */}
      </View>
    )
  }

  return (
    <Pressable onPress={depositFromPolygon} disabled={loading}>
      <Text>{loading ? 'Preparing…' : 'Deposit'}</Text>
    </Pressable>
  )
}
```

The hook returns once a deposit address is available and keeps polling until the session reaches a terminal status (`succeeded`, `bounced`, or `expired`). `fund`'s payment method can source from an EVM wallet (`{ type: 'evm', source }`) or a Solana wallet (`{ type: 'solana', source }`). To fund from a centralized exchange, use `payLink` instead.

On mobile you typically render `session.paymentMethod.receiverAddress` next to a QR code of `session.paymentMethod.addressUri`, and offer `session.paymentMethod.deeplinks` as one-tap buttons that open the user's wallet app via React Native's `Linking.openURL`.

### What `useFunding` returns

| Field | Type | Description |
| --- | --- | --- |
| `session` | `FundingSession \| null` | The current or last session, incl. `paymentMethod` once set. |
| `status` | `SessionStatus \| 'idle'` | Lifecycle status; `'idle'` before the first call. |
| `loading` | `boolean` | True while creating the session and minting the address. |
| `error` | `Error \| null` | The last error, if any. |
| `isAvailable` | `boolean` | True once the SDK client has initialized and the funding namespace is usable. |
| `fund(target, paymentMethod)` | `=> Promise<FundingSession>` | Create a session and set a payment method in one call. |
| `createSession(target)` | `=> Promise<FundingSession>` | Create a session for a destination, to set the payment method later. |
| `track(session)` | `=> Promise<FundingSession>` | Attach to a session created elsewhere (its `id` + `clientSecret`) and poll it to a terminal status. |
| `payLink(params)` | `=> Promise<string>` | Resolve a prefilled exchange on-ramp URL (Coinbase / Binance). |
| `reset()` | `=> void` | Clear session state and start over. |

:::tip[Reacting to settlement]
There's no completion callback — drive UI off `status`. It reaches `succeeded` when funds land (or `bounced` / `expired`). For server-side fulfilment, don't rely on the device: use [webhooks](https://www.openfort.io/docs/configuration/webhooks#funding-events).
:::

:::tip
Same‑chain deposits (source chain == destination chain) skip bridging entirely — the hook returns the wallet address directly, so a plain transfer works with no fees.
:::

## Fiat onramp (Apple Pay / Google Pay)

Users can also **buy** crypto in-app. The purchase is the same funding session with an `onramp` payment method — see [Fiat onramp](https://www.openfort.io/docs/configuration/funding#fiat-onramp) — and it composes from three pieces: verify the buyer, commit the session, mount the payment sheet.

Native Apple Pay / Google Pay runs on guest checkout, which requires an OTP-verified email and US phone number. `useWalletPayVerification` owns that state machine — render one screen per `step` (`'email' → 'emailCode' → 'phone' → 'phoneCode' → 'complete'`) and wait for `identity`:

```tsx
import { useFunding, useWalletPayVerification, OnrampPaymentSheet } from '@openfort/react-native'

function BuyWithApplePay({ walletAddress }: { walletAddress: string }) {
  const [agreementAccepted, setAgreementAccepted] = useState(false)
  const verification = useWalletPayVerification({ agreementAccepted })
  const { fund, session, status } = useFunding()

  function buy() {
    if (!verification.identity) return
    // Don't await — fund() resolves only at settlement. It sets `session`
    // (with the payment link) as soon as the method commits.
    fund(
      { chain: 'eip155:8453', currency: '0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913', address: walletAddress },
      { type: 'onramp', method: 'apple_pay', sourceAmount: '25', sourceCurrency: 'USD', ...verification.identity },
    )
  }

  const pm = session?.paymentMethod
  if (pm?.type === 'onramp' && pm.url) {
    return (
      <OnrampPaymentSheet
        url={pm.url}
        style={{ height: 380 }} // a WebView with no height collapses to zero
        onCommitted={() => {/* payment committed — watch `status` for settlement */}}
        onError={(code, message) => console.warn(code, message)}
      />
    )
  }

  // …render your verification screens per verification.step, then:
  return <Button title="Buy $25 with Apple Pay" onPress={buy} disabled={!verification.identity} />
}
```

How the pieces behave:

**`useWalletPayVerification`**

* Methods: `submitEmail` / `verifyEmailCode` / `submitPhone` / `verifyPhoneCode` / `resend` / `reset`, plus `loading`, `error`, and `testMode`.
* Openfort issues the OTPs; nothing throws — errors land in `error`.
* `submitPhone` refuses to send a code until `agreementAccepted` is true — collect the guest-checkout consent on the phone step.
* Completed verifications are stored securely on-device and reused for **~60 days**, skipping the OTPs on the next purchase.
* `identity` spreads directly into the `onramp` payment method, as above.
* Phone numbers are US mobiles in E.164 (`+14155550123`). On a `pk_test_` key (`testMode`), sandbox numbers (`+1000` + 7 digits) are accepted.

**`OnrampPaymentSheet`**

* A `react-native-webview` WebView over the committed payment link (`session.paymentMethod.url`) with the provider's event bridge wired up.
* `onCommitted` fires when the payment is committed — it is **not** settlement. Settlement arrives on the funding session: watch `status` (or the `fund()` promise) until `succeeded`, or use [webhooks](https://www.openfort.io/docs/configuration/webhooks#funding-events) server-side.
* On test keys the link already carries the provider's sandbox flag, so the real payment sheet is replaced with a test popup.

**Requirements**

* Your app must be inside `OpenfortProvider`, with `react-native-webview` installed (already a peer dependency of the SDK).
* `@openfort/react-native` ≥ 2.1.0.
* Fiat is mainnet-only, and the methods must be [enabled in the dashboard](https://www.openfort.io/docs/configuration/funding#enabling-it).

:::tip[Card / bank transfer: declare `angles: ['popup']`]
For card / bank-transfer purchases, commit the same way with `method: 'card'` or `'bank_transfer'` (no verification needed) and open the returned `session.paymentMethod.url` in the browser — e.g. `Linking.openURL` or `expo-web-browser` — instead of the payment sheet.

Include `angles: ['popup']` on the payment method (and on the resolve and quote calls, if you make them). In some regions these methods otherwise resolve to the `embedded` angle — a browser-only element flow with no URL to open. The declaration tells routing what this client can execute, so the same method degrades to a hosted checkout URL instead. Requires `@openfort/openfort-js` ≥ 2.3.0 — see [client capability](https://www.openfort.io/docs/products/embedded-wallet/javascript/signer/funding#sessionless-discovery-and-client-capability-230).
:::

## No-SDK: hosted deposit page in a WebView

If you'd rather not run any SDK code in your funding screen, open the hosted **deposit send page** inside a WebView. It reads the transfer from its URL and submits it through the wallet provider injected by the in-app browser — no Openfort SDK runs on the page.

Build a [deposit page URL](https://www.openfort.io/docs/configuration/funding#hosted-deposit-page) with the destination address and source-chain params, then render it with [`react-native-webview`](https://github.com/react-native-webview/react-native-webview).

```tsx
import { WebView } from 'react-native-webview'

function DepositWebView({ receiver }: { receiver: string }) {
  const params = new URLSearchParams({
    to: receiver,
    chainId: '42161', // Arbitrum (source chain)
    token: '0xaf88d065e77c8cC2239327C5EDb3A432268e5831', // USDC on Arbitrum
    decimals: '6',
    symbol: 'USDC',
    chain: 'Arbitrum',
    amount: '10000000', // base units, user-editable
  })
  const url = `https://deposit.openfort.io?${params.toString()}`

  return <WebView source={{ uri: url }} />
}
```

The page shows the transaction hash on success but does **not** post back into the host app, so don't wait on `onMessage` for completion — track settlement with [Webhooks](https://www.openfort.io/docs/configuration/webhooks#funding-events) or by polling the session through the [REST reference](https://www.openfort.io/docs/configuration/funding/headless).

## Next steps

* Drive funding from a backend or agent with the [JavaScript SDK](https://www.openfort.io/docs/products/embedded-wallet/javascript/signer/funding) or the [REST reference](https://www.openfort.io/docs/configuration/funding/headless).
* Customize or self-host the deposit page — see the [hosted deposit page](https://www.openfort.io/docs/configuration/funding#hosted-deposit-page).
* React to settlement with [Webhooks](https://www.openfort.io/docs/configuration/webhooks#funding-events).
