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.
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.
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.
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.
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 pricingQuick reference
Bookmark this for next time you see a failure in your Stripe logs:
payment_intent.payment_failed-- one-time payment declined. Customer needs a different card.charge.failed-- charge-level failure. Checkfailure_codefor details.invoice.payment_failed-- subscription payment failed. Email the customer before you lose them.checkout.session.expired-- abandoned checkout. Normal in small numbers; investigate if the rate is high.