Skip to content

ProjectsPennyWise

PennyWise

Personal project

The payments backend for a savings app, covering virtual accounts, wallets, and the money moving between them.

Role
Solo build · backend only
Timeline
2 weeks
Stack
TypeScript · Express · MongoDB · Redis · Flutterwave

Overview

PennyWise is the backend for a savings and payments app. Each user gets a virtual account and a wallet, plus the plumbing to move money between them: fund by bank transfer, send to another user, or withdraw to a saved account. Payments run through Flutterwave. Withdrawals are gated behind an OTP and a transaction PIN, and a webhook reconciles every transfer against its stored reference.

Problem

I'd been using a savings app and kept wondering how the money side actually worked, particularly virtual accounts, settlements, and what happens when a transfer half-completes. Reading about it wasn't enough, so I built the backend myself.

The hard part isn't the CRUD. It's that the payment provider, not your database, is the source of truth for money. A withdrawal can succeed at Flutterwave and never reach your app; a deposit can land before you're ready for it. So the system is built around reconciliation: every transfer stores a reference, and a webhook settles it after the fact rather than trusting the request that started it.

How it fits together

How money moves through the PennyWise APIThree flows share one user. Fund: the user sends a bank transfer to a virtual account hosted at Flutterwave, and a webhook credits the wallet and records the transaction. Send: the user transfers to another username, and PennyWise moves the balance from wallet to wallet internally. Withdraw: the user requests a withdrawal, PennyWise gates it behind an OTP and a transaction PIN, calls Flutterwave to disburse and saves the reference, and a webhook later reconciles the ledger by matching that reference. Flutterwave moves the money; the webhook, not the request, settles the ledger.Youapp userFUNDbank transferVirtual accounthosted at FlutterwavewebhookWallet creditedtransaction recordedSENDto @usernamePennyWiseinternal transferwallet to walletRecipient walletbalance updatedWITHDRAWrequestPennyWiseOTP + PIN gate/transfersFlutterwavedisburse · save refwebhookLedger reconciledmatched by referenceFlutterwave moves the money. The webhook, not the request, settles the ledger.asynchronous webhook callback

Fund and withdraw cross the Flutterwave boundary and settle asynchronously; a send stays inside PennyWise and completes in one transaction.

Optimized for Humans and Machines

© 2026 Ayomide Tijani