A Go, GORM, and SQLite system evolving from a personal banking application into a digital SACCO management platform for Kenyan SACCOs, chamas, microfinance groups, and other member-owned savings organizations.
The existing account, transaction, role, M-Pesa, statement, and audit capabilities remain the foundation. SACCO-specific features are being added incrementally so that member ownership, loans, eligibility rules, and future distributions have explicit ledgers and audit trails.
Built at Zone01 Kisumu β where we understand the why before writing the code.
- Basic account creation and balance tracking
- In-memory storage with Go maps
- Simple deposit and withdrawal logic
- Migrated from maps to SQLite via GORM
- Atomic DB transactions β money never gets lost mid-operation
- Soft delete system preserving financial audit trails
- Clean architecture β models, services, handlers, router
- Secure session management with random 32-byte token generation
- 10-minute inactivity timeout with browser warning popup
- Login rate limiting β 5 attempts before 15-minute lockout
- Timing attack prevention on authentication
- Secure cookie flags β HttpOnly, Secure, SameSite=Strict
- HTTPS enforcement in production
- 4-digit transaction PIN separate from login password
- Suspended accounts blocked at login with session invalidation
- Admin cannot block their own account
- Multi-account support β current and savings accounts per user
- Transfer by phone number or account number
- Account statements β PDF and CSV download with date range selection
- User profile management β update contact details, change password, change PIN
- Balance visibility toggle
- Transaction receipts with unique reference numbers
- Transaction search, filtering and pagination
- Email notifications on every transaction
- SMS notifications via Africa's Talking
- Admin audit log tracking every admin action
- Transaction reports and analytics with 7-day chart
- Automated suspicious transaction flagging
- Member share capital tracked separately from spendable savings
- Administrator-recorded share contributions with an audit trail
- Loan application, approval, disbursement, repayment schedules, and balances
- Configurable loan eligibility rules tied to savings and/or share capital
- Savings interest and share dividend calculation and posting
SACCO features are delivered as separate tickets and commits. Features are not described as complete while migrations, posting operations, or required policy configuration remain outstanding.
| # | Feature |
|---|---|
| 1 | Session expiry with 10-minute inactivity timeout |
| 2 | Login rate limiting with 15-minute lockout |
| 3 | Secure cookie flags and HTTPS enforcement |
| 4 | Transaction PIN on all financial operations |
| 5 | User profile page |
| 6 | Change password and change PIN |
| 7 | Multiple accounts per user (current + savings) |
| 8 | Transfer by phone number or account number |
| 9 | Transaction receipt page with print support |
| 10 | Transaction search, filtering and pagination |
| 12 | Account statement download (PDF + CSV) |
| 13 | Email notifications after every transaction |
| 14 | SMS notifications via Africa's Talking |
| 18 | Admin audit log |
| 19 | Transaction reports and analytics |
| 20 | Automated suspicious transaction flagging |
| 26 | Balance visibility toggle |
| 27 | Login with username or email |
| 31 | Account number on every transaction record |
| 32 | Responsive mobile layouts |
| 33 | Teller role assignment and teller operations |
| 34 | M-Pesa STK Push deposits |
| 35 | M-Pesa payment callback processing |
| 36 | SMS notification opt-out preference |
| 37 | Daily deposit, withdrawal, and transfer limits |
| 38 | Member account closure with balance and audit safeguards |
| 39 | Super-admin role for privileged administration |
| 40 | M-Pesa B2C withdrawal request client |
| 41 | Scheduled recurring transfer instructions and processing |
| 42 | Device registration and administrator approval gate |
| 43 | JWT token service for mobile API authentication |
| 44 | Production SMTP configuration validation |
| 45 | WebAuthn credential boundary and explicit provider gate |
| 46 | Minor-unit KES amount parsing utility |
| 47 | Member share-capital balance and contribution ledger |
| 48 | Loan application, approval, disbursement, schedules, and repayment tracking |
| 49 | Configurable savings and share-capital loan eligibility policy |
| 50 | Configurable annual savings interest and share dividend distributions |
| 51 | Monthly-average distributions, withholding, governance approval, and scheduled previews |
| 52 | Administrator share-capital redemption and member-exit integration |
| 53 | Scheduled loan collection, overdue tracking, and 90-day default classification |
| 54 | SACCO loan, share-capital, and distribution CSV reports |
| 55 | Persistent member loan arrears and default notifications |
The remaining items require dedicated production integrations or migration planning:
- Complete WebAuthn ceremony integration with a supported authenticator service.
- Migrate persisted monetary columns from whole KES to cents after a controlled data migration.
The following features are implemented but require production credentials and operational setup:
- M-Pesa B2C payout callbacks and settlement reconciliation.
- Verified custom-domain SMTP sender configuration.
- JWT mobile API endpoints built on the token service.
- Add a dedicated share-capital balance and contribution ledger rather than treating ownership capital as a spendable account.
- Allow administrators to record contributions atomically.
- Display share capital separately on the member dashboard.
- Integrate non-zero share capital explicitly into member exit checks.
- Schema migration: new GORM tables are required.
- Add loan applications with pending, approved, rejected, disbursed, and completed states.
- Record administrator decisions and disbursement.
- Generate scheduled repayments and track principal paid, interest paid, and outstanding balance.
- Schema migration: loan and repayment tables are required.
Automated collection from member accounts is not assumed; repayment posting is an explicit operation until a scheduler or payment mandate is implemented.
- Store eligibility policy as SACCO configuration rather than hard-coding a common industry rule.
- Support a configurable multiple of savings, share capital, or both.
- Explain eligibility results before an application is accepted.
- Schema migration: SACCO configuration fields or a configuration table are required.
No default such as βthree times savingsβ is treated as the SACCO's policy without administrator configuration.
- Configure savings interest and share dividend rates and calculation periods.
- Calculate proposed allocations from ledger balances.
- Require an explicit administrator posting action before balances change.
- Record every posted allocation for audit and idempotency.
- Schema migration: distribution runs and member allocation records are required.
Rates, balance basis, pro-rating rules, and posting destinations vary by SACCO and must be configured. Earlier tickets do not assume interest or dividends already exist.
fintech-labs/
βββ cmd/
β βββ server/
β βββ main.go
βββ internal/
β βββ db/
β β βββ db.go
β βββ handlers/ # HTTP handlers and auth/role middleware
β β βββ accounts.go
β β βββ admin.go
β β βββ authentication.go
β β βββ profile.go
β β βββ receipts.go
β β βββ statements.go
β β βββ transactions.go
β β βββ ui.go
β βββ models/
β β βββ models.go
β βββ notifications/
β β βββ email.go
β β βββ sms.go
β βββ router/
β β βββ router.go
β βββ auth/
β β βββ jwt.go
β β βββ webauthn.go
β βββ mpesa/
β β βββ mpesa.go
β βββ services/ # business rules and transactional operations
β β βββ services.go
β βββ utils/
β βββ utils.go
βββ web/
β βββ static/
β β βββ app.js
β β βββ styles.css
β βββ templates/
β βββ admin.html
β βββ dashboard.html
β βββ email.html
β βββ login.html
β βββ profile.html
β βββ receipt.html
β βββ register.html
β βββ register_admin.html
βββ Dockerfile
βββ go.mod
βββ go.sum
βββ README.md
- Language: Go (Golang)
- Database: SQLite with GORM ORM
- Frontend: HTML, CSS, Vanilla JavaScript
- Auth: Custom session management with bcrypt
- PDF Generation: gofpdf
- Email: SMTP via net/smtp with production sender validation
- SMS: Africa's Talking SMS API
- Charts: Chart.js
- Deployment: Render (https://fintech-labs-uaph.onrender.com)
# Clone the repository
git clone https://github.com/eojuma/fintech-labs.git
cd fintech-labs
# Sync dependencies
go mod tidy
# Set up environment variables
cp .env.example .env
# Edit .env with your credentials
# Run the app
go run cmd/server/main.goVisit http://localhost:8080 to access the app.
| Variable | Description |
|---|---|
DATABASE_PATH |
Path to SQLite database file (default: transaction.db) |
RENDER |
Set to true in production to enable secure cookies and HTTPS |
TZ |
Timezone (set to Africa/Nairobi on Render) |
SMTP_HOST |
SMTP server host (e.g. smtp.gmail.com) |
SMTP_PORT |
SMTP server port (e.g. 587) |
SMTP_USER |
SMTP username / email address |
SMTP_PASS |
SMTP App Password |
SMTP_FROM |
Sender email address |
AT_USERNAME |
Africa's Talking username (use sandbox for testing) |
AT_API_KEY |
Africa's Talking API key |
MPESA_B2C_INITIATOR_NAME |
Safaricom B2C initiator name |
MPESA_B2C_SECURITY_CREDENTIAL |
Encrypted B2C security credential |
MPESA_B2C_TIMEOUT_URL |
Public B2C timeout callback URL |
MPESA_B2C_RESULT_URL |
Public B2C result callback URL |
JWT_SECRET |
At least 32 random characters for mobile API tokens |
| Endpoint | Method | Auth | Purpose |
|---|---|---|---|
/login |
GET, POST | Public | User login |
/register-page |
GET | Public | Registration page |
/register |
POST | Public | Create account |
/logout |
POST | Session | Log out |
/dashboard |
GET | Session | User dashboard |
/deposit |
POST | Session + PIN | Deposit funds |
/withdraw |
POST | Session + PIN | Withdraw funds |
/transfer |
POST | Session + PIN | Send money |
/accounts/open |
POST | Session | Open savings account |
/statement/download |
GET | Session | Download statement |
/transactions/filter |
GET | Session | Filter transactions |
/receipt/{ref} |
GET | Session | View transaction receipt |
/profile |
GET | Session | View profile |
/profile/update |
POST | Session | Update contact details |
/profile/change-pin |
POST | Session | Change transaction PIN |
/profile/change-password |
POST | Session | Change password |
/profile/close |
POST | Session | Close account after zero-balance and password checks |
/session/refresh |
POST | Session | Keepalive |
/admin |
GET | Admin | Admin dashboard |
/admin/deposit |
POST | Admin | Deposit to user account |
/admin/withdraw |
POST | Admin | Withdraw from user account |
/admin/share-contribution |
POST | Admin | Record a member share-capital contribution |
/admin/share-redemption |
POST | Admin | Redeem share capital to a member current account |
/loans/apply |
POST | Session | Submit a member loan application |
/admin/loans/decision |
POST | Admin | Approve or reject a pending loan |
/admin/loans/disburse |
POST | Admin | Disburse an approved loan to the current account |
/admin/loans/repayment |
POST | Admin | Record an explicit loan repayment |
/admin/loans/eligibility-policy |
POST | Admin | Configure loan eligibility rules |
/admin/distributions/policy |
POST | Admin | Configure interest and dividend rates |
/admin/distributions/preview |
POST | Admin | Preview a period distribution |
/admin/distributions/post |
POST | Admin | Explicitly post a previewed distribution |
/admin/distributions/approve |
POST | Super admin | Record board or AGM approval for a distribution |
/admin/reports/sacco |
GET | Admin | Download SACCO management CSV reports |
/admin/toggle |
POST | Admin | Block or unblock account |
/admin/audit-log |
GET | Admin | View full audit log |
/admin/flagged |
GET | Admin | View flagged transactions |
/admin/assign-teller |
POST | Super admin | Assign teller role |
/admin/revoke-teller |
POST | Super admin | Revoke teller role |
/mpesa/deposit |
POST | Session | Initiate STK Push deposit |
/mpesa/callback |
POST | Safaricom | Process STK Push callback |
- Passwords hashed with bcrypt
- Session tokens are cryptographically random 32-byte hex strings
- Sessions stored server-side and validated on every request
- Cookie reissued on every request to reset browser-side MaxAge
- Cookie flags: HttpOnly, Secure (production), SameSite=Strict
- Session expires after 10 minutes of inactivity
- Warning popup at 9 minutes with keepalive option
- Login locked after 5 failed attempts for 15 minutes
- Timing attack prevention on authentication
- Transaction PIN separate from login password
- Suspended accounts blocked at login with all sessions invalidated
- Admin cannot block their own account
- HTTPS enforced in production via redirect middleware
- Users can only view their own receipts
- Automated suspicious transaction flagging
- Daily transaction limits for deposits, withdrawals, and transfers
- SMS opt-out preference
- Device approval gate for recognized browsers
- Super-admin authorization for role management
- Idempotent and amount-validated M-Pesa callbacks
- Signed JWT token service with expiry and role claims
Transactions are automatically flagged when:
| Rule | Threshold |
|---|---|
| Large single transaction | Amount β₯ KES 100,000 |
| Rapid successive transactions | 3 or more transactions within 5 minutes |
| Large withdrawal relative to balance | Withdrawal β₯ 80% of account balance |
Flagged transactions appear on the admin dashboard for review.
.dband.envfiles excluded from git via.gitignore- Soft deletes via GORM DeletedAt β financial records never deleted
- Atomic DB transactions on every financial operation
- All session records cleaned up on logout and account suspension
- Every transaction has a unique reference number for tracing
- Admin audit log is permanent and never deletable
Evans Juma β @eojuma
Special thanks to Silas Lelei for peer-reviewing the GORM logic and testing the endpoints during the transition from maps to persistent storage.