Next.js Integration
Accept crypto payments in Next.js App Router and Pages Router applications
Next.js Integration
Integrate Plaidly into your Next.js app using the official Node SDK.
npm install @plaidly/nodeEnvironment variables
Add to your .env.local:
PLAIDLY_API_KEY=pk_live_...
PLAIDLY_WEBHOOK_SECRET=whsec_...Create a checkout API route
// app/api/checkout/route.ts
import { PlaidlyClient } from '@plaidly/node';
import { NextRequest } from 'next/server';
const plaidly = new PlaidlyClient({
apiKey: process.env.PLAIDLY_API_KEY!,
});
export async function POST(req: NextRequest) {
const { amount, currency, chain, orderId } = await req.json();
const session = await plaidly.sessions.create({
amount,
currency,
chain,
network: 'mainnet',
callbackUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/api/webhook/plaidly`,
metadata: { orderId },
});
return Response.json({
sessionId: session.id,
walletAddress: session.walletAddress,
expiresAt: session.expiresAt,
});
}Webhook handler
// app/api/webhook/plaidly/route.ts
import { verifyWebhookSignature } from '@plaidly/node/webhooks';
import { NextRequest } from 'next/server';
export async function POST(req: NextRequest) {
const body = await req.text();
const sig = req.headers.get('x-plaidly-signature') ?? '';
let event;
try {
event = verifyWebhookSignature(
body,
sig,
process.env.PLAIDLY_WEBHOOK_SECRET!,
);
} catch {
return new Response('Invalid signature', { status: 400 });
}
if (event.event === 'payment.confirmed') {
const { orderId } = event.metadata;
await fulfillOrder(orderId);
}
return new Response('OK');
}
async function fulfillOrder(orderId: string) {
// Your fulfillment logic here
}Poll session status (client component)
// components/PaymentStatus.tsx
'use client';
import { useEffect, useState } from 'react';
interface Props {
sessionId: string;
}
export function PaymentStatus({ sessionId }: Props) {
const [status, setStatus] = useState<string>('pending');
useEffect(() => {
const interval = setInterval(async () => {
const res = await fetch(`/api/sessions/${sessionId}`);
const data = await res.json();
setStatus(data.status);
if (data.status === 'confirmed' || data.status === 'expired') {
clearInterval(interval);
}
}, 3000);
return () => clearInterval(interval);
}, [sessionId]);
return <div>Payment status: {status}</div>;
}Session status API route
// app/api/sessions/[id]/route.ts
import { PlaidlyClient } from '@plaidly/node';
import { NextRequest } from 'next/server';
const plaidly = new PlaidlyClient({
apiKey: process.env.PLAIDLY_API_KEY!,
});
export async function GET(
_req: NextRequest,
{ params }: { params: Promise<{ id: string }> },
) {
const { id } = await params;
const session = await plaidly.sessions.get(id);
return Response.json({
status: session.status,
amount: session.amount,
currency: session.currency,
});
}Pages Router
If you are using the Pages Router instead of the App Router:
// pages/api/checkout.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import { PlaidlyClient } from '@plaidly/node';
const plaidly = new PlaidlyClient({
apiKey: process.env.PLAIDLY_API_KEY!,
});
export default async function handler(
req: NextApiRequest,
res: NextApiResponse,
) {
if (req.method !== 'POST') {
return res.status(405).end();
}
const { amount, currency, chain, orderId } = req.body;
const session = await plaidly.sessions.create({
amount,
currency,
chain,
network: 'mainnet',
metadata: { orderId },
});
res.json({ sessionId: session.id, walletAddress: session.walletAddress });
}Sandbox testing
Set network: 'testnet' and use the sandbox simulate endpoint to trigger instant confirmations without real transactions:
// Trigger a simulated confirmation in sandbox
await plaidly.sandbox.simulate(session.id);