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

# Session Key Setup

> Let users authorize gasless tap-to-pay from their own wallet using session keys.

Session keys allow the TapRails SDK to execute payments from the user's wallet **without prompting them to sign every transaction**. The user signs a single approval once; subsequent payments are instant and gasless.

***

## How It Works

1. **SDK detects no session key** → shows the setup UI
2. **User reviews the daily limit** → approves in your wallet
3. **SDK generates a keypair** on-device, stores private key in Keychain
4. **SDK registers the public key** with the TapRails backend
5. **Future payments** are signed by the session key — no user interaction

***

## Automatic Setup with TapRailsThemeProvider

The simplest setup — wrap your app with `TapRailsThemeProvider` and configure `sessionKey` in `initialize()`. The SDK handles detection and UI automatically.

```tsx App.tsx theme={null}
import {
  ContactlessCryptoSDK,
  PaymentMode,
  TapRailsThemeProvider,
} from '@taprails/tap-to-pay';

// 1. Initialize with SESSION_KEY mode
ContactlessCryptoSDK.initialize({
  apiKey: 'pk_live_your_key',
  apiUrl: 'https://api.acmepay.com',
  environment: 'production',
  mode: PaymentMode.SESSION_KEY,
  sessionKey: {
    walletAddress: user.walletAddress,
    defaultDailyLimit: '250.00',
    autoRenew: true,
    onSignTransaction: async (tx) => {
      // Hand off to your wallet provider
      return await walletProvider.signTransaction(tx);
    },
    onSetupComplete: () => {
      console.log('Session key created — ready to pay!');
    },
    onSetupCancel: () => {
      console.log('User skipped session key setup');
    },
  },
});

// 2. Wrap your app — provider triggers setup UI when needed
export default function App() {
  return (
    <TapRailsThemeProvider>
      <YourApp />
    </TapRailsThemeProvider>
  );
}
```

`TapRailsThemeProvider` checks for a valid session key on mount. If none is found (or the key has expired and `autoRenew: true`), it automatically shows the `SessionKeySetupFlow` screen.

***

## Manual Setup with SessionKeySetupFlow

For more control over when the setup flow is shown:

```tsx WalletScreen.tsx theme={null}
import { SessionKeySetupFlow, useSessionKey } from '@taprails/tap-to-pay';

export function WalletScreen() {
  const [showSetup, setShowSetup] = useState(false);
  const { sessionKey, setupSessionKey } = useSessionKey();

  return (
    <>
      {!sessionKey && (
        <Button title="Enable Tap-to-Pay" onPress={() => setShowSetup(true)} />
      )}

      {showSetup && (
        <SessionKeySetupFlow
          onComplete={() => {
            console.log('Setup complete!');
            setShowSetup(false);
          }}
          onCancel={() => setShowSetup(false)}
        />
      )}
    </>
  );
}
```

***

## useSessionKey Hook

For programmatic session key management:

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

const {
  sessionKey,        // Current key data or null
  setupSessionKey,   // Trigger setup flow
  revokeSessionKey,  // Revoke and delete from Keychain
  isLoading,
  error,
  refresh,           // Re-read key from Keychain
} = useSessionKey();
```

### Checking key status

```tsx theme={null}
useEffect(() => {
  if (sessionKey) {
    const expiresAt = new Date(sessionKey.expiresAt);
    const isExpired = expiresAt < new Date();
    console.log(`Key ${isExpired ? 'expired' : 'valid'} — expires: ${expiresAt}`);
  }
}, [sessionKey]);
```

### Revoking a session key

```tsx theme={null}
const handleLogout = async () => {
  await revokeSessionKey();
  // Key is deleted from Keychain and deregistered from backend
  // Next payment will trigger setup flow again
};
```

***

## Integrating with Popular Wallet Providers

<CodeGroup>
  ```tsx WalletConnect (via wagmi) theme={null}
  import { useSignTransaction } from 'wagmi';

  const { signTransactionAsync } = useSignTransaction();

  ContactlessCryptoSDK.initialize({
    // ...
    sessionKey: {
      onSignTransaction: async (tx) => {
        const result = await signTransactionAsync({
          to: tx.to as `0x${string}`,
          data: tx.data as `0x${string}`,
          value: BigInt(tx.value),
        });
        return result;
      },
    },
  });
  ```

  ```tsx Privy theme={null}
  import { usePrivy } from '@privy-io/expo';

  const { user, sendTransaction } = usePrivy();

  ContactlessCryptoSDK.initialize({
    // ...
    sessionKey: {
      walletAddress: user?.wallet?.address || '',
      onSignTransaction: async (tx) => {
        const result = await sendTransaction({
          to: tx.to,
          data: tx.data,
          value: tx.value,
        });
        return result.transactionHash;
      },
    },
  });
  ```
</CodeGroup>

***

## Security Notes

* Session key **private keys never leave the device** — they're stored in the OS Keychain
* The daily limit is enforced on-chain by the ERC-20 approval
* Keys are automatically invalidated on the backend if revoked
* Each device generates its own keypair — a key from one device cannot be used on another
