Digital infrastructure for Kenya's rotating credit associations.
A chama is a self-organized savings and investment group. Kenya has over 300,000 registered chamas — from neighbourhood welfare groups pooling KES 500 each to diaspora investment clubs managing millions of shillings from London, Dallas, and Nairobi simultaneously.
This library provides the core domain model for digitising chama operations. It is not a SaaS — it is the building block.
Chama → Cycles → Rounds → Contributions
↓
Ledger (who paid, who owes, is the pot ready to disburse?)
- Chama — the group: members, officers, welfare fund
- Member — a seat in the rotation, phone number for M-Pesa
- Cycle — one complete rotation (everyone receives once)
- Round — one meeting: one person receives the pot
- Contribution — one payment, linked to an M-Pesa receipt
- Ledger — tracks contributions, identifies defaulters, determines when the pot is disbursable
from chama.models import Chama, Member, Cycle, Round, CycleStatus
from chama.ledger import Ledger
from datetime import date, timedelta
# Build the group
chama = Chama(name="Umoja Investment Club")
members = [
Member("Grace Wanjiku", "0712000001", seat=1),
Member("Patrick Ochieng","0722000002", seat=2),
Member("Diana Mwangi", "0733000003", seat=3),
Member("Samuel Kimani", "0711000004", seat=4),
]
for m in members:
chama.add_member(m)
# Set up a cycle: KES 10,000 per member per round
cycle = Cycle(number=1, contribution_amount=10_000, start_date=date.today(), status=CycleStatus.ACTIVE)
for i, m in enumerate(chama.rotation_order(), start=1):
cycle.rounds.append(Round(
number=i,
recipient_id=m.id,
meeting_date=date.today() + timedelta(days=30 * (i-1)),
pot_amount=10_000 * chama.member_count, # KES 40,000 pot
))
chama.cycles.append(cycle)
# Record payments as M-Pesa receipts come in
ledger = Ledger(chama)
ledger.record_contribution(members[0].id, cycle=1, round_number=1, amount=10_000, receipt="NLJ7RT61SV")
ledger.record_contribution(members[1].id, cycle=1, round_number=1, amount=10_000, receipt="MKJ8RT92XW")
ledger.record_contribution(members[2].id, cycle=1, round_number=1, amount=10_000, receipt="PKL9ST03YV")
# Check status
summary = ledger.round_summary(cycle=1, round_number=1)
print(f"Collected: KES {summary.collected:,} / {summary.expected_pot:,}")
print(f"Ready to disburse: {summary.is_disbursable}")
print(f"Waiting on: {[m.name for m in ledger.defaulters(1, 1)]}")
# → Collected: KES 30,000 / 40,000
# → Ready to disburse: False
# → Waiting on: ['Samuel Kimani']Chamas have been running for decades without software. The domain model respects that:
Contribution tracking is audit-first. Every contribution stores its M-Pesa receipt number. The Ledger's record_contribution() is idempotent on receipt — safe to call multiple times with the same receipt (webhook retries, duplicate SMS notifications).
Seat-based rotation. Members are assigned a seat (1-indexed) when they join. The seat determines their position in the receiving rotation — not join date, not alphabetical order. This matches how actual chamas work.
Partial payments exist. A member who pays KES 3,000 of a KES 5,000 due stays PENDING — not PAID. The Ledger surfaces this correctly and the remaining amount is tracked.
Welfare fund is separate. Most chamas run a parallel welfare fund (funeral, hospital, emergency contributions). This is tracked separately from the main pot and doesn't interfere with the rotation.
M-Pesa is the payment rail. Every Member stores a receive_phone (their M-Pesa number, which may differ from their contact number). Disbursements go to receive_phone.
pip install pytest
pytest tests/ -v
# 25 tests covering: member normalisation, rotation ordering, contribution recording,
# idempotency, ledger summaries, defaulter detection, partial payments- Streamlit dashboard (treasurer view: round status, defaulters, disbursement confirmation)
- M-Pesa STK Push integration (via
mpesa-python) for contribution collection - B2C integration for pot disbursement
- WhatsApp notification templates (contribution reminder, receipt confirmation, meeting notice)
- CSV export for treasurer reports
- Multi-currency support (diaspora chamas operating in USD, GBP, EUR)
- Digital constitution template with governance rules
mpesa-python— M-Pesa integration for contributions and disbursementsnairobi-stack— broader East Africa engineering context
CC BY-NC-ND 4.0. Commercial use: contact@aikungfu.dev.
Chamas have mobilised informal capital in Kenya for generations. This is the software layer they deserve.