---
title: "Paul Hartness (@retryfix) | Hashnode"
description: "Paul Hartness (@retryfix) writes on Hashnode. Read their latest articles and follow their work."
url: "https://hashnode.com/@retryfix"
---

@retryfix

# Paul Hartness

@retryfix · London, UK · Joined October 2026

## Comments

["A timeout means I don't know, not failed" is the line I'd pin above every payments handler. The same idea shows up on subscription renewals, where nobody taps Pay again: the gateway retries on its own schedule over days. So "not paid yet" needs its own state with its own rule for access, rather than being read as failed (cut them off) or as fine (ignore it). Otherwise the app either locks out a customer whose card is about to clear, or keeps serving one whose retries have all run out.](https://hashnode.com/posts/payment-failed-money-deducted-idempotency/6af2d4383c36fe3908d4fd5a/comment/6af439e92dce8ada2d19f134)

Comment · Article · 16h ago · [Payment failed but money deducted: how idempotency keys stop double charges](https://hashnode.com/posts/payment-failed-money-deducted-idempotency/6af2d4383c36fe3908d4fd5a)

[Exactly — once the failed-payment email is one template for both, the reminder is the last clean chance to say "update the card" before they guess wrong.](https://hashnode.com/posts/forgot-to-cancel-renewal-refund/6aead917454209be665e42c6/comment/6aec4ac4b30513f7951d0951#reply-6aec9b3b7543afaac85ddf69)

Reply · Article · 6d ago · ["I forgot to cancel." Before you answer the renewal refund email, check whether you ever reminded them.](https://hashnode.com/posts/forgot-to-cancel-renewal-refund/6aead917454209be665e42c6)

[Good catch that renewal reminders only go to charge\_automatically subscriptions. The reminder is also a good moment to ask for a card update, because once a renewal fails, Stripe's failed-payment email is the same template whether the card is short on funds or expired.](https://hashnode.com/posts/forgot-to-cancel-renewal-refund/6aead917454209be665e42c6/comment/6aec4ac4b30513f7951d0951)

Comment · Article · 6d ago · ["I forgot to cancel." Before you answer the renewal refund email, check whether you ever reminded them.](https://hashnode.com/posts/forgot-to-cancel-renewal-refund/6aead917454209be665e42c6)

[Storing first and reading current state from Stripe is the right call, and it matters most for anything that contacts the customer. If the sweeper picks up an invoice.payment\_failed event an hour late, the retry may already have succeeded. Checking the invoice status before sending a failed-payment email saves a confusing message to someone who has already paid.](https://hashnode.com/posts/stripe-webhooks-in-next-js-verify-deduplicate-and-process-each-event-once/6ae86d2557dd48491df9ec27/comment/6aeaf8570e75c729b805d936)

Comment · Article · Nov 4 · [Stripe webhooks in Next.js: verify, deduplicate and process each event once](https://hashnode.com/posts/stripe-webhooks-in-next-js-verify-deduplicate-and-process-each-event-once/6ae86d2557dd48491df9ec27)

[Good list. The 30 October grants change is going to catch a lot of people with a 42501 they can't explain. One I'd add next to the invoice.paid point is the failure side of a renewal. If the handler only listens for paid events, a failed renewal never reaches Supabase, so the user stays active in your table while Stripe has the subscription past\_due. Handling customer.subscription.updated and reading the current status covers both directions.](https://hashnode.com/posts/why-isn-t-my-stripe-webhook-updating-supabase/6ae9d43fde20b4f15aa70ce8/comment/6aeaf8440eabac1451004c02)

Comment · Article · Nov 4 · [Why isn’t my Stripe webhook updating Supabase?](https://hashnode.com/posts/why-isn-t-my-stripe-webhook-updating-supabase/6ae9d43fde20b4f15aa70ce8)

[Nice clear guide. On the invoice.payment\_failed TODO, one thing worth adding: check the invoice's next\_payment\_attempt. If it's set, Stripe will retry, so a gentle "please update your card" is enough. If it's null, that was the last try and the subscription is about to move to whatever your failed-payment settings say.](https://hashnode.com/posts/stripe-subscriptions-in-next-js-16-complete-saas-billing-guide-2026/6a3e12c5bd995b69263166fd/comment/6ae859cba4acd9000f3aee02)

Comment · Article · Nov 2 · [Stripe Subscriptions in Next.js 16: Complete SaaS Billing Guide (2026)](https://hashnode.com/posts/stripe-subscriptions-in-next-js-16-complete-saas-billing-guide-2026/6a3e12c5bd995b69263166fd)

[The "gate access on your database, not the redirect" point is the one that bites later. Worth deciding up front what access looks like while a subscription is past\_due: Stripe keeps retrying for a while, and cutting access on the first invoice.payment\_failed locks out people whose card would have cleared on the next attempt. Keeping access until the status actually moves to canceled or unpaid tends to be kinder and simpler.](https://hashnode.com/posts/stripe-webhooks-in-the-next-js-app-router-signature-checks-idempotency-and-the-bugs-ai-assistants-leave/6ae4a4e30d7ed2e767de6cb3/comment/6ae859b5807a1760b478c28b)

Comment · Article · Nov 2 · [Stripe webhooks in the Next.js App Router: signature checks, idempotency and the bugs AI assistants leave](https://hashnode.com/posts/stripe-webhooks-in-the-next-js-app-router-signature-checks-idempotency-and-the-bugs-ai-assistants-leave/6ae4a4e30d7ed2e767de6cb3)

[The point that a hold next to a real charge looks identical on a banking app is the one most people miss, so starting with the Payments status column is the right first move. One thing I'd add to fixing a real duplicate: when you cancel the extra subscription immediately, turn off proration on the cancel. Otherwise Stripe can leave a credit or a small final invoice on that customer, and the next email you get is "why is there another charge from you."](https://hashnode.com/posts/you-charged-me-twice-check-before-you-refund/6adefcfe55ca1b91884b612b/comment/6ae464a0a627c99f82de95d8)

Comment · Article · Oct 30 · ["You charged me twice." Find out which of four things happened before you refund.](https://hashnode.com/posts/you-charged-me-twice-check-before-you-refund/6adefcfe55ca1b91884b612b)

[Using the invoice ID as the primary key is a neat way to make duplicate invoice.paid events harmless. One path I'd add: a renewal whose payment fails. No invoice.paid means no new credit row, which is correct, but the user just wakes up with zero credits and no idea why, so a short note on invoice.payment\_failed with a card-update link saves you a confused support email.](https://hashnode.com/posts/credit-packs-subscriptions-in-one-wallet-expiring-credits-in-sqlite-without-a-cleanup-cron/6adf4b7dcaa30cf051499f2c/comment/6ae1b675110fec1240b36075)

Comment · Article · Oct 28 · [Credit packs + subscriptions in one wallet: expiring credits in SQLite without a cleanup cron](https://hashnode.com/posts/credit-packs-subscriptions-in-one-wallet-expiring-credits-in-sqlite-without-a-cleanup-cron/6adf4b7dcaa30cf051499f2c)

[Good call on not trusting the success page, since "cards get declined three days later" is exactly where tutorials stop. The step I'd add after storing past\_due is telling the customer: on invoice.payment\_failed, send an email with a link to update the card, then let access follow the status when Stripe finishes retrying.](https://hashnode.com/posts/stripe-subscriptions-with-supabase-and-next-js-the-parts-tutorials-skip/6ad47e095d327f3676375774/comment/6ae06b9582ca1520acc8e897)

Comment · Article · Oct 27 · [Stripe subscriptions with Supabase and Next.js: the parts tutorials skip](https://hashnode.com/posts/stripe-subscriptions-with-supabase-and-next-js-the-parts-tutorials-skip/6ad47e095d327f3676375774)
