JSPM

@cardql/react-native

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

CardQL SDK for React Native applications with mobile-optimized features

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.