Stripe sends webhook events when things happen in your account -- successful payments, failed charges, expired checkouts. If you're running a no-code app, these events are how your app knows what's going on with payments.

When they fail, your app goes blind. Here's a plain-English breakdown of the four most important failure events, what causes each one, and what you should do about them.

payment_intent.payment_failed

What it means

A customer tried to pay and the payment was declined. The money didn't move.

Common causes

  • Insufficient funds. The customer's account doesn't have enough money. Most common cause by far.
  • Card expired. The card on file has passed its expiration date.
  • Fraud detection. The customer's bank flagged the transaction as potentially fraudulent. This happens more often with international payments.
  • Incorrect card details. Wrong CVC, wrong zip code, mismatched billing address.
  • 3D Secure failed. The customer was prompted for authentication (common with European cards) and either failed or abandoned it.

What to do

For one-time payments: the customer needs to try a different card or contact their bank. You can't fix this on your end, but you can reach out proactively ("Hey, looks like your payment didn't go through -- want help sorting it out?"). That personal touch often saves the sale.

For subscriptions: Stripe's Smart Retries will automatically attempt the charge again. If retries keep failing, consider sending a manual email before the subscription cancels.

charge.failed

What it means

Similar to a failed payment intent, but this event fires at the charge level. You'll often see both events for the same failed payment.

Common causes

  • All the same reasons as payment_intent.payment_failed -- declined cards, expired cards, fraud blocks.
  • Processing errors. Occasionally Stripe or the card network has a processing issue. These are rare and usually resolve on retry.
  • Currency mismatch. The customer's card doesn't support the currency you're charging in.

What to do

Same approach. Check the failure_code and failure_message in the event payload -- they tell you exactly why the charge failed. Common codes: card_declined, expired_card, insufficient_funds, incorrect_cvc.

invoice.payment_failed

What it means

A subscription invoice couldn't be paid. This is the event that fires when a recurring payment fails -- your customer's monthly or annual charge didn't go through.

Common causes

  • Card expired since signup. Customer signed up 8 months ago. Their card expired. They didn't update it.
  • Card replaced by bank. Banks sometimes issue new cards (new number) due to a data breach. The old card on file stops working.
  • Spending limit hit. Customer's card has a monthly spending limit.

What to do

This is the most important one to act on, because it means you're about to lose a paying customer. Stripe will retry (you can configure retry rules in your billing settings), but if retries fail, the subscription enters "past due" and eventually cancels.

Best move: Email the customer immediately. "Hey, your payment didn't go through. You can update your card here: [billing portal link]." Most customers appreciate the heads-up and fix it within a day.

checkout.session.expired

What it means

A customer started your checkout flow but never completed it. The session timed out (default: 24 hours).

Common causes

  • Customer got distracted. Opened checkout, got a phone call, never came back. This is the most common reason.
  • Price shock. They saw the final price (with tax, shipping, etc.) and decided not to buy.
  • Checkout was confusing. Too many fields, unclear what they're paying for, or the page didn't load properly.
  • Technical issue. The checkout page had an error, or 3D Secure didn't load, or the customer's browser blocked a required script.

What to do

A few expired checkouts are normal. If you're seeing a high rate of expirations compared to completions, something is wrong with your checkout flow. Test it yourself. Try completing a purchase on mobile. Check if the page loads cleanly without ad blockers interfering.

Why you're probably not seeing these events

Here's the catch: Stripe sends all these events, but nobody is listening.

If you built your app on Bubble, Lovable, or another no-code platform, your Stripe integration probably only handles the happy path -- checkout.session.completed, invoice.paid. The failure events are sent by Stripe, but if your app doesn't have a webhook endpoint subscribed to them, they're ignored.

The Stripe dashboard shows these events in your logs, but you have to go look. If you're a solo founder with 50 other things to do, you're not checking your Stripe event logs every day.

Get alerted the moment a Stripe payment fails

Upmend subscribes to these failure events for you and sends you a plain-English alert with what went wrong and how to fix it.

See pricing

Quick reference

Bookmark this for next time you see a failure in your Stripe logs: