
Security News
/Research
Wallet-Draining npm Package Impersonates Nodemailer to Hijack Crypto Transactions
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
@cardql/react-native
Advanced tools
CardQL SDK for React Native applications with mobile-optimized features
CardQL SDK for React Native applications with mobile-optimized features including offline support, secure storage, and native UI components.
npm install @cardql/react-native
# or
yarn add @cardql/react-native
# or
pnpm add @cardql/react-native
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
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>
);
}
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>
);
}
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>
);
}
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>
);
}
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>
);
}
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>
);
}
interface CardQLProviderProps {
config: CardQLConfig;
children: ReactNode;
enableOfflineMode?: boolean; // Default: false
enableSecureStorage?: boolean; // Default: true
apiKeyStorageKey?: string; // Default: 'cardql_api_key'
}
interface CardQLContextValue {
cardql: CardQL;
config: CardQLConfig;
isOnline: boolean;
enableOfflineMode: boolean;
updateApiKey: (apiKey: string) => Promise<void>;
clearStoredData: () => Promise<void>;
}
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",
});
<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}
/>
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();
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>
);
}
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;
}
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...
}
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
/>;
// ✅ 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>
);
}
// ✅ 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>
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>
);
}
MIT
For support, please contact the CardQL team or visit our documentation.
FAQs
CardQL SDK for React Native applications with mobile-optimized features
The npm package @cardql/react-native receives a total of 2 weekly downloads. As such, @cardql/react-native popularity was classified as not popular.
We found that @cardql/react-native demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
/Research
Malicious npm package impersonates Nodemailer and drains wallets by hijacking crypto transactions across multiple blockchains.
Security News
This episode explores the hard problem of reachability analysis, from static analysis limits to handling dynamic languages and massive dependency trees.
Security News
/Research
Malicious Nx npm versions stole secrets and wallet info using AI CLI tools; Socket’s AI scanner detected the supply chain attack and flagged the malware.