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 Group Contribution App
Bonaventure OgetoBy Bonaventure Ogeto|Updated July 22, 2026

Build a Group Contribution App

Build a group contribution app by creating contribution cycles (monthly), sending each member a Paystack payment link for their fixed contribution, tracking payments per member per cycle, and disbursing the pooled amount to the designated member when all (or a quorum) have contributed. Use webhooks to track each member's payment as it arrives.

In this article

  1. 1. Group Setup and Contribution Cycles
  2. 2. Contribution Tracking and Cycle Payout
  3. 3. Learn More

Group Setup and Contribution Cycles

Tables: groups (name, contribution_amount, cycle_frequency, payout_order[], created_by), members (group_id, user_id, name, email, phone, bank_account, paystack_recipient_code, payout_position), cycles (group_id, cycle_number, month, year, recipient_member_id, status, total_collected), contributions (cycle_id, member_id, amount, paystack_ref, paid_at).

async function startNewCycle(groupId) {
  var group = await db.groups.findById(groupId);
  var lastCycle = await db.cycles.findLatest(groupId);
  var cycleNumber = (lastCycle?.cycle_number || 0) + 1;
  var recipientIndex = (cycleNumber - 1) % group.payout_order.length;
  var recipientMemberId = group.payout_order[recipientIndex];

  var now = new Date();
  var cycle = await db.cycles.create({
    group_id: groupId,
    cycle_number: cycleNumber,
    month: now.getMonth() + 1,
    year: now.getFullYear(),
    recipient_member_id: recipientMemberId,
    status: 'open',
    total_collected: 0,
  });

  // Send payment links to all members
  var members = await db.members.findByGroup(groupId);
  for (var member of members) {
    var reference = 'CHAMA-' + cycle.id + '-' + member.id + '-' + Date.now();
    await db.contributions.create({ cycle_id: cycle.id, member_id: member.id, paystack_ref: reference, status: 'pending' });

    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: member.email,
        amount: group.contribution_amount,
        currency: 'NGN',
        reference,
        metadata: { cycle_id: cycle.id, member_id: member.id, group_id: groupId },
      }),
    });
    var link = (await res.json()).data.authorization_url;
    await sendContributionRequest(member.phone, link, group.name, group.contribution_amount / 100);
  }
  return cycle;
}

Contribution Tracking and Cycle Payout

if (event.event === 'charge.success') {
  var meta = event.data.metadata;
  var contribution = await db.contributions.findByRef(event.data.reference);
  await db.contributions.update(contribution.id, { status: 'paid', paid_at: new Date() });
  await db.cycles.increment(meta.cycle_id, 'total_collected', event.data.amount);

  // Check if cycle is fully funded or deadline passed
  var cycle = await db.cycles.findById(meta.cycle_id);
  var group = await db.groups.findById(meta.group_id);
  var members = await db.members.findByGroup(meta.group_id);
  var paidCount = await db.contributions.countPaid(meta.cycle_id);

  if (paidCount >= members.length) {
    await disburseCycle(cycle, group);
  }
}

async function disburseCycle(cycle, group) {
  var recipient = await db.members.findById(cycle.recipient_member_id);

  // Ensure recipient has a Paystack transfer recipient code
  if (!recipient.paystack_recipient_code) {
    var res = 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: recipient.name, account_number: recipient.account_number, bank_code: recipient.bank_code, currency: 'NGN' }),
    });
    var code = (await res.json()).data.recipient_code;
    await db.members.update(recipient.id, { paystack_recipient_code: code });
    recipient.paystack_recipient_code = code;
  }

  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: cycle.total_collected,
      recipient: recipient.paystack_recipient_code,
      reason: group.name + ' — Cycle ' + cycle.cycle_number + ' payout',
    }),
  });

  await db.cycles.update(cycle.id, { status: 'disbursed' });
  await notifyAllMembers(group.id, recipient.name, cycle.total_collected);
}

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 bill splitting app for the simpler per-person payment link pattern without the rotation payout structure.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Generate a unique Paystack payment link per member per cycle — track payments individually.
  • ✓Allow partial cycle funding: proceed if 80% of members have contributed by the deadline.
  • ✓Record payout rotation order at group creation time to avoid disputes.
  • ✓Disburse pooled funds via Paystack Transfers to the cycle's designated recipient.
  • ✓Maintain an immutable ledger of all contributions and disbursements for member transparency.

Frequently Asked Questions

What happens if a member fails to contribute in a cycle?
Define a grace period (e.g., 3 days after the contribution deadline). If a member has not paid by the deadline, send a final reminder. After the grace period, mark their contribution as "defaulted." The group admin decides whether to proceed with the payout using the collected amount or delay until the defaulter pays. Track defaulters and implement a penalty policy (extra contribution next cycle or removal from the group).
Is this system compliant with SACCO regulations in Kenya?
Informal chama groups (friends, family) do not require SACCO registration. Formal SACCOs must register with the SACCO Societies Regulatory Authority (SASRA) in Kenya and comply with SACCO Societies Act requirements. This app is suitable for informal chamas but should not be used for formal SACCO operations without proper legal structuring. Consult a financial regulatory lawyer for formal SACCO operations.
Can members join mid-cycle?
It is cleaner to allow new members only at the start of a new cycle. Mid-cycle joins require prorating contributions and adjusting the payout pool, which complicates accounting. Set a clear rule at group creation: no new members mid-cycle. New members who join before the current cycle starts are added to the payout rotation for the next cycle.

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 Digital Content Paywall for Kenyan Readers

Kenyan publishers, bloggers, and content creators have an audience but struggle to monetize beyond ads. International paywall tools assume credit cards and monthly billing, both of which miss how Kenyans actually pay. This guide shows you how to build a paywall that accepts M-Pesa micropayments, offers daily and weekly plans alongside monthly, lets readers buy single articles, and manages free tiers to convert casual readers into paying subscribers.

Read more

Subaccounts on Paystack: Complete Guide

Everything you need to know about Paystack subaccounts: creating them via the API, validating bank details before creation, setting default split ratios, updating bank details when vendors change banks, listing and filtering subaccounts, and managing them at scale in a production marketplace.

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.