-
-
Dashboard
-
Risk report (risky sentences highlighted, fairer wording)
-
Milestones table (AG Grid) and payment timeline (Bryntum)
-
Approval inbox (milestone invoice and change order proposals)
-
Billing panel with the client’s message and “Analyse with AI”
-
Client page (paid deposit, watermarked locked file)
-
Evidence pack
-
Architecture diagram
Freelancers lose money in three places: a vague contract they signed too quickly, extra work the client asks for "while you're at it", and invoices that are paid late or never. Each one is small, so nobody chases it, and together they add up to weeks of unpaid work. We wanted an agent that watches all three, from the day the contract arrives to the day the money lands.
What it does
MilestonePay protects a freelancer's money from contract to cash.
- Before signing: paste a contract, or upload a PDF or a photo. The AI lists the payment milestones, flags risky clauses with a fairer rewrite for each, and suggests a deposit.
- Approval: the freelancer edits the milestones in a grid and approves. The deposit invoice goes out through PayPal at once.
- During the work: client messages are read against the contract. The AI spots approved milestones, out-of-scope requests (priced as change orders at the freelancer's hourly rate) and extra revision rounds. Each one waits in an approval inbox.
- Delivery: the client gets a private page with milestones, PayPal payment buttons and watermarked previews. The full files unlock by themselves when PayPal reports the invoice paid, and lock again after a refund.
- After delivery: dated milestones are invoiced a week early, late invoices are marked overdue, and PayPal reminders go out on a fixed schedule.
- If there's a dispute: one click builds a printable evidence pack with what was agreed, every file delivered (with a SHA-256 fingerprint), what the client approved and wrote, every invoice and payment, and the full audit log.
The rule we built around
The AI proposes, code moves the money, people approve. The AI never calls PayPal and never sets a final amount. Every quote it gives must appear word for word in the contract or message, or code throws it away. Prices come from hours times the freelancer's rate, worked out by code. Nothing is billed until the freelancer clicks approve, and every action is written to an audit log with who did it.
How we built it
- PayPal (sandbox): Invoicing v2 to create, send, read and remind; signed webhooks for paid, cancelled and refunded, each one checked with PayPal's signature verification; and an hourly check of unpaid invoices in case a webhook is slow or lost.
- AI: DeepSeek V4.1 Flash through OpenRouter, with strict JSON schemas, one shared connection, and a daily cap on calls.
- Backend: Python, FastAPI and SQLModel, on SQLite locally and Postgres (Neon) when hosted.
- Frontend: React, TypeScript and Tailwind, with AG Grid for the milestone, invoice, change order and audit tables, and Bryntum Gantt for the payment timeline.
- Hosting: one Docker container on Render, set up from a
render.yamlblueprint. - Tests: more than 250 automated tests that use a pretend PayPal and a pretend AI, so they cost nothing to run.
We are two people, and each of us worked with an AI coding assistant. We split the project by ownership: one took PayPal, data, money flow, the client page and hosting; the other took the AI prompts, the screens, the test contracts and the video.
Challenges we ran into
- Webhook signatures. PayPal signs the exact bytes it sent. Re-writing the event as JSON (key order, spacing, accents) made real events fail the check, so we now send the body back exactly as it arrived.
- Double clicks and money. Two fast clicks could create two invoices. We added per-item locks and tests that fire requests at the same time.
- Slow sandbox webhooks. A "paid" event took about eight minutes in one test, so we added the hourly PayPal check and a manual "Check payment" button.
- Keeping the AI honest. Models invent convincing clauses. The word-for-word quote check, and never letting the AI set a price, removed that whole class of problem.
- A tiny AI budget. We had about two dollars of credit, so we cached readings, capped daily calls, and kept every test on the pretend AI.
- Free hosting sleeps. Our hourly jobs waited an hour before their first run, but the free plan sleeps after 15 quiet minutes. They now run a minute after the app wakes.
Accomplishments that we're proud of
- A real end-to-end run on the hosted app: invoice sent, paid by a sandbox buyer, PayPal's webhook arrived, the milestone turned paid and the client's file unlocked, with nobody touching anything.
- The safety design. A judge can try prompt injection in a contract and the money still can't move without an approval.
- The evidence pack, which turns the audit log into something a freelancer can actually hand over in a dispute.
What we learned
- Money code needs to be boring: fixed status steps, decimal amounts, one place that talks to PayPal, and a log of everything.
- An AI is most useful when its job is narrow (read and propose) and code checks every answer.
- Test on the real service early. The sandbox behaved differently from our assumptions more than once.
What's next for MilestonePay
- Accounts for more than one freelancer.
- AI-drafted reminder and change-order notes, still approved by the freelancer.
- More currencies and a mobile layout for the freelancer side.
- Moving from the PayPal sandbox to live payments once it has been reviewed properly.
Built With
- ag-grid
- bryntum
- docker
- fastapi
- neon
- openrouter
- paypal
- postgresql
- python
- react
- render
- sqlmodel
- tailwindcss
- typescript
- vite