Package Exports
- @cardql/react-native
Readme
@cardql/react-native
CardQL SDK for React Native applications with mobile-optimized features including offline support, secure storage, and native UI components.
Installation
npm install @cardql/react-native
# or
yarn add @cardql/react-native
# or
pnpm add @cardql/react-native
Additional Dependencies
For full functionality, you may want to install these optional dependencies:
# For secure storage (recommended)
npm install react-native-keychain
# or for Expo
npm install expo-secure-store
# For network status monitoring (recommended)
npm install @react-native-community/netinfo
# For persistent storage
npm install @react-native-async-storage/async-storage
Quick Start
1. Setup Provider
Wrap your app with the CardQL provider:
import React from "react";
import { CardQLProvider } from "@cardql/react-native";
function App() {
return (
<CardQLProvider
config={{
apiKey: "your-api-key",
endpoint: "https://api.cardql.com/graphql",
}}
enableOfflineMode={true}
enableSecureStorage={true}>
<YourApp />
</CardQLProvider>
);
}
2. Use Hooks
Use CardQL hooks in your components:
import React from "react";
import { View, Text, TouchableOpacity, FlatList } from "react-native";
import { usePayments, useCreatePayment } from "@cardql/react-native";
function PaymentScreen() {
const { data: paymentsData, loading, error } = usePayments();
const createPayment = useCreatePayment({
onSuccess: (data) => {
console.log("Payment created:", data.createPayment);
},
});
const handleCreatePayment = async () => {
await createPayment.mutateAsync({
amount: "10.00",
currency: "USD",
merchantID: "merchant_123",
userID: "user_456",
});
};
if (loading) return <Text>Loading...</Text>;
if (error) return <Text>Error: {error.message}</Text>;
return (
<View>
<TouchableOpacity
onPress={handleCreatePayment}
disabled={createPayment.loading}>
<Text>{createPayment.loading ? "Creating..." : "Create Payment"}</Text>
</TouchableOpacity>
<FlatList
data={paymentsData?.payments}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<Text>
{item.amount} {item.currency} - {item.status}
</Text>
)}
/>
</View>
);
}
3. Use Payment Sheet
Use the pre-built payment sheet:
import React, { useState } from "react";
import { View, TouchableOpacity, Text } from "react-native";
import { PaymentSheet } from "@cardql/react-native";
function CheckoutScreen() {
const [showPaymentSheet, setShowPaymentSheet] = useState(false);
return (
<View>
<TouchableOpacity onPress={() => setShowPaymentSheet(true)}>
<Text>Pay Now</Text>
</TouchableOpacity>
<PaymentSheet
visible={showPaymentSheet}
onClose={() => setShowPaymentSheet(false)}
merchantID="merchant_123"
userID="user_456"
onSuccess={(payment) => {
console.log("Payment successful:", payment);
// Navigate to success screen
}}
onError={(error) => {
console.error("Payment failed:", error);
}}
/>
</View>
);
}
Mobile-Specific Features
Offline Support
The React Native SDK includes built-in offline support:
import React from "react";
import { useOfflineQueue, useIsOnline } from "@cardql/react-native";
function OfflineAwareComponent() {
const isOnline = useIsOnline();
const { queue, addToQueue, processQueue } = useOfflineQueue();
const handleOfflinePayment = async () => {
if (!isOnline) {
// Add to offline queue
await addToQueue(CREATE_PAYMENT, {
amount: "25.99",
currency: "USD",
merchantID: "merchant_123",
userID: "user_456",
});
}
};
return (
<View>
<Text>Status: {isOnline ? "Online" : "Offline"}</Text>
<Text>Queued Operations: {queue.length}</Text>
{!isOnline && (
<TouchableOpacity onPress={handleOfflinePayment}>
<Text>Create Payment (Offline)</Text>
</TouchableOpacity>
)}
</View>
);
}
Secure Storage
API keys and sensitive data are automatically stored securely:
import React from "react";
import { useCardQL } from "@cardql/react-native";
function AuthComponent() {
const { updateApiKey, clearStoredData } = useCardQL();
const handleLogin = async (apiKey: string) => {
// API key will be securely stored
await updateApiKey(apiKey);
};
const handleLogout = async () => {
// Clear all stored data
await clearStoredData();
};
return (
<View>
<TouchableOpacity onPress={() => handleLogin("new-api-key")}>
<Text>Login</Text>
</TouchableOpacity>
<TouchableOpacity onPress={handleLogout}>
<Text>Logout</Text>
</TouchableOpacity>
</View>
);
}
Network Status Monitoring
Monitor network connectivity:
import React from "react";
import {
useNetworkStatus,
useIsOnline,
useIsOffline,
useNetworkType,
} from "@cardql/react-native";
function NetworkStatusComponent() {
const networkStatus = useNetworkStatus();
const isOnline = useIsOnline();
const isOffline = useIsOffline();
const networkType = useNetworkType();
return (
<View>
<Text>Connected: {isOnline ? "Yes" : "No"}</Text>
<Text>Network Type: {networkType || "Unknown"}</Text>
<Text>WiFi Enabled: {networkStatus.isWifiEnabled ? "Yes" : "No"}</Text>
</View>
);
}
API Reference
Provider Props
interface CardQLProviderProps {
config: CardQLConfig;
children: ReactNode;
enableOfflineMode?: boolean; // Default: false
enableSecureStorage?: boolean; // Default: true
apiKeyStorageKey?: string; // Default: 'cardql_api_key'
}
Context Value
interface CardQLContextValue {
cardql: CardQL;
config: CardQLConfig;
isOnline: boolean;
enableOfflineMode: boolean;
updateApiKey: (apiKey: string) => Promise<void>;
clearStoredData: () => Promise<void>;
}
Offline Queue
const {
queue, // Array of queued operations
addToQueue, // Add operation to queue
processQueue, // Process all queued operations
clearQueue, // Clear the queue
removeFromQueue, // Remove specific operation
isProcessing, // Whether queue is being processed
} = useOfflineQueue({
maxRetries: 3, // Maximum retry attempts
retryDelay: 1000, // Delay between retries (ms)
storageKey: "custom_queue_key",
});
Payment Sheet
<PaymentSheet
visible={boolean}
onClose={() => void}
merchantID={string}
userID={string}
onSuccess={(payment) => void}
onError={(error) => void}
defaultAmount="10.00"
defaultCurrency="USD"
defaultDescription="Payment"
// Styling props
style={ViewStyle}
headerStyle={ViewStyle}
inputStyle={TextStyle}
buttonStyle={ViewStyle}
buttonTextStyle={TextStyle}
/>
Advanced Usage
Custom Storage Implementation
Replace the default storage with your own:
import { Storage } from "@cardql/react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import * as Keychain from "react-native-keychain";
class CustomSecureStorage implements Storage {
async getItem(key: string): Promise<string | null> {
try {
const credentials = await Keychain.getInternetCredentials(key);
return credentials ? credentials.password : null;
} catch {
return null;
}
}
async setItem(key: string, value: string): Promise<void> {
await Keychain.setInternetCredentials(key, key, value);
}
async removeItem(key: string): Promise<void> {
await Keychain.resetInternetCredentials(key);
}
async clear(): Promise<void> {
await Keychain.resetGenericPassword();
}
}
// Use custom storage
export const customStorage = new CustomSecureStorage();
Offline-First Architecture
Build an offline-first app:
import React, { useEffect } from "react";
import {
useOfflineQueue,
useIsOnline,
usePayments,
} from "@cardql/react-native";
function OfflineFirstComponent() {
const isOnline = useIsOnline();
const { queue, processQueue } = useOfflineQueue();
const { data: payments, refetch } = usePayments({
enabled: isOnline, // Only fetch when online
});
// Auto-sync when coming back online
useEffect(() => {
if (isOnline) {
processQueue();
refetch();
}
}, [isOnline, processQueue, refetch]);
return (
<View>
{!isOnline && (
<Text style={{ color: "orange" }}>
Offline mode - {queue.length} operations queued
</Text>
)}
{/* Your UI */}
</View>
);
}
Background Sync
Handle background synchronization:
import { useEffect } from "react";
import { AppState } from "react-native";
import { useOfflineQueue } from "@cardql/react-native";
function BackgroundSyncHandler() {
const { processQueue } = useOfflineQueue();
useEffect(() => {
const handleAppStateChange = (nextAppState: string) => {
if (nextAppState === "active") {
// App came to foreground, process queue
processQueue();
}
};
const subscription = AppState.addEventListener(
"change",
handleAppStateChange
);
return () => subscription?.remove();
}, [processQueue]);
return null;
}
Error Handling
Handle mobile-specific errors:
import { Alert } from "react-native";
import { useCreatePayment } from "@cardql/react-native";
function PaymentWithErrorHandling() {
const createPayment = useCreatePayment({
onError: (error, variables) => {
// Handle different error types
if (error.code === "NETWORK_ERROR") {
Alert.alert(
"Network Error",
"Please check your internet connection and try again.",
[{ text: "OK" }]
);
} else if (error.code === "VALIDATION_ERROR") {
Alert.alert("Invalid Payment", "Please check your payment details.", [
{ text: "OK" },
]);
} else {
Alert.alert(
"Payment Failed",
"An unexpected error occurred. Please try again.",
[{ text: "OK" }]
);
}
},
});
// Component implementation...
}
Styling
The PaymentSheet component can be fully customized:
const customStyles = {
container: {
backgroundColor: "#f5f5f5",
},
header: {
backgroundColor: "#007bff",
paddingVertical: 20,
},
title: {
color: "#fff",
fontSize: 20,
fontWeight: "bold",
},
input: {
borderRadius: 12,
paddingHorizontal: 20,
paddingVertical: 15,
fontSize: 16,
backgroundColor: "#fff",
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
},
button: {
backgroundColor: "#28a745",
paddingVertical: 18,
borderRadius: 12,
},
buttonText: {
fontSize: 18,
fontWeight: "bold",
},
};
<PaymentSheet
style={customStyles.container}
headerStyle={customStyles.header}
inputStyle={customStyles.input}
buttonStyle={customStyles.button}
buttonTextStyle={customStyles.buttonText}
// ... other props
/>;
Best Practices
1. Always Handle Offline States
// ✅ Good - Handle offline gracefully
function PaymentButton() {
const isOnline = useIsOnline();
const { addToQueue } = useOfflineQueue();
const createPayment = useCreatePayment();
const handlePayment = async (paymentData) => {
if (isOnline) {
await createPayment.mutateAsync(paymentData);
} else {
await addToQueue(CREATE_PAYMENT, paymentData);
Alert.alert("Offline", "Payment queued for when you're back online");
}
};
return (
<TouchableOpacity onPress={() => handlePayment(data)}>
<Text>{isOnline ? "Pay Now" : "Queue Payment"}</Text>
</TouchableOpacity>
);
}
2. Secure Storage for Sensitive Data
// ✅ Good - Use secure storage for API keys
<CardQLProvider
config={{ endpoint: 'https://api.cardql.com/graphql' }}
enableSecureStorage={true}
apiKeyStorageKey="my_app_cardql_key"
>
<App />
</CardQLProvider>
// ❌ Avoid - Don't hardcode API keys
<CardQLProvider
config={{
apiKey: 'hardcoded-key', // Don't do this
endpoint: 'https://api.cardql.com/graphql'
}}
>
<App />
</CardQLProvider>
3. Provide Feedback for Long Operations
function PaymentForm() {
const createPayment = useCreatePayment();
return (
<View>
<TouchableOpacity
onPress={handleSubmit}
disabled={createPayment.loading}
style={[styles.button, createPayment.loading && styles.buttonDisabled]}>
{createPayment.loading ? (
<ActivityIndicator color="#fff" />
) : (
<Text>Create Payment</Text>
)}
</TouchableOpacity>
</View>
);
}
Platform-Specific Considerations
iOS
- Ensure you have the Keychain sharing capability enabled for secure storage
- Test with different network conditions using Network Link Conditioner
Android
- Add INTERNET permission in AndroidManifest.xml
- Consider Android's battery optimization settings for background sync
License
MIT
Support
For support, please contact the CardQL team or visit our documentation.