UNPKG

@cardql/react-native

Version:

CardQL SDK for React Native applications with mobile-optimized features

602 lines (495 loc) 13.5 kB
# @cardql/react-native CardQL SDK for React Native applications with mobile-optimized features including offline support, secure storage, and native UI components. ## Installation ```bash 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: ```bash # 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: ```tsx 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: ```tsx 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: ```tsx 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: ```tsx 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: ```tsx 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: ```tsx 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 ```tsx interface CardQLProviderProps { config: CardQLConfig; children: ReactNode; enableOfflineMode?: boolean; // Default: false enableSecureStorage?: boolean; // Default: true apiKeyStorageKey?: string; // Default: 'cardql_api_key' } ``` ### Context Value ```tsx interface CardQLContextValue { cardql: CardQL; config: CardQLConfig; isOnline: boolean; enableOfflineMode: boolean; updateApiKey: (apiKey: string) => Promise<void>; clearStoredData: () => Promise<void>; } ``` ### Offline Queue ```tsx 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 ```tsx <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: ```tsx 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: ```tsx 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: ```tsx 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: ```tsx 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: ```tsx 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 ```tsx // ✅ 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 ```tsx // ✅ 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 ```tsx 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.