> ## Documentation Index
> Fetch the complete documentation index at: https://docs.taprails.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Customer Flow

> Make contactless USDC payments by tapping your phone to a merchant terminal.

The customer flow scans an NFC invoice from the merchant device and processes the payment via the TapRails backend.

***

## High-Level: PaymentFlowManager

The recommended approach — handles all UI states automatically.

```tsx CustomerScreen.tsx theme={null}
import { PaymentFlowManager } from '@taprails/tap-to-pay';
import { useState } from 'react';
import { TouchableOpacity, Text } from 'react-native';

export function CustomerScreen() {
  const [showFlow, setShowFlow] = useState(false);

  return (
    <>
      <TouchableOpacity onPress={() => setShowFlow(true)}>
        <Text>Pay Now</Text>
      </TouchableOpacity>

      {showFlow && (
        <PaymentFlowManager
          config={{
            type: 'customer',
            onComplete: (data) => {
              const { processedPayment } = data as CustomerFlowData;
              console.log('Payment sent!', processedPayment?.transactionId);
              setShowFlow(false);
            },
            onCancel: () => setShowFlow(false),
            onError: (error) => {
              console.error('Payment failed:', error.message);
              setShowFlow(false);
            },
          }}
          onCustomerCancel={() => setShowFlow(false)}
        />
      )}
    </>
  );
}
```

### Customer flow states

```
idle → scanning → confirming → processing → success
           ↓                        ↓
         error                    error
       (NFC timeout,           (API error,
        invalid tag)          insufficient funds)
```

| State        | UI shown                                                           |
| ------------ | ------------------------------------------------------------------ |
| `scanning`   | "Hold your phone near the payment terminal" with animated NFC icon |
| `confirming` | Payment details: amount, merchant, currency                        |
| `processing` | Step-by-step progress indicators                                   |
| `success`    | Receipt: amount, transaction ID, timestamp                         |
| `error`      | Contextual message + retry / cancel                                |

***

## Low-Level: useNFCCustomer

Use the low-level hook when you need full control over the payment UI.

### Step 1 — Scan the NFC invoice

```tsx theme={null}
import { useNFCCustomer } from '@taprails/tap-to-pay';

const {
  scanPaymentRequest,
  processPaymentRequest,
  isReading,
  isProcessing,
  paymentRequest,
  processedPayment,
  error,
  clearPayment,
} = useNFCCustomer();

const handleScan = async () => {
  const payment = await scanPaymentRequest();
  // payment is null if an error occurred (check `error` state)
};
```

`scanPaymentRequest()` internally calls `readSignedInvoice()` which:

1. Enables the NFC reader
2. Waits up to **30 seconds** for a tag
3. Validates the ECDSA signature against the merchant's registered device keys
4. Enforces a **5-minute timestamp window** (replay-attack protection)
5. Checks the invoice hasn't **expired**
6. Returns a typed `PaymentRequest` object

### Step 2 — Show payment details to user

After successful scan, `paymentRequest` contains:

```ts theme={null}
{
  paymentId: 'pay_abc123',
  amount: '25.00',           // USDC as decimal string
  merchantWallet: '0xabcd…',
  currency: 'USDC',
  network: 'base',
  timestamp: 1712345678000,
}
```

### Step 3 — Process the payment

```tsx theme={null}
const handlePay = async () => {
  if (!paymentRequest) return;

  const result = await processPaymentRequest(
    paymentRequest.paymentId,
    '',               // txHash — provided by backend in most flows
    userWalletAddress // optional: only needed in SESSION_KEY mode
  );

  if (result) {
    console.log('Status:', result.status);
    console.log('Transaction ID:', result.transactionId);
  }
};
```

***

## Complete Low-Level Example

```tsx CustomCustomerFlow.tsx theme={null}
import { useState } from 'react';
import { View, Text, Button, ActivityIndicator } from 'react-native';
import { useNFCCustomer, PaymentStatus } from '@taprails/tap-to-pay';

export function CustomCustomerFlow() {
  const {
    scanPaymentRequest,
    processPaymentRequest,
    isReading,
    isProcessing,
    paymentRequest,
    processedPayment,
    error,
    clearPayment,
  } = useNFCCustomer();

  const handleScan = async () => {
    await scanPaymentRequest();
  };

  const handlePay = async () => {
    if (!paymentRequest) return;
    await processPaymentRequest(paymentRequest.paymentId, '');
  };

  // Success
  if (processedPayment?.status === PaymentStatus.CONFIRMED) {
    return (
      <View>
        <Text>✅ Payment confirmed!</Text>
        <Text>Tx: {processedPayment.transactionId}</Text>
        <Button title="Done" onPress={clearPayment} />
      </View>
    );
  }

  // Processing
  if (isProcessing) {
    return <ActivityIndicator />;
  }

  // Scanned — show confirmation
  if (paymentRequest) {
    return (
      <View>
        <Text>Amount: {paymentRequest.amount} USDC</Text>
        <Text>To: {paymentRequest.merchantWallet.slice(0, 8)}…</Text>
        <Button title="Confirm & Pay" onPress={handlePay} />
        <Button title="Cancel" onPress={clearPayment} />
      </View>
    );
  }

  // Error
  if (error) {
    return (
      <View>
        <Text style={{ color: 'red' }}>{error.message}</Text>
        <Button title="Try Again" onPress={handleScan} />
      </View>
    );
  }

  // Idle / Scanning
  return (
    <View>
      {isReading
        ? <Text>📡 Hold phone near payment terminal…</Text>
        : <Button title="Tap to Pay" onPress={handleScan} />
      }
    </View>
  );
}
```

***

## SESSION\_KEY Mode

When using `SESSION_KEY` mode, the payment is funded from the user's own wallet. You don't need to pass anything extra to `processPaymentRequest` — the SDK automatically signs the payment with the stored session key.

If no session key exists, `TapRailsThemeProvider` will show the setup UI before the payment proceeds. See [Session Key Setup](/guides/session-key-setup).
