Skip to content

gabrielmahia/chama-protocol

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

chama-protocol

Digital infrastructure for Kenya's rotating credit associations.

Tests License: CC BY-NC-ND 4.0

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.


What it models

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

Quick start

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']

Domain design

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.


Testing

pip install pytest
pytest tests/ -v
# 25 tests covering: member normalisation, rotation ordering, contribution recording,
# idempotency, ledger summaries, defaulter detection, partial payments

Roadmap

  • 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

Related

  • mpesa-python — M-Pesa integration for contributions and disbursements
  • nairobi-stack — broader East Africa engineering context

License

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.

About

Digital infrastructure for Kenya's rotating credit associations (chamas/ROSCAs) — domain model, ledger, M-Pesa integration.

Topics

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages