Back to blogs

How to Integrate Dodo Payments with Next.js for International Payments

May 6, 2025
7 min read
How to Integrate Dodo Payments with Next.js for International Payments

Introduction

In today's global marketplace, facilitating international payments seamlessly is crucial for businesses. Dodo Payments offers a robust solution that simplifies the process of accepting payments from customers worldwide. By integrating Dodo Payments into your Next.js application, you can provide a smooth and secure checkout experience for your users.

This guide will walk you through the steps to integrate Dodo Payments using the official SDK, ensuring a streamlined payment process in your Next.js app.


Prerequisites


Before we begin, ensure you have the following:


  1. A Dodo Payments merchant account. If you don't have one, sign up at Dodo Payments.
  2. API credentials (API key and webhook secret) from your Dodo Payments dashboard.
  3. A Next.js project set up with TypeScript and Tailwind CSS.
  4. Basic knowledge of React and Next.js.


Step 1: Install the Dodo Payments SDK


First, install the official Dodo Payments SDK for Node.js:

npm install dodopayments


This SDK provides convenient access to the Dodo Payments API from your server-side code.


Step 2: Configure Environment Variables


Create a .env.local file in the root of your project and add your Dodo Payments API credentials:

DODO_API_KEY_TEST=your_api_key_here
DODO_PAYMENTS_WEBHOOK_KEY=your_webhook_key_here


Replace your_api_key_here and your_webhook_secret_here with the actual values from your Dodo Payments dashboard.


Step 3: Initialize the Dodo Payments Client


Create a utility file to initialize the Dodo Payments client using your API key:


// lib/dodoClient.ts
import DodoPayments from "dodopayments";
export const dodopayments = new DodoPayments({
bearerToken:
process.env.NODE_ENV === "development"
? process.env.DODO_API_KEY_TEST
: process.env.DODO_API_KEY_LIVE, // This is the default and can be omitted if env is named as DODO_PAYMENTS_API_KEY
environment:
process.env.NODE_ENV === "development" ? "test_mode" : "live_mode", // defaults to 'live_mode'
});


This client will be used to interact with the Dodo Payments API throughout your application.


Step 4: Create a Payment API Route


Set up an API route to create a new payment session:


// app/api/payments/create/route.ts

import { dodopayments } from "@/lib/dodopayments";
import { NextResponse } from "next/server";

export async function GET(request: Request) {
try {
const { searchParams } = new URL(request.url);
const productId = searchParams.get("productId");
const productWithQuantity = {product_id: productId as string, quantity: 1}

const response = await dodopayments.payments.create({
// GET BILLING, CUSTOMER INFO FROM CUSTOMER AND PASS IT.
// FOR COUNTRY CODE THE VALUE SHOULD BE - ISO country code alpha2 variant
billing: {
city: "",
country: "",
state: "",
street: "",
zipcode: "",
},
customer: {
email: "",
name: "",
},
payment_link: true,
product_cart: [productWithQuantity],
return_url: process.env.NEXT_PUBLIC_BASE_URL,
});
return NextResponse.json(response);
} catch (error) {
console.error(error);
return NextResponse.json(
{ error: "Failed to fetch products" },
{ status: 500 }
);
}
}


This route handles the creation of a payment session and returns the payment URL to the frontend.


Step 5: Implement the Payment Button on the Frontend


Create a React component that initiates the payment process:


//components/ProductCard.tsx

'"use client";
import { useState } from "react";
import { useRouter } from "next/navigation";

type Product = {
product_id: number;
name: string;
description: string;
price: number;
is_recurring: boolean;
};

export default function ProductCard({ product }: { product: Product }) {
const [loading, setLoading] = useState(false);
const router = useRouter();

const checkoutProduct = async (productId: number, is_recurring: boolean, useDynamicPaymentLinks: boolean) => {
if (useDynamicPaymentLinks) {
setLoading(true);
let productType = "onetime"
if (is_recurring) {
productType = "subscription"
}
const response = await fetch(`${process.env.NEXT_PUBLIC_BASE_URL}/api/checkout/${productType}?productId=${productId}`, {
cache: "no-store",
});
const data = await response.json();
router.push(data.payment_link)
} else {
let checkoutUrl = `https://test.checkout.dodopayments.com/buy/${productId}?quantity=1&redirect_url=${process.env.NEXT_PUBLIC_BASE_URL}`
router.push(checkoutUrl)
}

};

return (
<div className="bg-white border border-gray-200 rounded-lg shadow-lg p-6 hover:transform hover:scale-105 hover:shadow-xl transition-all duration-300">
<h2 className="text-xl font-bold text-black">{product.name}</h2>
<p className="text-gray-700 mt-2">{product.description}</p>
<p className="text-green-600 font-semibold mt-4">${product.price / 100}</p>
<button
className="text-xl font-bold text-black"
onClick={() => checkoutProduct(product.product_id, product.is_recurring, false)}
disabled={loading}
>
{loading ? "Processing..." : "Buy now"}
</button>
</div>
);
}


This component sends a request to the payment API route and redirects the user to the Dodo Payments checkout page.


Step 6: Set Up the Webhook Endpoint


To handle payment status updates, set up a webhook endpoint:


//app/api/webhooks/dodo/route.ts

import { Webhook } from "standardwebhooks";
import { headers } from "next/headers";
import { dodopayments } from "@/lib/dodopayments";


const webhook = new Webhook(process.env.DODO_PAYMENTS_WEBHOOK_KEY!);


export async function POST(request: Request) {
const headersList = await headers();

try {
const rawBody = await request.text();
const webhookHeaders = {
"webhook-id": headersList.get("webhook-id") || "",
"webhook-signature": headersList.get("webhook-signature") || "",
"webhook-timestamp": headersList.get("webhook-timestamp") || "",
};
await webhook.verify(rawBody, webhookHeaders);
const payload = JSON.parse(rawBody);

if (payload.data.payload_type === "Subscription") {
switch (payload.type) {
case "subscription.active":
const subscription = await dodopayments.subscriptions.retrieve(payload.data.subscription_id);
console.log("-------SUBSCRIPTION DATA START ---------")
console.log(subscription)
console.log("-------SUBSCRIPTION DATA END ---------")
break;
case "subscription.failed":
break;
case "subscription.cancelled":
break;
case "subscription.renewed":
break;
case "subscription.on_hold":
break
default:
break;
}
} else if (payload.data.payload_type === "Payment") {
switch (payload.type) {
case "payment.succeeded":
const paymentDataResp = await dodopayments.payments.retrieve(payload.data.payment_id)
console.log("-------PAYMENT DATA START ---------")
console.log(paymentDataResp)
console.log("-------PAYMENT DATA END ---------")
break;
default:
break;
}
}
return Response.json(
{ message: "Webhook processed successfully" },
{ status: 200 }
);
} catch (error) {
console.log(" ----- webhoook verification failed -----")
console.log(error)
return Response.json(
{ message: "Webhook processed successfully" },
{ status: 200 }
);
}
}


Ensure you configure the webhook URL in your Dodo Payments dashboard to point to this endpoint.


Step 7: Test the Integration


Before going live, test the payment flow:


  1. Use Dodo Payments' test mode and test card numbers.
  2. Complete a payment and verify that:
  3. The user is redirected to the success URL.
  4. The webhook endpoint receives the payment success event.
  5. Your database is updated accordingly.


Conclusion


Integrating Dodo Payments into your Next.js application allows you to handle international payments efficiently. By following this guide, you've set up:


  1. A secure payment initiation flow.
  2. A responsive frontend payment button.
  3. A webhook endpoint to handle payment confirmations.


Always ensure to handle errors gracefully and secure your endpoints to protect sensitive data.


Key Takeaways


  1. Dodo Payments SDK simplifies the integration process with its official SDK.
  2. Next.js API routes provide a seamless way to handle server-side operations.
  3. Webhooks are essential for real-time payment status updates.
  4. Always test thoroughly in a development environment before going live.


For more details, refer to the Dodo Payments Documentation.


Feel free to reach out if you have any questions or need further assistance with the integration!

Dodo Payments integrationNext.js international paymentsDodo Payments SDKpayment gateway Next.jsTypeScript payment integrationTailwind CSS paymentsDodo webhook setupsecure payments Next.jsNext.js API routesinternational checkout Next.jsDodo Payments tutorial.