Skip to main content

React Native SDK

The official PayKore SDK for React Native. Wraps the core @paykore/sdk with React-friendly patterns, a context provider and hooks, for building payment flows directly in your mobile app.


Installation​

npm install @paykore/react-native-sdk

This package requires @paykore/sdk as a peer dependency. If it isn't already installed:

npm install @paykore/sdk @paykore/react-native-sdk

Setup Provider​

Wrap your app in PayKoreProvider once, near the root of your component tree:

import { PayKoreProvider } from '@paykore/react-native-sdk';

export default function App() {
return (
<PayKoreProvider apiKey={process.env.EXPO_PUBLIC_PAYKORE_KEY} environment="sandbox">
<YourApp />
</PayKoreProvider>
);
}

Every hook described below reads its configuration from this provider, you won't need to pass an API key or environment to each hook individually.

warning

Only use publishable keys (pk_*) in mobile apps. Never embed secret keys (sk_*) in React Native code, anything bundled into your app, including environment variables prefixed for client exposure (like EXPO_PUBLIC_*), can be extracted from the compiled app binary by anyone who downloads it from the App Store or Play Store.


useWallet hook​

const { wallet, balance, loading, error, refresh } = useWallet('wallet_id');
Return valueTypeDescription
walletWallet | nullThe full wallet object once loaded.
balance{ kobo: number; naira: string } | nullConvenience accessor for the wallet's current balance.
loadingbooleantrue while the initial fetch or a refresh() is in flight.
errorPayKoreError | nullPopulated if the fetch fails.
refresh() => Promise<void>Re-fetches the wallet on demand.

Complete example, balance display with refresh:

import { useWallet } from '@paykore/react-native-sdk';
import { View, Text, Button, ActivityIndicator } from 'react-native';

function WalletBalance({ walletId }: { walletId: string }) {
const { wallet, balance, loading, error, refresh } = useWallet(walletId);

if (loading && !wallet) return <ActivityIndicator />;
if (error) return <Text>Couldn't load wallet: {error.message}</Text>;

return (
<View>
<Text>Balance: ₦{balance?.naira}</Text>
<Button title="Refresh" onPress={refresh} disabled={loading} />
</View>
);
}

useTransfer hook​

const { transfer, loading, error } = useTransfer();

// Usage:
await transfer.p2p({ sourceWallet, destWallet, amount, idempotencyKey });

transfer.p2p(...) triggers a P2P transfer and updates loading/error for the duration of the call, so you can drive a loading spinner or disabled button state directly from the hook without managing your own local state.


usePayment hook​

const { createQRIntent, payQRIntent, initiateUSSD, loading, error } = usePayment();

This hook bundles the three non-transfer payment flows, QR intent creation, QR intent payment, and USSD initiation, behind one shared loading/error pair, since a given screen typically only triggers one of them at a time.


QR code payment component example​

Mobile apps can't receive webhooks directly, there's no public URL for PayKore to push to. The standard pattern is: create the intent, render the QR, and poll for completion from the client while your backend's webhook handler is the source of truth server-side.

import { useState, useEffect } from 'react';
import { View, Text, ActivityIndicator } from 'react-native';
import QRCode from 'react-native-qrcode-svg';
import { usePayment, usePaymentStatus } from '@paykore/react-native-sdk';

function QRCheckout({ merchantWalletId, amountKobo, reference }: QRCheckoutProps) {
const { createQRIntent, loading, error } = usePayment();
const [intent, setIntent] = useState<QRIntent | null>(null);

useEffect(() => {
createQRIntent({ merchantWalletId, amountKobo, reference }).then(setIntent);
}, []);

// Polls the intent status every 2s until it resolves
const { status } = usePaymentStatus(intent?.intentId, { pollIntervalMs: 2000 });

if (loading || !intent) return <ActivityIndicator />;
if (error) return <Text>Couldn't create payment: {error.message}</Text>;

if (status === 'completed') {
return <Text>✅ Payment received!</Text>;
}

return (
<View>
<QRCode value={intent.qrPayload} size={240} />
<Text>Waiting for payment...</Text>
</View>
);
}

usePaymentStatus handles the polling loop internally and stops once the status reaches a terminal state (completed, failed, or expired), so you don't need to manually clear an interval.


When a user scans a PayKore QR code with their own wallet app (rather than your merchant app rendering the code), that wallet app needs a way to open and load the specific payment intent. This is handled via deep links.

Expo configuration (app.json):

{
"expo": {
"scheme": "yourapp",
"ios": {
"associatedDomains": ["applinks:yourapp.com"]
},
"android": {
"intentFilters": [
{
"action": "VIEW",
"data": [{ "scheme": "yourapp", "host": "pay" }],
"category": ["BROWSABLE", "DEFAULT"]
}
]
}
}
}

This registers yourapp://pay as a URL your app will handle. A scanned QR code that resolves to yourapp://pay?intentId=qri_4hJkL5mNpQ will open your app directly to the payment screen.

Handling the incoming intent ID:

import * as Linking from 'expo-linking';
import { useEffect } from 'react';

function usePaymentDeepLink(onIntentReceived: (intentId: string) => void) {
useEffect(() => {
const subscription = Linking.addEventListener('url', ({ url }) => {
const { queryParams } = Linking.parse(url);
if (queryParams?.intentId) {
onIntentReceived(queryParams.intentId as string);
}
});

return () => subscription.remove();
}, [onIntentReceived]);
}

Use the extracted intentId to call payQRIntent from the usePayment hook once the user confirms the amount on screen.


Error handling in hooks​

Every hook's error value is typed as PayKoreError | null, the same error class used by the underlying @paykore/sdk:

import type { PayKoreError } from '@paykore/sdk';

const { wallet, error } = useWallet(walletId);

if (error) {
const typedError: PayKoreError = error;
console.log(typedError.code); // e.g. "WALLET_NOT_FOUND"
console.log(typedError.requestId); // for support escalation
}

Branch on error.code exactly as you would server-side, see Web SDK: Error handling for the full list of codes you may encounter.


Next steps​

  • Web SDK → The underlying server-side SDK and full error code reference.
  • QR Payments → The full QR payment flow and payload structure.