Building with Expo

This guide will walk you through integrating Chipi Pay into your Expo application with biometric authentication and secure storage. We’ll cover everything from installation to implementing secure payment flows.

Prerequisites

  • Node.js 16 or later
  • Expo CLI
  • Basic knowledge of React Native
  • A Chipi Pay account
  • Clerk account for authentication
  • Device with biometric authentication support (for biometric features)

Getting Started

To get started with Chipi Pay in your Expo application, you’ll need to have a basic Expo project set up. If you don’t have one yet, you can create it using:
npx create-expo-app my-chipi-app
cd my-chipi-app

Installation

First, install the required packages:
# Install Chipi Pay SDK
npx expo install @chipi-stack/chipi-expo

# Install Clerk and authentication packages
npx expo install @clerk/clerk-expo expo-secure-store expo-web-browser

# Install biometric authentication package
npx expo install expo-local-authentication

Configuration

  1. Create a .env file in your project root and add your API keys:
EXPO_PUBLIC_CHIPI_API_KEY=your_api_key_here
EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key
  1. Update your app.json to include the required permissions:
{
  "expo": {
    "plugins": [
      [
        "expo-local-authentication",
        {
          "faceIDPermission": "Allow $(PRODUCT_NAME) to use Face ID to unlock your wallet."
        }
      ],
      "expo-secure-store"
    ],
    "ios": {
      "supportsTablet": true,
      "infoPlist": {
        "NSFaceIDUsageDescription": "Allow $(PRODUCT_NAME) to use Face ID to unlock your wallet."
      }
    }
  }
}

Implementing Secure Authentication Flow

  1. Initialize the SDKs with secure storage:
// App.tsx
import { ClerkProvider } from "@clerk/clerk-expo";
import { ChipiProvider } from "@chipi-stack/chipi-expo";
import { tokenCache } from "@clerk/clerk-expo/token-cache";

export default function App() {
  return (
    <ClerkProvider 
      publishableKey={process.env.EXPO_PUBLIC_CLERK_PUBLISHABLE_KEY}
      tokenCache={tokenCache}
    >
      <ChipiProvider apiKey={process.env.EXPO_PUBLIC_CHIPI_API_KEY}>
        <YourApp />
      </ChipiProvider>
    </ClerkProvider>
  );
}

Adding Clerk JWT Templates

1

1. Define the Data to Gather

Decide which user attributes you need to include in your JWT. For example: userId, email, role, or any custom claims relevant to your application.
2

2. Create the JWT Payload Template

Draft a template for your JWT payload. For example:
{
"email": "{{user.primary_email_address.email_address}}",
"orgId": "{{organization.id}}"
}

Replace the email and orgId Keys with the actual data you want to gather.
3

3. Generate the JWT

Use your backend or authentication provider to generate a JWT using the template. Click to see more about JWT tokens:This JWT can now be used to securely transmit the gathered user data.
4

4. Register JWT in Your Application

Register JWKS Endpoint URL in the dashboard

Setting up Secure Storage and Biometric Authentication

  1. Create a secure storage utility:
// utils/secureStorage.ts
import * as SecureStore from 'expo-secure-store';
import * as LocalAuthentication from 'expo-local-authentication';
import type { WalletData } from '@chipi-stack/chipi-expo';

export const saveWalletStorage = async (pin: string, walletData: WalletData) => {
  await SecureStore.setItemAsync(
    "wallet",
    JSON.stringify(walletData.wallet)
  );
  await SecureStore.setItemAsync("wallet_pin", pin, {
    requireAuthentication: true,
  });
}

export const savePinStorage = async (pin: string) => {
  await SecureStore.setItemAsync("wallet_pin", pin, {
    requireAuthentication: true,
  });
}

export const getPinStorage = async () => {
  return await SecureStore.getItemAsync("wallet_pin");
}

export const getWalletStorage = async () => {
  return await SecureStore.getItemAsync("wallet");
}

export const deleteSecureItem = async (key: string) => {
  try {
    await SecureStore.deleteItemAsync(key);
  } catch (error) {
    console.error('Error deleting secure item:', error);
    throw error;
  }
}

export const isBiometricAvailable = async () => {
  const compatible = await LocalAuthentication.hasHardwareAsync();
  const enrolled = await LocalAuthentication.isEnrolledAsync();
  return compatible && enrolled;
}

Next Steps

  • Add error handling and loading states
  • Customize the UI to match your app’s design
Need help? Check out our Telegram Community for support and to connect with other developers.