> **Can't find what you're looking for?** Use `search_docs` on the docs MCP server at `https://docs.yodl.me/api/mcp` to find what you need.

# Quickstart

By the end of this page a user can scan a merchant QR code in your app and complete a payment.

**Before you start:** finish the [install](/sdk/react-native#install) and have a wallet SDK in place that gives you an EIP-1193 provider, the connected address, and the current chain id — Privy, MetaMask, Coinbase Wallet, Rainbow and WalletConnect all qualify.

:::steps
##### Create the client once

`createSdk` is called once for the whole app, not per screen. Put it in its own module and import the instance where you need it.

```ts twoslash [sdk.ts]
import { createSdk } from '@yodlpay/react-native';

export const sdk = createSdk({
  // Your registered integration address — this attributes payments to you.
  integrationAddress: '0x000000000000000000000000000000000000dEaD',
  // Chains the wallet is allowed to switch to.
  supportedChainIds: [1, 8453],
});
```

`integrationAddress` must be a non-empty `0x` address. If you don't have one yet, see [the integration address](/integrations/integration-address).

##### Guard the scanned QR

`isPaymentQr` is a standalone helper — it needs no SDK instance, so you can call it before mounting anything.

```tsx
import { isPaymentQr } from '@yodlpay/react-native';

function handleScan(scanned: string) {
  if (!isPaymentQr(scanned)) return; // not a Yodl QR — keep scanning
  setQrData(scanned);
}
```

Skipping this check is the most common cause of a blank payment screen: `<YodlPayment>` renders nothing when `qrData` isn't a supported payment QR.

##### Render the payment flow

Wrap the screen in `<YodlProvider>` and render `<YodlPayment>` inside it.

```tsx [PaymentScreen.tsx]
import { YodlPayment, YodlProvider } from '@yodlpay/react-native';
import { sdk } from './sdk';
import { useWallet } from './wallet';

export function PaymentScreen({ qrData }: { qrData: string }) {
  const { provider, address, chainId } = useWallet();

  return (
    <YodlProvider sdk={sdk} provider={provider} address={address} chainId={chainId}>
      <YodlPayment
        qrData={qrData}
        onTransactionSent={(txHash) => console.log('submitted:', txHash)}
        onError={(err) => console.error(err)}
      />
    </YodlProvider>
  );
}
```
:::

That's a working payment.

:::warning
`address` and `chainId` must be the wallet's **live** values, passed straight from your wallet SDK's hooks — not fixed constants. The SDK pushes `accountsChanged` and `chainChanged` into the hosted UI as they change, so a stale `chainId` leaves the UI on the wrong chain. The `provider` is used only as the signing and RPC transport.
:::

## A fuller example

Scan → pay → back, plus the dashboard and payment lifecycle events.

```tsx [PaymentScreen.tsx]
import { useState } from 'react';
import { Button, Text, View } from 'react-native';
import { isPaymentQr, YodlDashboard, YodlPayment, YodlProvider } from '@yodlpay/react-native';
import { sdk } from './sdk';
import { useWallet } from './wallet';
import { QrScanner } from './QrScanner'; // your camera component, e.g. expo-camera

type Screen = 'scan' | 'pay' | 'dashboard';

export function PaymentScreen() {
  const { provider, address, chainId, isConnected } = useWallet();
  const [screen, setScreen] = useState<Screen>('scan');
  const [qrData, setQrData] = useState<string | null>(null);

  if (!isConnected) return <Text>Connect a wallet to continue.</Text>;

  function handleScan(scanned: string) {
    if (!isPaymentQr(scanned)) return;
    setQrData(scanned);
    setScreen('pay');
  }

  return (
    <YodlProvider sdk={sdk} provider={provider} address={address} chainId={chainId}>
      {screen === 'scan' && (
        <View style={{ flex: 1, gap: 12 }}>
          <QrScanner onScan={handleScan} />
          <Button title="Open dashboard" onPress={() => setScreen('dashboard')} />
        </View>
      )}

      {screen === 'pay' && qrData && (
        <YodlPayment
          qrData={qrData}
          onTransactionSent={(txHash) => console.log('submitted:', txHash)}
          onPaymentDetails={(details) => {
            // 'submitted' | 'processing' | 'success' | 'failure'
            if (details.status === 'success') setScreen('scan');
          }}
          onError={(err) => {
            console.error(err);
            setScreen('scan');
          }}
        />
      )}

      {screen === 'dashboard' && <YodlDashboard onError={console.error} />}
    </YodlProvider>
  );
}
```

## Next

* Users without a Yodl account can register up front with the [signup flow](/sdk/react-native/signup), or inside the payment itself — `<YodlPayment />` asks a wallet with no account to register before it pays. Either way, [email attestation](/integrations/email-attestation) lets that registration skip Yodl's OTP.
* To match your brand, see [theming](/sdk/react-native/theming).
* Every prop and callback is in the [API reference](/sdk/react-native/api).
