1

Getting Started

To get started with Chipi Pay in your Next.js application
npx create-next-app@latest my-chipi-app
cd my-chipi-app
With Clerk, you can follow their Quickstart Guide to get started.
2

Install the Chipi SDK

First, install the required packages:
# Install Chipi Pay SDK
npm install @chipi-pay/chipi-sdk

# Install Clerk
npm install @clerk/nextjs
3

Setup the Chipi SDK Provider

  1. Create a .env file in your project root and add your API keys:
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=your_clerk_publishable_key
CLERK_SECRET_KEY=your_clerk_secret_key
NEXT_PUBLIC_CHIPI_PUBLIC_KEY=your_chipi_api_public_key
  1. Set up the Chipi Pay provider in your application:
"use client";

import { ChipiProvider } from "@chipi-pay/chipi-sdk";

const API_PUBLIC_KEY = process.env.NEXT_PUBLIC_CHIPI_PUBLIC_KEY
if (!API_PUBLIC_KEY) throw new Error("API_PUBLIC_KEY is not set");


export function Providers({ children }: { children: React.ReactNode }) {
  return (
    <ChipiProvider
      config={{
        apiPublicKey: API_PUBLIC_KEY,
      }}
    >
      {children}
    </ChipiProvider>
  );
}

  1. Update your root layout to use the providers:
// app/layout.tsx
import { ClerkProvider } from '@clerk/nextjs'
import { Providers } from './providers'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>
        <ClerkProvider>
          <Providers>{children}</Providers>
        </ClerkProvider>
      </body>
    </html>
  )
}
4

Creating a Wallet

Create a wallet creation component:
// app/components/CreateWallet.tsx
"use client";

import { useState } from "react";
import { useUser } from "@clerk/nextjs";
import { useCreateWallet } from "@chipi-pay/chipi-sdk";
import { saveWallet } from "../actions";

export default function CreateWallet() {
  const { user } = useUser();
  const { createWalletAsync } = useCreateWallet();
  const [encryptKey, setEncryptKey] = useState("");
  const [isProcessing, setIsProcessing] = useState(false);
  const [error, setError] = useState("");
  const [success, setSuccess] = useState("");

  const handleCreateWallet = async () => {
    if (!encryptKey) {
      setError("Please enter an encryption key");
      return;
    }

    try {
      setIsProcessing(true);
      setError("");
      setSuccess("");

      const response = await createWalletAsync({
        encryptKey: encryptKey,
        bearerToken: await user?.getToken({ template: "chipi-sdk" }),
      });

      console.log('createWalletResponse', response);
      
      // Save wallet to Clerk's public metadata
      await saveWallet(response.wallet);
      
      setSuccess("Wallet created successfully!");
      setEncryptKey("");
    } catch (error) {
      setError(error.message || "Failed to create wallet");
      console.error("Error creating wallet:", error);
    } finally {
      setIsProcessing(false);
    }
  };

  return (
    <div className="space-y-4 p-4 border rounded-lg">
      <h2 className="text-xl font-semibold">Create Wallet</h2>
      
      {error && <div className="text-red-500 text-sm">{error}</div>}
      {success && <div className="text-green-500 text-sm">{success}</div>}
      
      <div className="space-y-2">
        <input
          type="password"
          placeholder="Enter encryption key"
          value={encryptKey}
          onChange={(e) => setEncryptKey(e.target.value)}
          className="w-full p-2 border rounded"
        />
        
        <button
          onClick={handleCreateWallet}
          disabled={isProcessing || !encryptKey}
          className="bg-blue-500 text-white px-4 py-2 rounded disabled:opacity-50 w-full"
        >
          {isProcessing ? "Creating..." : "Create Wallet"}
        </button>
      </div>
    </div>
  );
}
5

Storing the Wallet

We need to save the wallet for each new Clerk user. We will use Clerk’s public metadata to store the wallet info.
  1. Create a server action to save the wallet:
// app/actions.ts
"use server";

import { auth, clerkClient } from "@clerk/nextjs";

export async function saveWallet(walletData: any) {
  const { userId } = auth();
  
  if (!userId) {
    throw new Error("Not authenticated");
  }

  // Save wallet to Clerk's public metadata
  await clerkClient.users.updateUser(userId, {
    publicMetadata: {
      wallet: JSON.stringify(walletData)
    }
  });
}

export async function getWallet() {
  const { userId } = auth();
  
  if (!userId) {
    throw new Error("Not authenticated");
  }

  const user = await clerkClient.users.getUser(userId);
  const walletData = user.publicMetadata.wallet;
  
  if (!walletData) {
    return null;
  }

  return JSON.parse(walletData as string);
}
6

Sending USDC Example

Here’s an example of implementing a transfer flow with Clerk authentication:
// app/components/Transfer.tsx
"use client";

import { useState } from "react";
import { useUser } from "@clerk/nextjs";
import { useTransfer } from "@chipi-pay/chipi-sdk";

const USDC_CONTRACT = "0x053c91253bc9682c04929ca02ed00b3e423f6710d2ee7e0d5ebb06f3ecf368a8";

export default function Transfer() {
  const { user } = useUser();
  const { transferAsync } = useTransfer();
  const [amount, setAmount] = useState("");
  const [recipientAddress, setRecipientAddress] = useState("");
  const [encryptKey, setEncryptKey] = useState("");
  const [isProcessing, setIsProcessing] = useState(false);
  const [error, setError] = useState("");
  const [success, setSuccess] = useState("");

  const handleTransfer = async () => {
    if (!amount || !recipientAddress || !encryptKey) {
      setError("Please fill in all fields");
      return;
    }

    try {
      setIsProcessing(true);
      setError("");
      setSuccess("");

      // Get stored wallet from Clerk's public metadata
      const storedWallet = user?.publicMetadata?.wallet;
      if (!storedWallet) {
        setError("No wallet found. Please create a wallet first.");
        return;
      }

      // Get the bearer token
      const token = await user?.getToken({ template: "chipi-sdk" });
      if (!token) {
        setError("No bearer token found");
        return;
      }

      const transferResponse = await transferAsync({
        encryptKey: encryptKey,
        bearerToken: token,
        wallet: JSON.parse(storedWallet as string),
        contractAddress: USDC_CONTRACT,
        recipient: recipientAddress,
        amount: amount,
        decimals: 6,
      });

      console.log("transfer response", transferResponse);
      setSuccess("Transfer completed successfully!");
      
      // Clear form
      setAmount("");
      setRecipientAddress("");
      setEncryptKey("");
    } catch (error) {
      setError(error.message || "Transfer failed");
      console.error("Transfer error:", error);
    } finally {
      setIsProcessing(false);
    }
  };

  return (
    <div className="space-y-4 p-4 border rounded-lg">
      <h2 className="text-xl font-semibold">Transfer USDC</h2>
      
      {error && <div className="text-red-500 text-sm">{error}</div>}
      {success && <div className="text-green-500 text-sm">{success}</div>}
      
      <div className="space-y-2">
        <input
          type="text"
          placeholder="Recipient Address"
          value={recipientAddress}
          onChange={(e) => setRecipientAddress(e.target.value)}
          className="w-full p-2 border rounded"
        />
        
        <input
          type="number"
          placeholder="Amount"
          value={amount}
          onChange={(e) => setAmount(e.target.value)}
          className="w-full p-2 border rounded"
        />
        
        <input
          type="password"
          placeholder="Encryption Key"
          value={encryptKey}
          onChange={(e) => setEncryptKey(e.target.value)}
          className="w-full p-2 border rounded"
        />
        
        <button
          onClick={handleTransfer}
          disabled={isProcessing || !amount || !recipientAddress || !encryptKey}
          className="bg-green-500 text-white px-4 py-2 rounded disabled:opacity-50 w-full"
        >
          {isProcessing ? "Processing..." : "Make Transfer"}
        </button>
      </div>
    </div>
  );
}
Need help? Check out our Telegram Community for support and to connect with other developers.