Inspiration

Every month I paid my music teacher by bank transfer, with no name and no note, and then sent the screenshot in a chat as proof. One day I realized my teacher must get dozens of these from different parents, every month, and has to work out who each one is from and what it pays for.

Talking to my teacher, who runs two music-school branches plus online classes, confirmed it: the first ten days of the month are chaos. Bills go out through PayPal, but parents pay however they like: Zelle, checks, bank transfers, or just "I paid". PayPal never sees those payments, so the bills keep saying unpaid until the owner records each one by hand.

What it does

Tally keeps PayPal bills accurate, even when parents pay another way.

  1. A parent sends a payment screenshot, from their private family link or through the owner.
  2. AI reads it: amount, date, sender, method and reference, from any bank or payment app.
  3. Tally's code matches it: it finds the family (even from "R KUMAR"), then checks every combination of open classes. $170 = only Dance $90 + Guitar $80.
  4. The owner approves with one click, because a screenshot isn't proof the money arrived; only the owner can check the bank.
  5. Tally records it on the family's real PayPal invoice and reads PayPal's status back.

It also handles the messy cases:

  • Paid on PayPal: the webhook updates Tally by itself, so the owner does nothing.
  • "I already paid" with no screenshot: the family shows under Needs you, and the owner confirms it.
  • Late families: AI writes a reminder in the family's language, and PayPal sends it with a Pay button. Remind all does every late family at once.
  • Duplicates are caught by fingerprint. Overpayments become family credit. Part payments fill the oldest charges first.
  • Two families fit the same amount? Tally never guesses. It asks the owner to pick.
  • Many receipts at once: sure matches come pre-ticked, so one click approves them all.

How I built it

  • Backend: .NET 10, ASP.NET Core, EF Core 10 and Azure SQL (serverless), with a retry strategy, row-version concurrency and Restrict deletes so payment history can never disappear by accident.
  • PayPal: Invoicing API v2 (create, send, remind, record and delete external payments, status read-back), plus webhooks with signature verification and idempotency keys.
  • AI: Azure OpenAI (gpt-4o-mini) through Semantic Kernel, with structured outputs.
  • Messaging: PayPal webhooks go through an Azure Service Bus queue (dead-letter after 5 tries), and each event is processed once.
  • Frontend: React, TypeScript, Vite, Tailwind and shadcn/ui. The Payments page uses AG Grid Enterprise.
  • Hosting: Docker on Render, with a health check and an uptime monitor.

The rule behind everything is AI reads, code decides, a human approves. The AI never sees the families list and never touches the money. The "Payment Detective" is plain C# that explains every match with strong, medium and weak evidence.

Challenges I ran into

  • Keeping AI out of money decisions. It was tempting to let the model match payments. Instead it only reads, and deterministic code tries every class combination, so every match is exact and explainable.
  • Webhooks can't reach a laptop. I added a "Check with PayPal" fallback that asks the API directly when you return from a PayPal tab.
  • A race condition in the shared demo. The first visit sometimes showed $0 because the page rendered in the middle of an automatic reset. I fixed it with a SemaphoreSlim gate that waits for a running reset and re-checks afterwards.
  • Free-tier hosting. The database pauses when idle, so the app shows a clear "waking up" message instead of an empty screen.
  • No dead ends. Testing showed screens where users didn't know what to do next, so every screen now ends with a clear next step: Done, Dismiss, or Start the guide fresh.

Accomplishments that I'm proud of

  • Every payment, however it was paid, ends up on the family's real PayPal invoice, so PayPal stays the source of truth.
  • What took an owner 10 steps per payment now takes one click.
  • A live demo judges can use, with a 5-step guide, a sandbox test buyer, and a demo that resets itself.

What I learned

  • How deep the PayPal Invoicing API goes. Recording external payments was the key that made the whole idea work.
  • Using AI where it's strong (reading messy images, writing in other languages) and keeping it out of where it's risky (money).
  • React, coming from a .NET background, plus Semantic Kernel structured outputs and designing for idempotency.

What's next for Tally

  • Forward receipts by email or chat instead of uploading
  • Automatic reminders on a schedule
  • An "Ask Tally" agent for batch work, with the owner still approving
  • Several organizers in one Tally, with sign-in

Built With

Share this project:

Updates

Submission history