Urgent.News

What's breaking now, across thousands of outlets.

Tech

Cancelled customers who keep access: the Stripe webhook bug nobody reports

If you take subscriptions through Stripe, you keep two copies of "who is paying": Stripe's, and a column in your own database ( subscribed , is_pro , plan ...) that your app actually checks. A webhook keeps them in sync. When the webhook misses an event, the two copies disagree. It fails in two directions: Paid, no access. Someone pays and stays locked out. They email you within the hour.…

Subscriptions on Stripe require two copies of information - one from Stripe and another in your own database. A webhook ensures these copies stay in sync. However, if the webhook misses an event, the two copies can become out of sync. This can lead to two scenarios: someone pays but doesn't get access, or someone cancels but still retains access.

The latter is the issue at hand, as it's the problem that's often overlooked. The issue stems from three main reasons. Firstly, the webhook only handles the "checkout.session.completed" event, granting access without revoking it. Secondly, when a customer cancels at period end, Stripe sends a "customer.subscription.updated" event with "cancel_at_period_end" set to true.

This payment isn't revoked until the period ends, which can lead to paying customers being locked out early. Thirdly, if the checkout success page sets "subscribed" to true, anyone who opens that URL can access the service, regardless of payment status. This can't be rectified by a cancellation webhook. The webhook may also fail entirely due to several reasons.

It could be pointing to the wrong route after a deploy, have a signature verification failure, or throw an error in the handler. Once the issue is identified, it's crucial to replay the failed events from the dashboard immediately, as Stripe retries failed deliveries for about three days before giving up. If the endpoint was down over a long weekend, for example, it could lose events permanently.

Additionally, refunds and disputes don't automatically cancel the subscription. If access should be ended, the webhook should be programmed to handle "charge.refunded" and "charge.dispute.created" events. The first step is to identify the affected customers. In Stripe, filter "Billing → Subscriptions" by "Canceled" and cross-reference these customers in your access table.

Any customer still having access is affected. The same principle applies to checking active subscribers. To automate this process, you can create a tool like "Entitled" that regularly compares Stripe and your own database, listing any discrepancies and suggesting fixes. A free scan is available. Regularly checking your webhook's event list is also recommended.

Written by urgent.news from Dev.to's reporting — not their text. Machine-written — may contain errors; check the original before relying on it.

Read the original at dev.to →

More in Tech

I Built a Support Agent That Remembers Customers Between Chats

A support agent can give the right answer and still make a customer repeat themselves. I built this system around a simple idea: use long-term memory to carry useful context between conversations, but…

  • Memory layer uses Hindsight with unique bank per customer for retrieval.
  • Retrieval is tailored to current query, preventing random reiteration of stored information.

We Didn’t Have a Product Problem. We Had a Memory Problem.

Building PRATHIDHWANI: A Memory-Powered Customer Feedback Intelligence System Using Hindsight A few months ago, I was sitting in a product discussion when someone asked a deceptively simple question…

  • Team discovered lack of clear memory of past decisions hindered addressing customer feedback
  • PRATHIDHWANI system connects customer feedback, product changes, and historical context
  • Dashboard provides visual overview of feedback trends and historical signals

How Many Tokens Is That Elasticsearch Hit? A Reproducible RAG Compression Benchmark

This is a follow-up to my earlier post introducing jtoken . That one was the pitch. This one is the actual measurement — the benchmark I run before claiming any savings number, and how you can run it…

  • jtoken compression library evaluated for token savings in RAG pipeline
  • Benchmark uses real tokenizers (tiktoken) to measure compression impact
  • jtoken achieves 19.1% reduction for MongoDB docs, 13.2% for nested API events

More from Tuesday 29 September →