Inspiration
I went looking for the most complained-about companies in Australian payments. Ezidebit has 110 reviews on ProductReview and every one is negative. Debit Success has 493 reviews and sits at 1.1 stars. The complaints tell one story on repeat: a direct debit bounced, the provider retried it blind, and every retry added another $10 to $22 dishonour fee. One gym member copped three $9.90 fees in a single day on a $45 membership. She had already opened the gym app and paid manually. The retry fired anyway.
Failed payments cause 20 to 40 percent of subscription churn, and most of those customers wanted to stay. Recovery tooling exists overseas, but it is card-only and Stripe-only. Nothing does it for BECS direct debit, the rail Australian gyms, telcos and billers actually run on.
The full research pack behind this (complaint threads, market numbers, fee mechanics, competitor table) ships inside the repo under docs/research.
What it does
Recoupe listens to Pinch's bank-results webhook. When a payment dishonours, it reads the decline code and picks the right move instead of hammering the same account:
- Insufficient funds: the customer gets a fee-free SMS with three choices. Retry on payday, pay half now, or pick a date. No login, one tap.
- Expired or invalid card: no retry at all, since it can only fail again. The SMS carries a 20-second update-card link instead, tokenised through Pinch's CaptureJS so card details never touch our servers.
- Hard failures like account-closed: retries are suppressed entirely. Pinch's own docs say those retries just burn the processing fee.
There is also a payday nudge before a debit lodges: "this comes out Tuesday, your payday is Thursday, want to move it?" That prevents the first fee. Everything downstream of the webhook can only stop the repeats.
Merchants get a dashboard with two numbers: revenue recovered and fees avoided. Pricing is 20 percent of recovered revenue, nothing upfront, so it only costs money when it makes money.
How I built it
Node 20, TypeScript, Express, SQLite, React, Vite. The server maps all 7 documented Pinch dishonour codes to recovery plans, verifies webhook HMAC signatures over the raw body, and issues short-lived single-use signed rescue tokens. The Pinch client supports both auth paths (Application OAuth with scope=api1, and Merchant keys), the Time-Travel sandbox header, magic-string dishonour simulation, and API-registered webhooks with an events-polling fallback. The whole demo runs in mock mode with zero credentials; the same code flips to the live Pinch test environment with two lines in .env.
Challenges
Email dunning gets 8 to 11 percent click-through (Baremetrics, sample of one million emails), which is too low to build a business on. So the rescue flow is SMS-first. Expired cards cannot be saved by retries at all: Stripe repairs them silently with network account updaters, Pinch has no equivalent, so the update-card link is doing the job of a feature this rail does not have. And a genuinely annoying one: the update-card form overflowed our phone-frame demo panel sideways during QA, which we caught by driving the real UI with a browser agent before filming.
Verified, not asserted
23 of 23 end-to-end checks pass: signed webhook in, decline-code routing, rescue page, single-use token reuse rejection (410), payday nudge, both recovery flows moving real counters. The 60-second video is one continuous unedited take of the running app.
Built With
- node.js
- pinch-payments-api
- react
- sms
- sqlite
- typescript
Log in or sign up for Devpost to join the conversation.