Skip to main content
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.
App.tsx
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:
WalletScreen.tsx
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:
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

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

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

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;
    },
  },
});

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