# @cardql/react-native-tap

CardQL SDK for React Native tap-to-pay for secure in-person payments.

## Features

- **Payment Terminal Integration**: Built on certified payment infrastructure
- **Multiple Reader Support**: Works with certified card readers
- **Local Mobile Payments**: Tap-to-phone payments using device NFC (where supported)
- **Real-time Processing**: Live payment status and transaction updates
- **TypeScript Support**: Fully typed API with comprehensive type definitions
- **Error Handling**: Detailed error reporting with user-friendly messages
- **Offline Support**: Inherits offline capabilities from @cardql/react-native
- **Receipt Generation**: Automatic receipt creation with transaction details

## Installation

```bash
npm install @cardql/react-native-tap
# or
yarn add @cardql/react-native-tap
```

### Peer Dependencies

This package requires the following peer dependencies:

```bash
npm install @cardql/react-native-tap react react-native
```

## Quick Start

### 1. Setup Payment Terminal Provider

First, wrap your app with the payment terminal provider and implement a token provider:

```tsx
import React from "react";
import { PaymentTerminalProvider } from "@cardql/react-native-tap";

function App() {
  const fetchTokenProvider = async () => {
    // Fetch connection token from your backend
    const response = await fetch(`${API_URL}/connection_token`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
    });
    const { secret } = await response.json();
    return secret;
  };

  return (
    <PaymentTerminalProvider
      logLevel="verbose"
      tokenProvider={fetchTokenProvider}>
      {/* Your app components */}
    </PaymentTerminalProvider>
  );
}
```

### 2. Use the TapToPayReader Component

```tsx
import React from "react";
import { TapToPayReader } from "@cardql/react-native-tap";

function PaymentScreen() {
  const fetchTokenProvider = async () => {
    // Your token provider implementation
    const response = await fetch(`${API_URL}/connection_token`, {
      method: "POST",
    });
    const { secret } = await response.json();
    return secret;
  };

  const handleSuccess = (result) => {
    console.log("Payment successful:", result);
    // Handle successful payment
  };

  const handleError = (error) => {
    console.error("Payment failed:", error);
    // Handle payment error
  };

  return (
    <TapToPayReader
      amount="10.00"
      currency="USD"
      merchantID="your-merchant-id"
      userID="user-123"
      tokenProvider={fetchTokenProvider}
      onSuccess={handleSuccess}
      onError={handleError}
      autoInit={true}
      autoDiscoverReaders={true}
      simulated={__DEV__} // Use simulated readers in development
    />
  );
}
```

### 3. Using the Hook Directly

For more control, use the `useTapToPay` hook:

```tsx
import React, { useEffect } from "react";
import { useTapToPay } from "@cardql/react-native-tap";

function CustomPaymentFlow() {
  const fetchTokenProvider = async () => {
    const response = await fetch(`${API_URL}/connection_token`, {
      method: "POST",
    });
    const { secret } = await response.json();
    return secret;
  };

  const tapToPay = useTapToPay({
    config: {
      merchantID: "your-merchant-id",
      currency: "USD",
      paymentConfig: {
        tokenProvider: fetchTokenProvider,
        logLevel: "info",
        simulated: __DEV__,
      },
    },
    readerConfig: {
      discoveryMethod: "localMobile",
      simulated: __DEV__,
    },
    events: {
      onReaderDiscovered: (readers) => {
        console.log("Found readers:", readers.length);
      },
      onReaderConnected: (reader) => {
        console.log("Connected to reader:", reader.id);
      },
      onPaymentMethodCollected: (paymentMethod) => {
        console.log("Payment method collected:", paymentMethod.type);
      },
      onSuccess: (result) => {
        console.log("Payment successful:", result);
      },
      onError: (error) => {
        console.error("Payment error:", error);
      },
    },
    autoInit: true,
    autoDiscoverReaders: true,
  });

  const handlePayment = async () => {
    try {
      const result = await tapToPay.processPayment({
        amount: "25.00",
        currency: "USD",
        merchantID: "your-merchant-id",
        userID: "user-123",
        description: "Coffee and pastry",
      });

      if (result.success) {
        console.log("Payment completed:", result.payment);
      } else {
        console.error("Payment failed:", result.error);
      }
    } catch (error) {
      console.error("Payment processing error:", error);
    }
  };

  return (
    <View>
      <Text>
        Status: {tapToPay.isInitialized ? "Ready" : "Initializing..."}
      </Text>
      <Text>Readers: {tapToPay.discoveredReaders.length}</Text>
      <Text>Connected: {tapToPay.connectedReader?.id || "None"}</Text>

      <Button
        title="Start Payment"
        onPress={handlePayment}
        disabled={!tapToPay.connectedReader || tapToPay.isCollectingPayment}
      />
    </View>
  );
}
```

## Reader Types

### Local Mobile (Tap-to-Phone)

Use your device's built-in NFC for contactless payments:

```tsx
const readerConfig = {
  discoveryMethod: "localMobile",
  simulated: false,
};
```

### Bluetooth Readers

Connect to certified Bluetooth card readers:

```tsx
const readerConfig = {
  discoveryMethod: "bluetoothScan",
  simulated: false,
};
```

### Internet Readers

Connect to internet-connected payment terminal readers:

```tsx
const readerConfig = {
  discoveryMethod: "internet",
  locationId: "your-location-id",
};
```

## Configuration

### TapToPayConfig

```tsx
interface TapToPayConfig {
  merchantID: string;
  currency?: string;
  timeout?: number;
  acceptedCardTypes?: CardType[];
  paymentConfig: PaymentTerminalConfig;
  captureMethod?: "automatic" | "manual";
  customBranding?: {
    primaryColor?: string;
    logo?: string;
    merchantName?: string;
  };
}
```

### PaymentTerminalConfig

```tsx
interface PaymentTerminalConfig {
  tokenProvider: () => Promise<string>;
  logLevel?: "verbose" | "info" | "warn" | "error";
  simulated?: boolean;
}
```

### CardReaderConfig

```tsx
interface CardReaderConfig {
  discoveryMethod: "bluetoothScan" | "localMobile" | "internet";
  simulated?: boolean;
  locationId?: string;
  autoConnect?: boolean;
  enableTipping?: boolean;
  skipTipping?: boolean;
}
```

## Payment Flow

1. **Initialize**: Terminal SDK initializes with your token provider
2. **Discover**: Find available card readers (automatic or manual)
3. **Connect**: Connect to a specific reader
4. **Create Payment Intent**: Initialize payment with amount and currency
5. **Collect Payment Method**: Reader captures card information
6. **Confirm Payment**: Process the payment through the payment gateway
7. **Complete**: Payment is confirmed and receipt is generated

## Error Handling

The SDK provides detailed error information:

```tsx
interface TapToPayError {
  code: TapToPayErrorCode;
  message: string;
  details?: any;
  gatewayError?: any;
  userFriendlyMessage?: string;
  canRetry?: boolean;
  suggestedAction?: string;
}
```

Common error codes:

- `TERMINAL_NOT_SUPPORTED`: Device doesn't support payment terminal
- `READER_CONNECTION_FAILED`: Could not connect to card reader
- `PAYMENT_COLLECTION_FAILED`: Could not collect payment method
- `PAYMENT_CONFIRMATION_FAILED`: Could not confirm payment
- `TRANSACTION_DECLINED`: Payment was declined by issuer

## Events

Listen to payment flow events:

```tsx
const events = {
  onReaderDiscovered: (readers: Reader[]) => void;
  onReaderConnected: (reader: Reader) => void;
  onReaderDisconnected: (reason?: string) => void;
  onPaymentMethodCollected: (paymentMethod: PaymentMethod) => void;
  onPaymentIntentCreated: (paymentIntent: PaymentIntent) => void;
  onDisplayMessage: (message: string) => void;
  onError: (error: TapToPayError) => void;
  onSuccess: (result: TapPaymentResult) => void;
  onCancel: () => void;
};
```

## Testing

### Simulated Mode

Enable simulated mode for testing:

```tsx
<TapToPayReader
  simulated={true}
  // ... other props
/>
```

### Test Cards

In simulated mode, use these test card numbers:

- Visa: `4242424242424242`
- Mastercard: `5555555555554444`
- American Express: `378282246310005`

## Requirements

### iOS

- iOS 15.1 or later
- NFC-capable device for local mobile payments
- Xcode 13 or later

### Android

- Android API level 26 (Android 8.0) or later
- NFC-capable device for local mobile payments
- compileSdkVersion 35
- targetSdkVersion 35

## Security

- All payment data is handled securely by the payment terminal
- Card data never touches your application
- PCI DSS compliant by design
- End-to-end encryption for all transactions

## Support

For support with payment terminal integration:

- [CardQL Documentation](https://docs.cardql.com)

## License

MIT License - see LICENSE file for details.
