JSPM

@cardql/react-native-tap

1.0.1
  • ESM via JSPM
  • ES Module Entrypoint
  • Export Map
  • Keywords
  • License
  • Repository URL
  • TypeScript Types
  • README
  • Created
  • Published
  • Downloads 3
  • Score
    100M100P100Q42205F
  • License MIT

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

Package Exports

  • @cardql/react-native-tap

Readme

@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

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

Peer Dependencies

This package requires the following peer dependencies:

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:

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

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:

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:

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

Bluetooth Readers

Connect to certified Bluetooth card readers:

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

Internet Readers

Connect to internet-connected payment terminal readers:

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

Configuration

TapToPayConfig

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

PaymentTerminalConfig

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

CardReaderConfig

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:

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:

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:

<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:

License

MIT License - see LICENSE file for details.