Inspiration
Businesses rarely owe money in a straight line. A owes B, B owes C, C owes D, and D owes A, so everyone ends up sending payments that partly cancel each other out. Each extra payment means fees, delays and reconciliation work, especially for small businesses. [Add one line about where you saw this problem.]
I wanted to fix this with AI, but not by letting an AI move money. Payment amounts have to be exactly right, so NetPay uses AI to understand the debts and deterministic code to calculate and verify every amount.
What it does
NetPay takes a web of unpaid invoices between two or more businesses and turns it into the smallest practical set of net payments.
- Groups obligations by connected parties and by currency, and never mixes currencies
- Shows who owes whom after netting
- Optionally drafts PayPal Sandbox invoices, only after a human approves the plan
Example group:
A owes B 100 INR
B owes C 60 INR
C owes D 40 INR
D owes A 20 INR
B owes D 25 INR
NetPay handles this as one group, not as a three-party triangle. The five obligations net down to three payments: A pays 15, 20 and 45 to B, C and D.
How we built it
Let ( o_{ij} ) be the amount party ( i ) owes party ( j ). Each party's net position is what it is owed minus what it owes:
$$n_i = \sum_j o_{ji} - \sum_j o_{ij}$$
Only the non-zero net positions matter. A zero-sum subgroup of ( k ) parties can be settled in ( k-1 ) payments, so if the parties are split into ( m ) zero-sum subgroups, the total is
$$P = N - m$$
payments, where ( N ) is the number of parties with a non-zero balance. Minimizing payments therefore means maximizing ( m ). For groups of up to 16 non-zero balances, an exact dynamic-programming optimizer finds that maximum partition, and each subgroup is settled with greedy payer-to-creditor matching. Larger groups use the greedy algorithm on the whole group, which is fast but not guaranteed to be minimal.
The system has four layers:
- Settlement engine (
netpay/engine.py): netting, exact and greedy algorithms, and verification of every plan - Exact money: amounts are integers in units of 1/10,000 of a currency unit, and more than four decimal places are rejected rather than rounded. Currency codes are validated against Babel's CLDR registry.
- Local AI (
netpay/ai.py): a chat demo using Ollama withqwen2.5:1.5bthat extracts obligations from natural language and asks for clarification on ambiguous inputs like$or "dollars" instead of guessing. No cloud AI key is needed. - PayPal Sandbox (
netpay/paypal.py,pipeline.py,ledger.py): invoice creation behind an explicit approval gate, with an idempotent SQLite ledger so re-running a plan never sends duplicate invoices. The config loader rejects non-Sandbox PayPal URLs.
Challenges we ran into
- Trusting a small local model. It can misread a request, so the AI never calculates anything. It only extracts obligations, and the user reviews them before any plan is approved.
- Money correctness. Floats were out, so I used integer units, strict decimal parsing, and per-currency minor-unit formatting (0 decimals for JPY, 3 for KWD) that rejects amounts needing rounding.
- Optimality vs. scale. Exact partitioning grows exponentially with group size (about 75 ms at 14 parties and 203 ms at 16 in my benchmark), so I use it up to 16 non-zero balances and fall back to greedy beyond that.
- Safe invoicing. Re-running a plan must never double-send, which is what the idempotent ledger is for.
- [Add one personal challenge, e.g. a bug or design decision that cost you time.]
Accomplishments that we're proud of
- A settlement engine with a provable minimum payment count for groups of up to 16 parties, plus a verified greedy fallback for larger ones
- A clear separation of roles: the AI extracts and explains, deterministic code calculates and verifies, and a human approves before anything reaches PayPal
- An idempotent ledger and a Sandbox-only safety check, so the demo cannot double-send invoices or touch live PayPal
- A unit test suite and a benchmark. On seeded samples, 80 obligations among 16 parties settle in 15 payments:
| Parties | Obligations | Greedy payments | Exact payments | Greedy time | Exact time |
|---|---|---|---|---|---|
| 6 | 20 | 5 | 5 | 0.05 ms | 0.3 ms |
| 10 | 40 | 9 | 9 | 0.07 ms | 4.0 ms |
| 14 | 60 | 13 | 13 | 0.10 ms | 75.4 ms |
| 16 | 80 | 15 | 15 | 0.10 ms | 203.4 ms |
These are single-run timings on my development machine, and the seeded samples happened to give the same payment count for both strategies. Exact optimization matters most on structured cases where greedy misses a zero-sum subgroup.
What we learned
- Keep AI for understanding and explanation, and keep exact work in verified deterministic code
- Human approval gates and idempotency matter as much as the algorithm in anything touching payments
- [Add your own takeaway.]
What's next for NetPay: AI-Assisted Multilateral Settlement
A hosted API, production PayPal onboarding (a partner or payout flow for multi-merchant use), and a dashboard. This repo is a local milestone/demo, not a production service.