Accept DERO Payments in Next.js
A complete guide for integrating DeroPay into a Next.js 15 App Router project — from installation to webhook handling.
What You'll Build
- API routes for creating invoices and checking status
- A payment page with QR code and "Pay with DERO" button
- A webhook endpoint for fulfilling orders
- Server-side invoice creation from checkout
Install Dependencies
bun add dero-paySet Environment Variables
# .env.local
DERO_WALLET_RPC=http://127.0.0.1:10103/json_rpc
DERO_DAEMON_RPC=http://127.0.0.1:10102/json_rpc
WEBHOOK_SECRET=your-secret-here
NEXT_PUBLIC_APP_URL=http://localhost:3000Create the Payment Engine
// lib/deropay.ts
import { createPaymentHandlers } from "dero-pay/next";
export const {
createInvoiceHandler,
statusHandler,
webhookHandler,
engine,
} = createPaymentHandlers({
walletRpcUrl: process.env.DERO_WALLET_RPC!,
daemonRpcUrl: process.env.DERO_DAEMON_RPC!,
webhookUrl: `${process.env.NEXT_PUBLIC_APP_URL}/api/webhooks/dero`,
webhookSecret: process.env.WEBHOOK_SECRET!,
});Create API Routes
// app/api/pay/create/route.ts
import { createInvoiceHandler } from "@/lib/deropay";
export const POST = createInvoiceHandler;// app/api/pay/status/route.ts
import { statusHandler } from "@/lib/deropay";
export const GET = statusHandler;// app/api/webhooks/dero/route.ts
import { verifyWebhookSignature } from "dero-pay/server";
export async function POST(request: Request) {
const body = await request.text();
const signature = request.headers.get("X-DeroPay-Signature")!;
if (!verifyWebhookSignature(body, signature, process.env.WEBHOOK_SECRET!)) {
return new Response("Invalid signature", { status: 401 });
}
const event = JSON.parse(body);
if (event.event === "invoice.completed") {
// Fulfill the order using event.metadata.orderId
console.log("Payment completed:", event.invoiceId);
}
return new Response("OK");
}Create the Payment Page
// app/pay/[invoiceId]/page.tsx
"use client";
import { DeroPayProvider, InvoiceView, PayWithDero } from "dero-pay/react";
import { useRouter } from "next/navigation";
export default function PayPage({
params,
}: {
params: { invoiceId: string };
}) {
const router = useRouter();
return (
<DeroPayProvider appName="My Store" statusEndpoint="/api/pay/status">
<div className="max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold mb-6">Complete Payment</h1>
<InvoiceView
invoiceId={params.invoiceId}
onCompleted={() => router.push("/thank-you")}
/>
<div className="mt-4">
<PayWithDero invoiceId={params.invoiceId} />
</div>
</div>
</DeroPayProvider>
);
}Create the Checkout Flow
// app/checkout/page.tsx
import { engine } from "@/lib/deropay";
import { deroToAtomic } from "dero-pay";
import { redirect } from "next/navigation";
export default function CheckoutPage() {
async function checkout(formData: FormData) {
"use server";
const invoice = await engine.createInvoice({
name: "Premium Plan",
description: "Monthly subscription",
amount: deroToAtomic("10.0"), // 10 DERO
metadata: { orderId: "ORD-" + Date.now() },
});
redirect(`/pay/${invoice.id}`);
}
return (
<div className="max-w-md mx-auto p-8">
<h1 className="text-2xl font-bold mb-4">Checkout</h1>
<p className="mb-6">Premium Plan — 10 DERO/month</p>
<form action={checkout}>
<button
type="submit"
className="w-full bg-green-600 text-white py-3 rounded-lg font-medium"
>
Pay with DERO
</button>
</form>
</div>
);
}Make sure your DERO wallet and daemon are running before starting the dev server. See Prerequisites for setup instructions.