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

Build a Bill Splitting App

Build a bill splitting app by letting one person create a bill with total amount and participants, calculating each person's share, generating individual Paystack payment links for each participant, and tracking payments via webhooks until the bill is fully settled. The bill creator sees live payment status for each participant.

In this article

  1. 1. Bill Creation and Share Calculation
  2. 2. Payment Tracking and Bill Settlement
  3. 3. Learn More

Bill Creation and Share Calculation

Tables: bills (title, total_amount, creator_id, status, created_at), bill_participants (bill_id, name, email, phone, share_amount, status, paystack_ref, paid_at).

async function createBill(creatorId, title, totalAmount, participants) {
  var bill = await db.bills.create({ title, total_amount: totalAmount, creator_id: creatorId, status: 'open' });

  // Default: equal split. Creator can customize amounts
  var equalShare = Math.floor(totalAmount / participants.length);
  var remainder = totalAmount - (equalShare * participants.length);

  for (var i = 0; i < participants.length; i++) {
    var share = equalShare + (i === 0 ? remainder : 0); // give remainder to first person
    var reference = 'BILL-' + bill.id + '-P' + i + '-' + Date.now();

    await db.billParticipants.create({
      bill_id: bill.id,
      name: participants[i].name,
      email: participants[i].email,
      phone: participants[i].phone,
      share_amount: share,
      status: 'pending',
      paystack_ref: reference,
    });

    // Pre-create Paystack payment link for each participant
    await sendPaymentLinkToParticipant(participants[i], share, reference, bill.title);
  }
  return bill;
}

async function sendPaymentLinkToParticipant(participant, amount, reference, billTitle) {
  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: participant.email, amount, currency: 'NGN', reference,
      metadata: { participant_phone: participant.phone, bill_title: billTitle },
    }),
  });
  var link = (await res.json()).data.authorization_url;

  // Send via WhatsApp (preferred in Nigeria) or SMS
  await sendWhatsApp(participant.phone, 'You owe NGN ' + (amount / 100) + ' for "' + billTitle + '". Pay here: ' + link);
}

Payment Tracking and Bill Settlement

// Webhook: mark participant as paid
if (event.event === 'charge.success') {
  var participant = await db.billParticipants.findByRef(event.data.reference);
  if (!participant) return res.sendStatus(200);

  await db.billParticipants.update(participant.id, { status: 'paid', paid_at: new Date() });

  var bill = await db.bills.findById(participant.bill_id);
  var allParticipants = await db.billParticipants.findAll({ bill_id: bill.id });
  var allPaid = allParticipants.every(p => p.status === 'paid');

  if (allPaid) {
    await db.bills.update(bill.id, { status: 'settled' });
    await notifyCreator(bill.creator_id, 'All payments received! Bill "' + bill.title + '" is fully settled.');
  } else {
    var paidCount = allParticipants.filter(p => p.status === 'paid').length;
    await notifyCreator(bill.creator_id, paidCount + '/' + allParticipants.length + ' paid for "' + bill.title + '".');
  }
}

// Get bill status for creator dashboard
async function getBillStatus(billId) {
  var bill = await db.bills.findById(billId);
  var participants = await db.billParticipants.findAll({ bill_id: billId });
  return {
    ...bill,
    participants: participants.map(p => ({ name: p.name, share: p.share_amount, status: p.status, paid_at: p.paid_at })),
    paid_total: participants.filter(p => p.status === 'paid').reduce((sum, p) => sum + p.share_amount, 0),
    remaining: participants.filter(p => p.status === 'pending').reduce((sum, p) => sum + p.share_amount, 0),
  };
}

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 group contribution app for a related pattern where group members make recurring contributions.

Sign up for the McTaba newsletter

Key Takeaways

  • ✓Generate one Paystack payment link per participant — this allows individual tracking of who has paid.
  • ✓Shares can be equal or custom amounts — let the bill creator decide how to divide.
  • ✓Send WhatsApp or SMS payment link to each participant — not just email.
  • ✓The bill creator sees a live dashboard: 2/5 paid, 3/5 pending.
  • ✓Send automatic reminders to participants who have not paid after 24 hours.

Frequently Asked Questions

What if someone claims they cannot afford their share?
The app facilitates payment; it does not enforce it. If someone cannot or will not pay, the bill creator has two options: adjust the remaining participants' shares to cover the gap, or chase the person outside the app. Consider adding a "Request adjustment" feature where the creator can edit individual shares after the fact.
Can the bill creator pay on behalf of someone else?
Yes. Add a "Pay for [Person]" button on the bill dashboard that lets the creator make a Paystack payment using that participant's reference. When the webhook fires, it marks that participant as paid — just as if they had paid themselves.
How do I handle the case where someone pays twice accidentally?
Make your webhook handler idempotent: check if the participant is already marked as paid before processing the event. If already paid, acknowledge the webhook (return 200) but take no action. Refund the second payment using the Paystack Refund API with the duplicate transaction reference.

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 Kenyan Delivery App with Rider Payouts

Delivery apps in Kenya face a unique payment challenge: money flows in from customers through M-Pesa and cards, and it needs to flow out to riders through M-Pesa wallets. Between those two movements, the platform takes a commission. This guide covers the full architecture: customer payments, commission calculation, rider payout via Paystack Transfers, batch processing, and the earnings dashboard that keeps riders informed.

Read more

Paystack Multi-Split Payments Explained

A thorough walkthrough of Paystack multi-split payments: creating Transaction Split groups, assigning shares to multiple subaccounts, using split codes in transactions, updating split groups dynamically, and handling the edge cases around rounding, residuals, and per-order split group creation.

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.