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 a Microfinance Repayment Portal
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build a Microfinance Repayment Portal

Build a microfinance repayment portal by disbursing loans via the Paystack Transfers API to borrowers' bank accounts, generating a repayment schedule on disbursement, sending a Paystack payment link for each repayment due, and optionally using card tokenization (authorization_code from first repayment) for auto-debit on subsequent installments.

In this article

  1. 1. Loan Disbursement and Repayment Schedule
  2. 2. Repayment Collection and Overdue Handling
  3. 3. Learn More

Loan Disbursement and Repayment Schedule

Tables: borrowers (name, phone, id_number, bank_code, account_number, paystack_recipient_code, credit_score), loans (borrower_id, principal, interest_rate, term_weeks, weekly_installment, status, disbursed_at), repayments (loan_id, installment_number, due_date, amount_due, amount_paid, status, paystack_ref, paid_at).

function calculateInstallment(principal, interestRate, termWeeks) {
  var totalInterest = principal * interestRate;
  var total = principal + totalInterest;
  return Math.ceil(total / termWeeks);
}

async function disburseLoan(loanId) {
  var loan = await db.loans.findById(loanId);
  var borrower = await db.borrowers.findById(loan.borrower_id);

  // Ensure recipient code exists
  if (!borrower.paystack_recipient_code) {
    var rcpRes = await fetch('https://api.paystack.co/transferrecipient', {
      method: 'POST',
      headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
      body: JSON.stringify({ type: 'nuban', name: borrower.name, account_number: borrower.account_number, bank_code: borrower.bank_code, currency: 'NGN' }),
    });
    var code = (await rcpRes.json()).data.recipient_code;
    await db.borrowers.update(borrower.id, { paystack_recipient_code: code });
    borrower.paystack_recipient_code = code;
  }

  // Disburse
  await fetch('https://api.paystack.co/transfer', {
    method: 'POST',
    headers: { Authorization: 'Bearer ' + process.env.PAYSTACK_SECRET_KEY, 'Content-Type': 'application/json' },
    body: JSON.stringify({ source: 'balance', amount: loan.principal, recipient: borrower.paystack_recipient_code, reason: 'Loan disbursement #' + loanId }),
  });
  await db.loans.update(loanId, { status: 'disbursed', disbursed_at: new Date() });

  // Generate repayment schedule
  var installment = calculateInstallment(loan.principal, loan.interest_rate, loan.term_weeks);
  for (var i = 1; i <= loan.term_weeks; i++) {
    var dueDate = new Date();
    dueDate.setDate(dueDate.getDate() + (i * 7));
    var reference = 'REPAY-' + loanId + '-' + i;
    await db.repayments.create({ loan_id: loanId, installment_number: i, due_date: dueDate, amount_due: installment, status: 'pending', paystack_ref: reference });
  }
  await sendDisbursementNotification(borrower.phone, loan.principal / 100, loan.term_weeks);
}

Repayment Collection and Overdue Handling

// Send payment link for each installment due
async function sendRepaymentLink(borrowerEmail, repaymentId) {
  var repayment = await db.repayments.findById(repaymentId);
  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: borrowerEmail,
      amount: repayment.amount_due,
      currency: 'NGN',
      reference: repayment.paystack_ref,
      metadata: { repayment_id: repaymentId, loan_id: repayment.loan_id, installment: repayment.installment_number },
    }),
  });
  return (await res.json()).data.authorization_url;
}

// Webhook: record repayment
if (event.event === 'charge.success') {
  var meta = event.data.metadata;
  if (!meta.repayment_id) return res.sendStatus(200);

  await db.repayments.update(meta.repayment_id, {
    status: 'paid',
    amount_paid: event.data.amount,
    paid_at: new Date(),
  });

  // Check if loan is fully repaid
  var pendingCount = await db.repayments.countPending(meta.loan_id);
  if (pendingCount === 0) {
    await db.loans.update(meta.loan_id, { status: 'completed' });
    await notifyBorrowerLoanComplete(meta.loan_id);
  }
}

// Cron: flag overdue repayments and apply late penalty
async function processOverdueRepayments() {
  var graceDeadline = new Date();
  graceDeadline.setDate(graceDeadline.getDate() - 3); // 3-day grace

  var overdue = await db.repayments.findAll({ status: 'pending', due_date_lt: graceDeadline });
  var LATE_PENALTY_RATE = 0.02; // 2% of installment per week overdue

  for (var repayment of overdue) {
    var weeksOverdue = Math.floor((new Date() - new Date(repayment.due_date)) / (7 * 24 * 60 * 60 * 1000));
    var penalty = Math.floor(repayment.amount_due * LATE_PENALTY_RATE * weeksOverdue);
    await db.repayments.update(repayment.id, { status: 'overdue', late_penalty: penalty });
    await notifyBorrowerOverdue(repayment.loan_id, repayment.installment_number, penalty / 100);
  }
}

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 savings goal app with scheduled debits for card tokenization auto-debit — the same pattern used for recurring loan repayments.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Disburse loans via Paystack Transfers — borrowers receive funds directly to their bank or M-Pesa.
  • ✓Generate a repayment schedule at disbursement time: amount, due date, and balance per installment.
  • ✓Use card tokenization (first repayment) to auto-debit subsequent installments without manual action.
  • ✓Track overdue installments and apply penalty rates after a 3-day grace period.
  • ✓Aggregate collection stats per loan officer or group for portfolio health monitoring.

Frequently Asked Questions

Do I need a lending licence to operate a microfinance repayment portal?
A repayment collection portal that processes payments on behalf of a licensed microfinance institution (MFI) is a technology service, not a lending activity. The MFI holds the lending licence and takes the credit risk. Your platform facilitates collection. If you are also underwriting and managing the loan risk yourself, you need a money lending licence from CBN (Nigeria) or CBK (Kenya).
How do I handle group loans (solidarity groups)?
For group loans, link multiple borrowers to a single loan. Each group member shares equal liability. Send one repayment link to the group leader who collects from members and makes a single payment. Alternatively, split the loan into sub-loans per member and track each member's repayment individually. The group model simplifies collection but complicates defaults — one member's default affects the whole group.
Can I use card auto-debit for repayments instead of sending payment links?
Yes. At the first repayment, use a standard Paystack checkout. Save the authorization_code from the charge.success webhook. For subsequent installments, use POST /charge/authorization with the saved code — no need for the borrower to take any action. Send an SMS notification before each auto-debit so the borrower knows to maintain sufficient funds. If a debit fails, send the manual payment link as a fallback.

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 Veterinary Clinic Booking and Payment System

A build guide for a veterinary clinic booking and payment system where pet owners book appointments online, pay a deposit via Paystack to confirm the slot, and pay the balance at the clinic after the consultation. Includes pet health records, vaccination schedules, and automated reminders.

Read more

Paystack Kenya Settlement: To Bank or M-Pesa Wallet

Everything Kenyan Paystack merchants need to know about settlement. Covers bank account settlement versus M-Pesa wallet settlement, how to configure each, settlement timelines, net settlement after fees, handling multiple destinations, and when to choose one over the other.

Read more

Splitting Payments for SACCOs and Chamas

SACCOs and chamas collect contributions from members and pool them for loans, welfare, and investments. This guide covers using Paystack to collect member contributions, split across welfare and savings subaccounts, and execute bulk dividend payouts to members.

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.