Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

176 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Invoice Anything

Quoted, approved, done, paid — before the ute left the street.

A phone-first product for pricing and invoicing anything — a tradie's job, a market stallholder's furniture — on the spot, with a payment link the customer opens on their own phone and pays in one tap.

Built for the Pinch Payments $50K Hackathon (spec settled 24 July 2026 · MVP demo 26 July · pitch night 31 July).


The idea

Invoicing apps are not scarce. The differentiator here is speed plus payment details captured at quote time, so the invoice is paid in one tap the moment the job is done. No chasing.

On top of that sits the second half of the pitch — a loyalty scheme funded by the payment rail itself:

  • The Business is charged a card-equivalent rate whichever method the Customer picks, so it is cost-neutral between methods and keeps nothing from the difference.
  • A Customer who pays by bank earns points out of that difference — the gap between what we charge and what the bank rail actually cost, withheld by Pinch as an applicationFee and credited to the platform.
  • Points are pooled across every Business in the app and spend as a discount against any Document from any Business. 100 points = $1.

Nobody is out of pocket honouring points earned elsewhere: the cash was collected at earn time, from the payment that created them.

The two flows

Business (installs the app)

  1. Describe — say what you did, in your own words. It's a text box; the phone keyboard's own mic button is the voice input, so there's no second speech vendor.
  2. Review — the description comes back as priced, itemised lines. Labour off the configured hourly rate, materials off the Business's own Price Book, anything else an AI estimate flagged for review. GST applied per the Business's registration.
  3. Send — as a Quote with an approve-up-to Ceiling, or straight to Invoice. The Customer gets a link.
  4. Work complete — confirm the final amount. At or under the Ceiling it invoices immediately; over it, the Customer re-approves first.

Customer (installs nothing, and there is never a wall in front of paying)

  1. Opens the link in their phone browser. Approves the Ceiling and captures payment details — bank or card, on their own device, never touching our server.
  2. Pay now — one tap, with a nudge showing what paying by bank earns.
  3. Payment confirmation — itemised, plus points earned. Deliberately not called a receipt: Paid means the money is committed, not that it reached the Business.

Repo layout

apps/
  api/    .NET 10 minimal API — auth, documents, pricing, payments, rewards
  app/    Expo / React Native app (iOS, Android, and the static web export)
docs/
  adr/          eight architecture decision records — the reasoning
  agents/       conventions for agents working in this repo
  handover/     session handovers
specs/          the product spec and the v2 rewards build spec
.azure/         ARM template for the whole resource group
.ado/           Azure DevOps build/deploy pipelines
CONTEXT.md      the domain glossary — read this first

Stack

Concern Choice
API .NET 10, ASP.NET Core minimal APIs, EF Core 10, Azure SQL
Auth ASP.NET Core Identity + JWT bearer (Businesses); one-time codes (Customers)
App Expo 57 / React Native 0.86 / React 19, expo-router, Zustand
AI pricing Anthropic Messages API (with a fixture pricer for tests and offline work)
Payments Pinch — cards and BECS direct debit, Managed Merchants, webhooks
Hosting Native iOS and Android builds from the one codebase; Azure App Service (API) + Storage static website (web export), Australia East

What we used from Pinch

Everything below runs against Pinch's sandbox. The integration lives in apps/api/InvoiceAnything.Api/Payments and apps/app/src/features/payments.

CaptureJS — tokenisation in the Customer's browser. Card and BECS details are tokenised client-side and never touch our server. Pinch ships CaptureJS as a browser script with no npm package and no native SDK, so the native build deliberately refuses to collect payment details rather than falling back to posting them to our own API — which is fine, because the Customer always arrives by link in a browser and the Business's app never collects them.

Payers and sources. POST payers and POST payers/{id}/sources store the Customer's payment method against the token, which is what makes the later invoice payable in one tap with no retyping.

Payments — two rails, one Document. POST payments for a BECS direct debit, whose outcome arrives days later; POST payments/realtime for a card, which is usually decided in the same breath. Both pass through Processing on the way to Paid.

Managed Merchants — one sub-merchant per Business. Our Pinch account is the parent and each Business is created underneath it via POST merchants/managed. Every call that touches a Business's money carries the Current-Merchant header, so the payment is created against that sub-merchant and settles to that Business. Getting this wrong is quiet in a way worth naming: a wrong merchant is a 400, but an absent header falls through and charges the parent account with no error anywhere — so the header is enforced in the call signature rather than left to a caller to remember (ADR-0004).

applicationFee — the mechanism the whole rewards model rests on. Documented by Pinch as Managed Merchants only, it withholds a share of a payment from the Business's settlement and credits it to the parent. That withheld amount is what funds a Customer's points. The figure is read back off the payment, never recomputed from a fee schedule — recomputing it re-implements Pinch's own cap and rounding, and the Business drifts off cost-neutral silently.

Webhooks — how a bank debit finishes on its own. Deliveries are HMAC-verified with a replay window before anything is read, and Pinch's payment statuses are translated into our own vocabulary at the edge: approved, settled and transferred all mean Paid; dishonoured is terminal and carries its reason. Pinch publishes statuses that are neither success nor refusal (cleared-pending-dispute, returned-without-settlement …) — those leave the Document where it is for a human, rather than being guessed in either direction.

GET /fees — the merchant's own schedule. Read per merchant and briefly cached, used only to display what paying by bank would earn. The authoritative number for any single payment is what Pinch itself returns for that amount and method. The cache is load-bearing rather than an optimisation: the pay screen is deliberately reachable without a session, so an uncached read would let anyone holding a link turn one page load into one Pinch request.

GET transfers/items/{id} — settlement lines, paged, for reconciling what actually landed.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages