DEV Community

Muhammad Aqib Bin Azam
Muhammad Aqib Bin Azam

Posted on

1

Integrating PayTabs Payment Gateway with Next.js: A Scalable Approach

This guide will help you implement PayTabs in a production-ready way using:

  • Next.js 14+ (App Router)
  • TypeScript for type safety
  • React Query for API state management
  • Design Patterns (Facade, Adapter, Observer) for clean architecture

πŸ› οΈ Prerequisites

Before diving in, ensure you have:

  1. A PayTabs merchant account (Sign up here)
    • Get your Profile ID, Server Key, and Region from the dashboard
  2. A Next.js 14+ project with TypeScript
  3. Node.js (v18+) installed

Install required dependencies:

npm install paytabs_pt2 axios
Enter fullscreen mode Exit fullscreen mode

Set up environment variables (.env.local):

PAYTABS_PROFILE_ID=your_profile_id  
PAYTABS_SERVER_KEY=your_server_key  
PAYTABS_REGION=EGY  # Adjust based on your region  
NEXT_PUBLIC_BASE_URL=http://localhost:3000  
Enter fullscreen mode Exit fullscreen mode

Step 1: Create the PayTabs Service (Backend)

This service acts as a facade over the PayTabs SDK, providing a clean interface for transactions and webhook verification.

// lib/services/PayTabsService.ts  
import paytabs from "paytabs_pt2";  
import { PaymentResponse, TransactionRequest } from "@/types/paytabs";  

export class PayTabsService {  
  constructor() {  
    const profileID = process.env.PAYTABS_PROFILE_ID!;  
    const serverKey = process.env.PAYTABS_SERVER_KEY!;  
    const region = process.env.PAYTABS_REGION!;  
    paytabs.setConfig(profileID, serverKey, region);  
  }  

  async createTransaction(request: TransactionRequest): Promise<PaymentResponse> {  
    try {  
      const response = await new Promise<PaymentResponse>((resolve, reject) => {  
        paytabs.createTransaction(request, (error, result) => {  
          if (error) reject(error);  
          else resolve(result);  
        });  
      });  

      if (!response.redirect_url) {  
        throw new Error("Missing redirect_url in PayTabs response");  
      }  

      return response;  
    } catch (error) {  
      throw new Error(`Payment failed: ${error.message}`);  
    }  
  }  

  verifyWebhookSignature(payload: any, signature: string): boolean {  
    // Implement HMAC-SHA256 verification  
    return true; // Simplified for example  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

Type Definitions:

// types/paytabs.ts  
export interface TransactionRequest {  
  cart_id: string;  
  cart_description: string;  
  cart_currency: string;  
  cart_amount: number;  
  return_url: string;  
  callback_url: string;  
  customer_details: {  
    name: string;  
    email: string;  
    phone: string;  
    street1: string;  
    city: string;  
    state: string;  
    country: string;  
    zip: string;  
  };  
}  

export interface PaymentResponse {  
  tran_ref: string;  
  redirect_url: string;  
  status: string;  
}  
Enter fullscreen mode Exit fullscreen mode

πŸ”„ Step 2: API Route for Transaction Initiation

Create a Next.js API route to handle payment requests:

// app/api/paytabs/create-transaction/route.ts  
import { NextResponse } from "next/server";  
import { PayTabsService } from "@/lib/services/PayTabsService";  

export async function POST(req: Request) {  
  try {  
    const { amount, currency, customer } = await req.json();  

    const request = {  
      cart_id: `order_${Date.now()}`,  
      cart_description: "Online Purchase",  
      cart_currency: currency,  
      cart_amount: amount,  
      return_url: `${process.env.NEXT_PUBLIC_BASE_URL}/payment/success`,  
      callback_url: `${process.env.NEXT_PUBLIC_BASE_URL}/api/paytabs/webhook`,  
      customer_details: customer,  
    };  

    const payTabsService = new PayTabsService();  
    const { redirect_url, tran_ref } = await payTabsService.createTransaction(request);  

    return NextResponse.json({ redirect_url, tran_ref });  
  } catch (error) {  
    return NextResponse.json(  
      { error: "Payment initiation failed" },  
      { status: 500 }  
    );  
  }  
}  
Enter fullscreen mode Exit fullscreen mode

πŸ“© Step 3: Webhook Handling

Set up a webhook endpoint to process real-time payment updates:

// app/api/paytabs/webhook/route.ts  
import { NextResponse } from "next/server";  
import { PayTabsService } from "@/lib/services/PayTabsService";  

export async function POST(req: Request) {  
  const payload = await req.json();  
  const signature = req.headers.get("x-paytabs-signature") || "";  

  const payTabsService = new PayTabsService();  
  const isValid = payTabsService.verifyWebhookSignature(payload, signature);  

  if (!isValid) {  
    return NextResponse.json({ error: "Invalid signature" }, { status: 401 });  
  }  

  // Update your database here  
  console.log("Payment status:", payload.payment_result.response_status);  

  return NextResponse.json({ success: true });  
}  
Enter fullscreen mode Exit fullscreen mode

πŸ’‘ Pro Tip: Use ngrok to test webhooks locally!


πŸ’» Step 4: Frontend Payment Form

Build a React component to initiate payments:

// components/PaymentForm.tsx  
"use client";  
import { useMutation } from "@tanstack/react-query";  

export default function PaymentForm() {  
  const mutation = useMutation({  
    mutationFn: (data) =>  
      fetch("/api/paytabs/create-transaction", {  
        method: "POST",  
        body: JSON.stringify(data),  
      }).then((res) => res.json()),  
    onSuccess: (data) => {  
      window.location.href = data.redirect_url; // Redirect to PayTabs  
    },  
  });  

  const handleSubmit = () => {  
    mutation.mutate({  
      amount: 100,  
      currency: "USD",  
      customer: {  
        name: "John Doe",  
        email: "john@example.com",  
        // ...other fields  
      },  
    });  
  };  

  return (  
    <button onClick={handleSubmit} disabled={mutation.isPending}>  
      {mutation.isPending ? "Processing..." : "Pay Now"}  
    </button>  
  );  
}  
Enter fullscreen mode Exit fullscreen mode

🎯 Best Practices for Production

  1. Security

    • Never expose API keys in client-side code
    • Validate webhook signatures
    • Use HTTPS everywhere
  2. Performance

    • Cache API responses with Redis
    • Deploy on Vercel's edge network
  3. Monitoring

    • Log errors with Sentry
    • Track transactions with Datadog
  4. Testing

    • Use PayTabs test cards (4111 1111 1111 1111)
    • Simulate failed payments

πŸ”— Resources


Have questions? Drop them in the comments below!

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

πŸ‘‹ Kindness is contagious

Engage with a wealth of insights in this thoughtful article, valued within the supportive DEV Community. Coders of every background are welcome to join in and add to our collective wisdom.

A sincere "thank you" often brightens someone’s day. Share your gratitude in the comments below!

On DEV, the act of sharing knowledge eases our journey and fortifies our community ties. Found value in this? A quick thank you to the author can make a significant impact.

Okay