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. Unit Testing Paystack Integrations
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Unit Testing Paystack Integrations

Unit test three main areas in a Paystack integration: (1) webhook signature validation — does your HMAC check correctly accept valid signatures and reject invalid ones? (2) business logic — does your order fulfillment function correctly update the DB given a successful charge event? (3) idempotency — does your handler skip processing if the reference was already processed? Mock the Paystack API and the database in these tests.

In this article

  1. 1. What to Unit Test in a Paystack Integration
  2. 2. Unit Test Examples in Jest
  3. 3. Learn More

What to Unit Test in a Paystack Integration

Three areas worth unit testing:

  1. Webhook signature validation — the HMAC SHA512 check that prevents fake webhooks
  2. Event handling logic — what happens when charge.success fires (fulfill), charge.failed fires (mark failed), subscription.disable fires (cancel)
  3. Idempotency — duplicate events for the same reference must not cause double fulfillment

What you do not need to unit test: Paystack's own API behavior, the correctness of the transaction data Paystack returns, or network-level reliability.

Unit Test Examples in Jest

// webhook-handler.test.js
const crypto = require('crypto');
const { handleWebhook } = require('./webhook-handler');

const SECRET = 'test_secret_key';

function makeSignature(body) {
  return crypto.createHmac('sha512', SECRET).update(body).digest('hex');
}

describe('Webhook signature validation', () => {
  it('accepts a request with valid signature', async () => {
    var body = JSON.stringify({ event: 'charge.success', data: { reference: 'ref_001', amount: 100000, status: 'success', metadata: { order_id: '123' } } });
    var sig = makeSignature(body);
    var result = await handleWebhook(body, sig, SECRET);
    expect(result.status).toBe(200);
  });

  it('rejects a request with wrong signature', async () => {
    var body = JSON.stringify({ event: 'charge.success', data: {} });
    var result = await handleWebhook(body, 'wrong_sig', SECRET);
    expect(result.status).toBe(401);
  });
});

describe('Charge fulfillment', () => {
  it('fulfills order on charge.success', async () => {
    var mockDb = { orders: { update: jest.fn() } };
    var event = { event: 'charge.success', data: { reference: 'ref_002', amount: 100000, status: 'success', metadata: { order_id: '456' } } };
    await fulfillOrder(event.data, mockDb);
    expect(mockDb.orders.update).toHaveBeenCalledWith({ id: '456', status: 'paid', paystack_ref: 'ref_002' });
  });

  it('does not fulfill on charge.failed', async () => {
    var mockDb = { orders: { update: jest.fn() } };
    var event = { event: 'charge.failed', data: { reference: 'ref_003', metadata: { order_id: '789' } } };
    await handleFailedCharge(event.data, mockDb);
    expect(mockDb.orders.update).toHaveBeenCalledWith({ id: '789', status: 'payment_failed' });
    // Crucially: fulfillOrder should NOT have been called
  });
});

describe('Idempotency', () => {
  it('skips fulfillment if reference already processed', async () => {
    var mockDb = {
      webhookEvents: { findOne: jest.fn().mockResolvedValue({ id: 1 }), create: jest.fn() },
      orders: { update: jest.fn() },
    };
    var event = { event: 'charge.success', data: { reference: 'ref_001', metadata: { order_id: '123' } } };
    await handleWithIdempotency(event, mockDb);
    expect(mockDb.orders.update).not.toHaveBeenCalled();
  });
});

Get weekly developer tips

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

No spam. Unsubscribe anytime.

Learn More

This guide is part of the Paystack testing guide.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Unit tests for Paystack code test your logic, not Paystack's API — mock all outbound API calls.
  • ✓Test webhook signature validation with both valid and invalid signatures — the invalid case is as important as the valid one.
  • ✓Test the fulfillment logic by calling your handler function directly with a mock charge.success event object.
  • ✓Test idempotency: call your handler twice with the same reference — the second call should be a no-op.
  • ✓Test the failure path: call your handler with a charge.failed event — ensure orders are not fulfilled.
  • ✓Keep tests fast: no network calls, no database writes in unit tests — use in-memory mocks for everything.

Frequently Asked Questions

Should I test the Paystack API response format in unit tests?
No — you should trust that Paystack returns the format documented in their API reference. Unit tests verify that your code handles those responses correctly. If Paystack changes their API format, your integration tests (which call the real test API) will catch that.
How do I test webhook handling without a running HTTP server?
Extract the business logic out of the HTTP handler into a pure function that takes the event object. Test that function directly — no HTTP server needed. The HTTP handler just validates the signature and calls the function. This makes unit testing much simpler.
What test data should I use for Paystack event mocks?
Use realistic but fake data: actual reference format (alphanumeric, 12+ chars), amounts in kobo, realistic email addresses. Copy the structure of a real Paystack webhook event from the documentation. The important thing is that the data structure matches what Paystack actually sends.
How many unit tests should a typical Paystack webhook handler have?
At minimum: 1 test for valid signature, 1 for invalid signature, 1 for each event type you handle (charge.success, charge.failed, subscription.disable, etc.), and 1 idempotency test. That is typically 5-10 tests for a standard integration. More edge cases (missing metadata, unexpected event types) add value.

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

Testing Paystack Integrations: The Complete Guide

A complete guide to testing Paystack integrations at every level: test mode, test cards, unit tests with mocks, integration tests, end-to-end flows with Playwright, webhook simulation, contract testing, CI pipelines, staging environments, load testing, and coverage targets for payment code.

Read more

Simulating Declined Cards on Paystack

A card decline is one of the most common failure states in a payment flow, and your application must handle it gracefully. This guide shows how to simulate specific card declines in Paystack test mode and how to verify your decline handling code works correctly.

Read more

Integration Testing Paystack Flows End to End

Integration tests for Paystack test the real flow — your backend calls the actual Paystack test API, a webhook fires to your server, and your database is updated. Unlike unit tests with mocks, integration tests catch issues that only appear when the real API is involved.

Read more

Seeding Test Data for a Payments Feature

Good test data is essential for testing payment features. This guide covers creating Paystack test mode resources (plans, subaccounts, split codes) and seeding your application database with matching payment records for realistic test scenarios.

Read more

Paystack Webhook Returning 403 in Django

Django returns 403 Forbidden when a POST request does not include a valid CSRF token. Paystack webhooks are server-to-server requests that cannot include CSRF tokens. The fix is the @csrf_exempt decorator on your webhook view. This guide covers both plain Django views and Django REST Framework.

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.