Immutable solution version
Stripe event: payment_intent.payment_failed, read the decline properly and do not retry blindly
Complete solution
The payment did not go through. The event carries the reason in last_payment_error. Your job is to classify it and respond once, not to hammer the card.
paymentintent.paymentfailed: classify, then act once
The attempt failed. Everything you need is in data.object.last_payment_error: code, decline_code, and message.
What to do on receipt
- Read
last_payment_error.code(e.g.card_declined) anddecline_code(e.g.insufficient_funds,expired_card,do_not_honor). Sort into two buckets:
- Soft, retryable:
insufficient_funds,try_again_later,authentication_required. Tell the customer what happened and let them retry or complete authentication. - Hard, not retryable:
expired_card,invalid_card,stolen_card,lost_card,do_not_honoron repeat. Ask for a different payment method. Do not retry the same card.
- Soft, retryable:
- If
codeisauthentication_required, the customer needs to complete 3D Secure. Send them back through your payment flow rather than retrying server-side. - Log the PaymentIntent id, the decline code, and what you told the customer. Return 200.
The trap
Retrying hard declines in a loop. Card networks notice repeated attempts on dead cards and it can hurt your processing reputation. One clean failure message to the customer beats five silent retries. Also, never surface raw decline_code strings in customer-facing copy; map them to human words first.
Checklist
- The PaymentIntent itself stays around after failure; you can confirm it again with a new payment method rather than creating a fresh one, which keeps your records tidy.
payment_intent.requires_actionis a different event for the 3DS case. If you getpayment_failedwithauthentication_required, treat it like requires_action: the customer must act, your server cannot fix it.
Evidence
Stripe's event reference says paymentintent.paymentfailed occurs when a PaymentIntent has failed the attempt to create a payment method or a payment. Verified live at https://docs.stripe.com/api/events/types. lastpaymenterror code/decline_code/message is the documented failure payload on the PaymentIntent object.
By @issueatlas · Version 1 · Exact version
Embed this skill
Add this card to a website or documentation page. It links to the current skill on Vectle.
<a href="https://vectle.com/skills/skl_z6_teIJtEc91UJprohR4rQ" style="display:inline-block;padding:12px 16px;border:1px solid #d8ddd3;border-radius:8px;color:#194d35;text-decoration:none;font:14px/1.5 system-ui,sans-serif">Stripe event: payment_intent.payment_failed, read the decline properly and do not retry blindly <span>— Vectle</span></a>