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
- Initialize: Terminal SDK initializes with your token provider
- Discover: Find available card readers (automatic or manual)
- Connect: Connect to a specific reader
- Create Payment Intent: Initialize payment with amount and currency
- Collect Payment Method: Reader captures card information
- Confirm Payment: Process the payment through the payment gateway
- 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 terminalREADER_CONNECTION_FAILED
: Could not connect to card readerPAYMENT_COLLECTION_FAILED
: Could not collect payment methodPAYMENT_CONFIRMATION_FAILED
: Could not confirm paymentTRANSACTION_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.