Project Setup
// .env.local
PAYCHAINLY_API_KEY=pk_live_...
PAYCHAINLY_WEBHOOK_SECRET=whsec_...
PAYCHAINLY_API_URL=https://api.paychainly.com
Server Action: Create Payment Link
// app/actions/payment.ts
'use server';
export async function createPaymentLink(formData: FormData) {
const amount = parseFloat(formData.get('amount') as string);
const res = await fetch(`${process.env.PAYCHAINLY_API_URL}/api/v1/payment-links`, {
method: 'POST',
headers: {
'x-api-key': process.env.PAYCHAINLY_API_KEY!,
'Content-Type': 'application/json',
},
body: JSON.stringify({ amount, description: 'Payment from Next.js' }),
});
const { data } = await res.json();
redirect(data.payUrl);
}
API Route: Webhook Handler
// app/api/webhooks/paychainly/route.ts
import crypto from 'crypto';
import { NextRequest, NextResponse } from 'next/server';
export async function POST(req: NextRequest) {
const rawBody = await req.text();
const sig = req.headers.get('x-paychainly-signature') ?? '';
const expected = 'sha256=' + crypto
.createHmac('sha256', process.env.PAYCHAINLY_WEBHOOK_SECRET!)
.update(rawBody)
.digest('hex');
if (!crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(sig))) {
return NextResponse.json({ error: 'Invalid signature' }, { status: 401 });
}
const event = JSON.parse(rawBody);
// Process: credit user, update order, send email...
return NextResponse.json({ received: true });
}
Checkout Form Component
// app/checkout/page.tsx
import { createPaymentLink } from '@/app/actions/payment';
export default function CheckoutPage() {
return (
<form action={createPaymentLink}>
<input name="amount" type="number" step="0.01" placeholder="Amount in USDT" />
<button type="submit">Pay with USDT</button>
</form>
);
}
Edge Runtime Compatibility
The webhook route can run on the Edge Runtime since it only uses the Web Crypto API (no Node.js buffers). Change runtime to 'edge' for lower latency globally.