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. Build an Invoicing and Payment Reminder Tool
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build an Invoicing and Payment Reminder Tool

Build an invoicing tool by creating invoice records, generating a Paystack transaction link for each invoice, and marking the invoice as paid when the charge.success webhook fires with the matching reference. A daily cron job checks overdue invoices and sends reminder emails at defined intervals.

In this article

  1. 1. Invoice Creation and Payment Link
  2. 2. Overdue Detection and Automated Reminders
  3. 3. Learn More

Invoice Creation and Payment Link

Tables: invoices (issuer_id, client_name, client_email, items[], subtotal, tax, total, currency, due_date, status, paystack_ref, paid_at), reminder_log (invoice_id, reminder_type, sent_at).

async function createInvoice(issuerId, clientEmail, clientName, items, dueDate) {
  var subtotal = items.reduce(function(sum, item) { return sum + (item.qty * item.unit_price); }, 0);
  var tax = Math.floor(subtotal * 0.075); // 7.5% VAT (Nigeria)
  var total = subtotal + tax;
  var reference = 'INV-' + Date.now();

  var invoice = await db.invoices.create({
    issuer_id: issuerId,
    client_name: clientName,
    client_email: clientEmail,
    items,
    subtotal,
    tax,
    total,
    due_date: dueDate,
    status: 'sent',
    paystack_ref: reference,
  });

  // Generate Paystack payment link
  var res = 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: clientEmail,
      amount: total,
      currency: 'NGN',
      reference,
      metadata: { invoice_id: invoice.id, issuer_id: issuerId },
    }),
  });
  var paymentUrl = (await res.json()).data.authorization_url;

  await sendInvoiceEmail(clientEmail, clientName, invoice, paymentUrl);
  return { invoice, paymentUrl };
}

// Webhook: mark invoice paid
if (event.event === 'charge.success') {
  var meta = event.data.metadata;
  if (meta.invoice_id) {
    await db.invoices.update(meta.invoice_id, { status: 'paid', paid_at: new Date() });
    await generateAndSendReceipt(meta.invoice_id, event.data.reference);
  }
}

Overdue Detection and Automated Reminders

// Cron: runs daily to detect overdue invoices and send reminders
async function processOverdueInvoices() {
  var today = new Date();
  var pendingInvoices = await db.invoices.findAll({ status: 'sent' });

  for (var invoice of pendingInvoices) {
    var daysOverdue = Math.floor((today - new Date(invoice.due_date)) / (1000 * 60 * 60 * 24));
    if (daysOverdue <= 0) continue; // not yet overdue

    var remindersAlreadySent = await db.reminderLog.findByInvoice(invoice.id);
    var sentTypes = remindersAlreadySent.map(function(r) { return r.reminder_type; });

    var toSend = null;
    if (daysOverdue >= 3 && !sentTypes.includes('3day'))  toSend = '3day';
    if (daysOverdue >= 7 && !sentTypes.includes('7day'))  toSend = '7day';
    if (daysOverdue >= 14 && !sentTypes.includes('14day')) toSend = '14day';

    if (toSend) {
      // Re-initialize payment link (same reference re-usable until paid)
      var paymentUrl = await getPaymentUrl(invoice.paystack_ref, invoice.client_email, invoice.total);
      await sendReminderEmail(invoice.client_email, invoice.client_name, invoice, paymentUrl, toSend);
      await db.reminderLog.create({ invoice_id: invoice.id, reminder_type: toSend, sent_at: new Date() });

      if (toSend === '14day') {
        await db.invoices.update(invoice.id, { status: 'overdue' });
        await notifyIssuer(invoice.issuer_id, 'Invoice #' + invoice.id + ' is 14 days overdue.');
      }
    }
  }
}

// Mark invoice as void if client and issuer agree it should not be paid
async function voidInvoice(invoiceId) {
  var invoice = await db.invoices.findById(invoiceId);
  if (invoice.status === 'paid') throw new Error('Cannot void a paid invoice');
  await db.invoices.update(invoiceId, { status: 'void' });
}

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

See build a freelance escrow style platform for a related tool that holds payment until work is approved.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓One Paystack payment link per invoice — embed it in the invoice email so clients can pay in one click.
  • ✓Mark invoices as paid via the charge.success webhook, not via manual reconciliation.
  • ✓Automated reminders at 3, 7, and 14 days overdue without manual follow-up.
  • ✓Support partial payments: track amount_paid vs invoice total.
  • ✓Generate a PDF receipt on payment confirmation for the client's records.

Frequently Asked Questions

Can I let clients pay in installments against one invoice?
Yes. Track amount_paid on the invoice. For each partial payment, initialize a new Paystack transaction for the remaining balance and store the reference. Update amount_paid in each charge.success webhook. Mark the invoice as paid when amount_paid equals the total. Show the client their outstanding balance in each reminder.
What currency should I use for invoices sent to international clients?
Paystack supports USD for Nigerian businesses with the right account type. For invoices to international clients, use USD, initialize the Paystack transaction with currency: "USD", and the client pays in USD. Settlement comes to you in NGN at the rate Paystack applies. Check current Paystack multicurrency documentation for your account type.
How do I handle a client who disputes the invoice amount?
Add a "Request adjustment" flow: the client clicks a link in the invoice email, explains the dispute, and you receive a notification. You can void the original invoice and create a revised one. Until the dispute is resolved, pause automated reminders for that invoice to avoid annoying an already-engaged client.

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

Things You Can Build with Paystack: Project Guides

A complete map of real-world projects you can build using the Paystack API. From online store checkouts and subscription platforms to savings apps and solar pay-as-you-go systems, each category links to step-by-step build guides with working code.

Read more

Build a Parking Payment App

A build guide for a parking payment app where drivers scan a QR code on entry, pay for their session via Paystack (card or M-Pesa), and the system validates payment before the barrier lifts for exit. Covers time-based pricing, session management, and overstay handling.

Read more

Build a Referral Payout System

A build guide for a referral payout system where users share referral links, earn commissions when referrals convert, and withdraw their balance via Paystack Transfers to their bank account or M-Pesa wallet.

Read more

Build a Farmers Market Ordering Platform

A build guide for a farmers market ordering platform where customers order fresh produce from multiple farmers in one cart, the platform collects the combined payment via Paystack, and each farmer receives their share via Paystack Split Payments or Transfers. Covers inventory management, order routing to individual farmers, and delivery coordination.

Read more

Building a Freelancer Invoicing Tool for Kenyan Developers

Kenyan freelance developers juggle M-Pesa payments from local clients and USD wire transfers from international ones, often tracking everything in a spreadsheet or WhatsApp. An invoicing tool that creates professional invoices, attaches Paystack payment links, handles multi-currency billing, tracks payment status, and generates receipts turns freelance chaos into a real business. This guide covers the complete build.

Read more

Percentage vs Flat Split Configuration on Paystack

A detailed comparison of percentage and flat split configurations on Paystack. Covers how each model works, worked margin calculations across different transaction sizes, when to use percentage vs flat, implementing per-transaction overrides, and building hybrid models that combine both approaches.

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.