> ## 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.

# Cross-Wallet Tap-to-Pay

> Enable your users to pay any merchant, on any wallet, with a single NFC tap.

**Cross-Wallet Tap-to-Pay** enables consumer payment apps, neo-banks, and fintech platforms to give their users a contactless checkout experience in the physical world.

By integrating the TapRails SDK, your app can read an NFC payment invoice emitted by any merchant terminal, abstract away wallet incompatibility (e.g. Phantom paying a Coinbase merchant), and settle stablecoin transactions instantly on Base.

***

## How It Works

TapRails acts as the interoperability bridge between the sender's mobile app and the merchant's payment reader.

```mermaid theme={null}
sequenceDiagram
    autonumber
    participant Merchant as Merchant App
    participant TapRails as TapRails SDK
    participant Customer as Customer Wallet
    participant API as TapRails API
    participant Chain as Blockchain

    Merchant->>TapRails: Create payment request
    Customer->>TapRails: Tap merchant device
    TapRails->>Customer: Payment details
    Customer->>TapRails: Authorize payment
    TapRails->>API: Submit payment
    API->>Chain: Settle stablecoin
    Chain-->>API: Confirm settlement
    API-->>Merchant: Payment confirmed
    API-->>Customer: Payment confirmed
```

<Info>
  **Zero Friction for End Users:** Customers simply tap their phone against the merchant's NFC terminal, review the amount on screen, and confirm. The settlement happens on-chain in seconds.
</Info>

***

## 1. Select Your Payment Mode

TapRails supports two distinct payment execution modes depending on your app architecture:

| Payment Mode                  | Account Custody               | User Experience                                            | Best For                                             |
| :---------------------------- | :---------------------------- | :--------------------------------------------------------- | :--------------------------------------------------- |
| **`PaymentMode.POOL`**        | Custodial (Your company pool) | Instant 1-tap payment, pre-funded company treasury balance | Neo-banks, Fintech payment apps, Payroll spend cards |
| **`PaymentMode.SESSION_KEY`** | Non-Custodial (User wallet)   | Gasless spending directly from user's own wallet           | Self-custody crypto wallets, Web3 apps               |

***

## 2. High-Level Integration (`PaymentFlowManager`)

The fastest way to add tap-to-pay is with `PaymentFlowManager`. It handles NFC scanning, UI transitions, error handling, and receipt generation out of the box.

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

export function CustomerPayScreen() {
  const [isPaying, setIsPaying] = useState(false);

  return (
    <View style={styles.container}>
      <Text style={styles.title}>Tap & Pay at Checkout</Text>
      
      <TouchableOpacity 
        style={styles.payButton} 
        onPress={() => setIsPaying(true)}
      >
        <Text style={styles.buttonText}>Tap to Pay</Text>
      </TouchableOpacity>

      {isPaying && (
        <PaymentFlowManager
          config={{
            type: 'customer',
            onComplete: (data) => {
              const { processedPayment } = data as CustomerFlowData;
              console.log('Payment Successful!', processedPayment?.transactionId);
              setIsPaying(false);
            },
            onCancel: () => {
              console.log('User cancelled tap-to-pay');
              setIsPaying(false);
            },
            onError: (error) => {
              console.error('Payment Error:', error.message);
              setIsPaying(false);
            },
          }}
          onCustomerCancel={() => setIsPaying(false)}
        />
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  title: { fontSize: 22, fontWeight: 'bold', marginBottom: 20 },
  payButton: { backgroundColor: '#7C3AED', paddingVertical: 16, paddingHorizontal: 32, borderRadius: 12 },
  buttonText: { color: '#FFF', fontSize: 18, fontWeight: '600' },
});
```

***

## 3. Low-Level Integration (`useNFCCustomer`)

If you want to construct your own custom UI and control the step-by-step state machine, use the `useNFCCustomer` hook.

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

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

  const handleStartScan = async () => {
    // 1. Listen for merchant NFC invoice
    const request = await scanPaymentRequest();
    if (!request) return;

    // 2. Process the payment once scanned
    await processPaymentRequest(request);
  };

  return (
    <View style={{ padding: 24 }}>
      {isReading && <Text>Hold device near the merchant reader...</Text>}
      
      {paymentRequest && (
        <View>
          <Text>Merchant: {paymentRequest.merchantName}</Text>
          <Text>Amount: ${paymentRequest.amount} USDC</Text>
        </View>
      )}

      {isProcessing && <ActivityIndicator size="large" color="#7C3AED" />}

      {processedPayment && (
        <View>
          <Text>Success! Tx: {processedPayment.transactionId}</Text>
          <Button title="Done" onPress={clearPayment} />
        </View>
      )}

      {error && <Text style={{ color: 'red' }}>Error: {error.message}</Text>}

      {!isReading && !paymentRequest && !processedPayment && (
        <Button title="Scan Merchant NFC" onPress={handleStartScan} />
      )}
    </View>
  );
}
```

***

## 4. Backend Settlement & Webhook Verification

Once the payment is submitted, your server receives real-time notification via the `payment.confirmed` webhook. Use this event to update order status, issue reward points, or send a receipt notification to the customer.

```typescript server.ts theme={null}
import express from 'express';
import crypto from 'crypto';

const app = express();
app.use(express.json());

const WEBHOOK_SECRET = process.env.TAPRAILS_WEBHOOK_SECRET!;

app.post('/webhooks/taprails', (req, res) => {
  const signature = req.headers['x-taprails-signature'] as string;
  
  // Verify webhook signature
  const hmac = crypto
    .createHmac('sha256', WEBHOOK_SECRET)
    .update(JSON.stringify(req.body))
    .digest('hex');

  if (signature !== hmac) {
    return res.status(401).send('Invalid signature');
  }

  const { event, data } = req.body;

  if (event === 'payment.confirmed') {
    console.log(`Payment confirmed: ${data.payment_id}`);
    console.log(`Amount: ${data.amount} ${data.currency}`);
    console.log(`Merchant ID: ${data.merchant_id}`);
    console.log(`Transaction Hash: ${data.tx_hash}`);
    
    // Update internal order / user ledger here
  }

  res.status(200).send({ received: true });
});
```

***

## Web Fallback Experience

If a customer taps an NFC terminal using a phone that does **not** have your app installed (or on non-integrated devices), TapRails automatically provides a secure **Zero-App Web Fallback**. The NFC tag directs the customer's browser to a web checkout page where they can connect any Web3 wallet (via WalletConnect, Coinbase Wallet, etc.) and complete the payment seamlessly.

***

## Next Steps

<CardGroup cols={2}>
  <Card title="Merchant Acceptance" icon="store" href="/guides/merchant-acceptance">
    Learn how to turn Android phones into POS terminals accepting tap-to-pay.
  </Card>

  <Card title="Wallet Integration" icon="arrow-right-arrow-left" href="/guides/wallet-integration">
    Embed gasless session-key tap-to-pay inside self-custody wallets.
  </Card>
</CardGroup>
