McTaba Labs logo
Software EngineeringThe WhiteboardProjectsEventsMasterclassesWebinarsLearn
Book Call

500+ developers already in the community

Get answers, find accountability partners, and stay ahead

Join Discord →
McTaba Labs logo

Educating 1 million students in tech by 2030.

Programs

  • HighSchool Graduates Launchpad
  • AI & Business Automation
  • Corporate Training
  • McTaba Story & Mission
  • Full-Stack Curriculum
  • Course Pricing & Plans
  • Frequently Asked Questions

Resources

  • Join Discord Community
  • Tech Events in Nairobi
  • Masterclasses
  • Free Resources
  • Learn
  • Campus
  • Africa Tech Talent Report
  • Partner With Us
  • Start a Project
  • Our Work

Contact Us

  • 124 Manyani East Road, Lavington, Nairobi
  • +254727795603
  • Book a Discovery Call

Office Hours:

Mon-Fri: 9am - 2pm EAT

Sat: 10am - 12pm EAT

© 2027 McTaba Labs - A product of Edtech Industries. All rights reserved.

Privacy PolicyTerms of ServiceRefund Policy
  1. Learn
  2. Paystack for African Developers
  3. Paystack with a Headless CMS Checkout
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Paystack with a Headless CMS Checkout

In a headless CMS setup: 1) Fetch product data (price, name) from your CMS API (Sanity GROQ or Contentful REST/GraphQL). 2) Create a Next.js API route at /api/checkout that calls POST /transaction/initialize with the Paystack secret key server-side. 3) Redirect the customer to the Paystack authorization_url. 4) Handle charge.success webhook in /api/webhooks/paystack — verify HMAC and update your order DB.

In this article

  1. 1. Headless CMS + Paystack Architecture
  2. 2. Next.js API Route for Paystack Checkout
  3. 3. Webhook Handler in Next.js
  4. 4. Learn More

Headless CMS + Paystack Architecture

The flow: Next.js frontend fetches product data from CMS → customer clicks Buy → Next.js API route initializes Paystack with price from CMS → customer is redirected to Paystack checkout → on success, Paystack fires a webhook to your Next.js API route → you update your database and fulfill the order.

Key rule: always fetch the price from your CMS server-side before passing to Paystack. Never pass a client-supplied price to Paystack — a customer could modify the request to pay less.

Next.js API Route for Paystack Checkout

// pages/api/checkout.js (or app/api/checkout/route.js in App Router)
import { createClient } from '@sanity/client';

var sanity = createClient({ projectId: process.env.SANITY_PROJECT_ID, dataset: 'production', useCdn: true, apiVersion: '2024-01-01' });

export default async function handler(req, res) {
  if (req.method !== 'POST') return res.status(405).end();
  var { productId, email } = req.body;

  // Fetch price from CMS — never trust client-supplied price
  var product = await sanity.fetch('*[_id == $id][0]{ name, price }', { id: productId });
  if (!product) return res.status(404).json({ error: 'Product not found' });

  var reference = 'order_' + productId + '_' + Date.now();
  var response = await fetch('https://api.paystack.co/transaction/initialize', {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({
      email,
      amount: Math.round(product.price * 100), // NGN to kobo
      reference,
      metadata: { productId, productName: product.name },
      callback_url: process.env.NEXT_PUBLIC_BASE_URL + '/checkout/callback',
    }),
  });

  var data = await response.json();
  res.json({ url: data.data.authorization_url });
}

Get weekly developer tips

Join 25,000+ developers. Practical guides, job tips, and new content — straight to your inbox.

No spam. Unsubscribe anytime.

Webhook Handler in Next.js

// pages/api/webhooks/paystack.js
import crypto from 'crypto';

export const config = { api: { bodyParser: false } };

async function getRawBody(req) {
  return new Promise((resolve, reject) => {
    var chunks = [];
    req.on('data', (chunk) => chunks.push(chunk));
    req.on('end', () => resolve(Buffer.concat(chunks)));
    req.on('error', reject);
  });
}

export default async function handler(req, res) {
  var rawBody = await getRawBody(req);
  var signature = req.headers['x-paystack-signature'];
  var expected = crypto.createHmac('sha512', process.env.PAYSTACK_WEBHOOK_SECRET).update(rawBody).digest('hex');
  if (expected !== signature) return res.status(401).end();

  var event = JSON.parse(rawBody);
  if (event.event === 'charge.success') {
    var txn = event.data;
    var { productId } = txn.metadata;
    await db.orders.upsert({ reference: txn.reference }, { status: 'paid', productId, email: txn.customer.email });
  }
  res.sendStatus(200);
}

Learn More

See the Paystack integrations guide for platform-specific plugins.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Fetch product prices from Sanity/Contentful at runtime — never hardcode prices in frontend components.
  • ✓The Paystack initialize call must happen in a server-side API route, not in the browser.
  • ✓Pass the CMS product ID as Paystack metadata so your webhook knows what was purchased.
  • ✓Verify amount server-side before passing to Paystack — the CMS price is the source of truth.
  • ✓Webhooks work the same regardless of CMS — handle charge.success in your API route.

Frequently Asked Questions

Can I use Contentful instead of Sanity?
Yes. Replace the Sanity query with a Contentful REST API or GraphQL call to fetch the product price. The Paystack integration code is the same — only the CMS data fetch changes. The key principle is the same: fetch price server-side from CMS before initializing Paystack.
What if my headless CMS does not store prices?
If prices are in a separate system (e.g., a database or Airtable), fetch from there instead. The point is to not trust the client. Whatever is your source of truth for price, query it server-side in the API route before calling Paystack initialize.
How do I handle the Paystack redirect in a Next.js App Router setup?
In App Router, create app/api/checkout/route.ts with a POST handler that returns a Response.json() with the Paystack URL. On the client, use router.push(data.url) or window.location.href = data.url to redirect to Paystack checkout. Handle the callback in app/checkout/callback/page.tsx by calling your /api/verify?reference=... endpoint.

Learn Paystack Integration

KES 2,999

The definitive guide to building payment systems with Paystack — from your first transaction to production-grade payment infrastructure across Africa. 9 modules, 47 lessons. Free preview available.

Start Paystack Course

Not ready? Create Free Account

Keep reading

Paystack Plugins and No-Code Integrations

A complete map of every official and community Paystack plugin, no-code connector, and automation tool. Covers e-commerce platforms, website builders, headless commerce, automation workflows, reporting integrations, and the decision framework for choosing plugins over custom code.

Read more

Paystack with OpenCart

A practical guide to integrating Paystack with OpenCart for African e-commerce stores. Covers extension installation, configuration, webhook setup, testing, and the most common setup problems.

Read more

Paystack with Zapier

Zapier connects Paystack to 6,000+ apps without writing code. This guide shows how to set up Paystack webhook triggers in Zapier and build the most common payment automation workflows for African businesses.

Read more

Paystack Storefront for a Small Business

Paystack Storefront gives small businesses a free online shop without needing a website or developer. List your products, set prices, and share a link. Customers browse, pick, and pay — you get notified and fulfill the order.

Read more

Paystack Transaction Export and Reporting via API

The Paystack dashboard lets you download CSV reports manually. But when you need daily automated exports, custom financial reports, or reconciliation pipelines, you need the API. This guide covers listing transactions with filters, exporting data in bulk, and building reporting workflows that run without human intervention.

Read more

Explore related topics

M-Pesa & African Stack

M-Pesa Daraja API tutorials, USSD development, and WhatsApp Business API guides. The African Stack that global bootcamps do not teach.

Learn to Code in Kenya

Everything a Kenyan needs to know before writing their first line of code. Career paths, real costs, hardware requirements, and honest answers about what it takes.

Learning to Code

Roadmaps, language guides, and practical advice for learning to code and launching a programming career.

Breaking Into Tech

Practical, no-nonsense guides for anyone looking to start or change their career in tech. From choosing your first language to acing interviews.

The African Stack: Skills for This Market

The skills that make Kenyan developers hard to replace: M-Pesa, USSD, WhatsApp, fintech, and building for the African market.

Bonaventure Ogeto

Bonaventure Ogeto

Founder, McTaba Labs

Bonaventure is the founder of McTaba Labs, a coding bootcamp in Nairobi, Kenya. He has spent years building products for the African market and mentoring the next generation of developers across the continent.