You open WooCommerce in the morning, scan new orders, and one status keeps staring back at you: Failed. The customer reached checkout. They trusted the site enough to enter payment details. Then something broke at the last step.
That moment isn't just a payment error. It's a sales recovery problem, a support problem, and a UX problem sitting in one line item.
For Divi and WooCommerce stores, Stripe decline codes are where those problems become actionable. If you read them correctly, you can decide whether to retry, ask for updated details, surface a better checkout message, or stop wasting attempts on a card that will never work. If you ignore them, you leave money on the table and train customers to abandon the cart the second anything feels uncertain.
That matters even more if you sell subscriptions, memberships, or repeat-purchase products. One failed payment can interrupt future revenue, not just a single order. If that side of the business is part of your model, this guide to recurring revenue for creators is a useful companion read because it frames why retention and recovery matter as much as acquisition.
Why Every Failed Payment Is a Call to Action
A failed order rarely starts as a technical mystery. It usually starts as a customer experience failure with technical evidence attached.
A shopper clicks Place order, waits, sees a vague decline notice, and has no idea what to do next. Some try again immediately. Some switch cards. Some leave. In WooCommerce, the default result often gives you too little context for the customer and not enough structure for your team.
What the failed order is really telling you
Every decline says one of four things:
- Retry might work: The payment failed for a temporary reason.
- Customer input is wrong: Something like card details or authentication needs attention.
- The card can't be used: A replacement method is needed.
- Your checkout flow needs help: The message shown to the customer isn't guiding the next step.
That last point gets missed all the time. Store owners focus on Stripe logs and forget that the customer only sees confusion.
A failed payment is only final when both the payment system and the storefront stop helping.
Why WooCommerce stores feel this pain more sharply
With WooCommerce, you're often running a stack of moving parts: theme, plugins, checkout customizations, payment gateway settings, email flows, maybe a subscription extension, and a lot of business logic layered on top. When Stripe returns a decline code, the raw signal is useful. The storefront response often isn't.
That's why Stripe decline codes matter so much in a Divi environment. They let you turn an unhelpful failure into a specific response. Retry later. Re-enter CVC. Complete authentication. Try another card. Contact the bank. Update expiry date. Those are very different experiences, and they shouldn't all collapse into one red notice at checkout.
Soft Declines vs Hard Declines The First Thing to Know
If you only remember one thing about Stripe decline codes, remember this: some declines are temporary, and some are final. Your whole recovery strategy depends on knowing which is which.

What a soft decline means
A soft decline means the card isn't necessarily bad. The transaction was rejected for a reason that may clear with time, customer action, or a cleaner retry path.
Common examples include:
- Do not honor
- Insufficient funds
- Authentication required
- Issuer-side temporary blocks
Think of a soft decline as "not right now."
That doesn't mean you should hammer the gateway with retries. It means the transaction might still be recoverable if you choose the right next step. Sometimes that step is a short delay. Sometimes it's prompting the customer to authenticate. Sometimes it's asking them to try again later or use another card.
What a hard decline means
A hard decline means the payment method shouldn't be retried for that transaction path. The card may be expired, invalid, reported lost, reported stolen, or otherwise unusable.
Think of a hard decline as "this card won't work for this purchase."
In those cases, retries usually create noise, frustrate the customer, and can put you on the wrong side of card network rules if you keep pushing. Your job shifts from recovery through automation to recovery through clear guidance.
The practical rule for WooCommerce teams
Here is the simplest working rule:
- Soft decline: Retry carefully or guide the customer through a specific fix.
- Hard decline: Stop retries and ask for a different payment method or corrected details.
Practical rule: Never build one generic automation for all failed payments. That setup looks efficient in the admin but performs badly at checkout.
How this changes your checkout UX
Once you split declines into soft and hard, the messaging gets easier:
| Situation | What you should show |
|---|---|
| Soft decline | “Your bank didn't approve this charge right now. Please try again shortly or use a different card.” |
| Authentication issue | “Your card needs additional verification. Please complete authentication to finish checkout.” |
| Expired card | “This card appears to be expired. Please update the expiry date or use another card.” |
| Invalid CVC | “The security code didn't match. Please re-enter the CVC and try again.” |
That difference is the foundation for everything else in this guide. Without it, retries become guesswork and customer notices become generic dead ends.
Quick Reference for Common Stripe Decline Codes
When a payment fails, you need a fast lookup, not a long theory lesson. The table below covers the Stripe decline codes WooCommerce store owners run into most often and the immediate action that usually makes sense.
The two decline codes that appear most frequently in B2C e-commerce are 05 (Do Not Honor) and 51 (Insufficient Funds). They are retryable, but retrying has limits. Visa permits 15 attempts and Mastercard allows 35 attempts within a 30-day window, with potential fines up to $15,000 if merchants exceed the rules, according to this Stripe decline code reference from Redux Payments.
Common Stripe Decline Codes and Actions
| Decline Code | Plain English Meaning | Type | Recommended Action |
|---|---|---|---|
| 05 / do_not_honor | The issuer rejected the payment without giving a clear reason | Soft | Retry once later, then ask the customer to contact their bank or use another card |
| 51 / insufficient_funds | The card account didn't have enough available funds | Soft | Retry later rather than immediately, or ask the customer to use a different payment method |
| 65 | Credit limit exceeded | Hard for immediate checkout purposes | Ask for another card or ask the customer to contact their issuer |
| 54 | Card expired | Hard | Prompt the customer to update the expiry date or use a different card |
| 97 | Invalid CCV | Hard until corrected | Ask the customer to re-enter the security code carefully |
| incorrect_cvc | The CVC entered doesn't match the issuer record | Hard until corrected | Keep the customer on the payment form and ask for the correct CVC |
| authentication_required | The card requires customer authentication to complete payment | Soft with customer action required | Trigger the authentication flow and explain that checkout isn't complete yet |
| generic_decline | The issuer or risk system declined the transaction without a specific reason | Soft | Treat it as potentially recoverable, avoid rapid-fire retries, and guide the customer if a retry fails again |
What to do with this table in practice
Don't just read the code and move on. Tie it to a real action inside your WooCommerce flow:
- Checkout notice: Show a message that matches the likely cause.
- Order note: Store the decline code on the order for support and reporting.
- Customer email: If the order remains unpaid, send a follow-up based on the code category.
- Retry policy: Limit retries to soft declines and keep them within network rules.
If you're building for a client, this is one of those places where a small amount of implementation detail creates a noticeable business result. The store feels less broken, support tickets get more specific, and customers get a next step instead of a dead end.
A Deeper Dive into Decline Code Categories
Single codes are useful. Patterns are better.
If several failed orders share the same category of decline, the problem usually isn't random. It's tied to issuer behavior, customer input, fraud controls, or authentication flow. Looking at decline codes by category helps you decide whether to tweak checkout UX, payment settings, or back-end handling.
Issuer declines
Issuer declines come directly from the cardholder's bank. The issuer approved neither the timing nor the risk profile of the transaction, and sometimes it gives very little detail.
This category includes codes like do_not_honor and generic_decline. These are frustrating because they don't tell you exactly what happened. Still, they point to a real pattern: the bank saw something it didn't like, or the issuer returned a broad rejection without context.
What usually works:
- Space out retries for soft issuer declines.
- Avoid showing overly technical messages to the customer.
- Offer a backup payment method when the same customer fails again.
- Watch for clusters by country, card brand, or time of day.
What doesn't work is treating every issuer decline as fraud or every issuer decline as a final no. Both assumptions are expensive.
Card detail and validity errors
Some Stripe decline codes are straightforward. The customer entered the wrong data, or the stored card data is no longer usable.
Typical examples include:
- incorrect_cvc
- invalid CCV
- expired card
These are the easiest declines to improve with front-end UX. A better inline notice at checkout often fixes the issue without any support involvement. If the customer can correct the error immediately, don't push them into a generic failed order flow.
A good checkout experience keeps the form in place, preserves as much entered data as possible, and highlights the field that needs attention. A bad one dumps the user on a generic error notice with no path back.
When the problem is a typo, the fastest revenue recovery tool is a better form state, not a smarter dunning email.
Fraud and risk blocks
Some declines happen because Stripe Radar or the issuer's risk systems block the transaction. That's a different situation from an expired card or missing funds.
These cases require caution. If the payment was blocked for risk reasons, you shouldn't keep retrying automatically. Instead:
- Review whether your fraud rules are too aggressive.
- Check whether legitimate customers from certain locations are being blocked more often.
- Offer an alternative payment path when appropriate.
- Make sure support can recognize the pattern quickly.
The biggest mistake here is blunt-force automation. A retry script can't solve a fraud model mismatch.
Authentication and SCA-related issues
When you see authentication_required, the cardholder hasn't completed the extra verification needed for the transaction. That's not a card failure in the traditional sense. It's a flow failure.
For WooCommerce stores, these are often caused by:
- A customer closing the browser too early
- Poorly surfaced 3D Secure prompts
- Mobile friction in the redirect or modal flow
- Theme or plugin conflicts that interrupt checkout scripts
How to use categories operationally
A practical setup in WooCommerce is to map categories to teams and actions:
| Category | Store action |
|---|---|
| Issuer decline | Retry policy plus customer-friendly follow-up |
| Card detail error | Keep customer at checkout and prompt correction |
| Fraud or risk block | Review rules and route to support if needed |
| Authentication required | Re-open or re-trigger the auth flow |
That turns Stripe decline codes from log noise into something you can route, automate, and improve over time.
Building a Smart Retry Strategy to Recover Revenue
Too many stores have one retry policy: charge again soon and hope for the best. That's not a strategy. It's just repetition.
A better approach starts with one correction. Generic declines are not always permanent failures. Many merchants still treat them that way, even though 2025 case studies show they're often soft declines. A staggered retry logic over 24 to 72 hours can recover up to 28% of these transactions, while 45% of global e-commerce merchants still fail to implement that kind of strategy, according to this analysis of Stripe decline handling from Flycode.

What smart retries actually do
A smart retry strategy changes timing, eligibility, and customer communication.
It doesn't retry every failure. It retries the right failures. It doesn't notify the customer every time a soft decline happens. It only pulls the customer in when their action improves the odds of recovery.
A practical retry framework for WooCommerce
Use this as a working model:
Classify the code first
Retry only soft declines. Stop immediately on hard declines.Delay the second attempt
For insufficient funds or generic issuer declines, an immediate second attempt often adds no value.Set a maximum retry count
Your logic must respect card network retry limits and avoid turning one failed order into a compliance problem.Escalate the message, not just the retry
If a soft decline repeats, switch from silent retry to a customer-facing prompt.Offer a path out
Always give the shopper another way to complete the order, such as another card or another payment method.
What works and what doesn't
Here is the difference in plain terms.
| Approach | Result |
|---|---|
| Immediate repeated retries | Annoys issuers, wastes attempts, confuses customers |
| Staggered retries over time | Better chance of catching temporary issues after they clear |
| Same message for every decline | Creates uncertainty and abandonment |
| Code-aware notices and emails | Helps customers take the right next step |
Key takeaway: A retry should have a reason. If nothing has changed since the last attempt, the next attempt probably won't change the outcome either.
Where this fits with checkout recovery
Retry logic handles customers who leave after failure or who never complete a corrected payment on the spot. But checkout UX still matters. If your page shows a dead-end error, you'll lose shoppers before any retry automation gets a chance to work.
That overlap is where cart recovery and payment recovery meet. If you already work on checkout optimization, this guide on how to reduce cart abandonment fits naturally into the same workflow because the friction often starts before the second payment attempt ever happens.
Rules worth enforcing in code
For most WooCommerce builds, these guardrails are sensible:
- Retry only soft declines: Don't re-attempt lost, stolen, expired, or clearly invalid cards automatically.
- Track attempts per order and payment method: You need visibility before you need sophistication.
- Stop after repeated failure: At that point, customer action is usually more useful than another background charge.
- Store the last decline code: It should drive the next message your system sends.
A good retry strategy feels calm from the customer's perspective. The store doesn't panic, doesn't spam, and doesn't keep making the same request without a better reason.
Creating Clear Decline Messages with Divi Areas Pro
The default WooCommerce decline message usually tells the customer what happened in the vaguest possible way. "Your card was declined" isn't guidance. It's a shrug.
You can do better by pairing Stripe decline codes with targeted on-page messaging. Instead of forcing the customer to guess, you can show the next best action right where the friction happens.

Replace vague notices with code-aware guidance
A strong decline message does three things:
- Names the issue in plain English
- Tells the customer what to do next
- Keeps them close to conversion
Compare these two examples:
“Your card was declined.”
Versus:
“Your bank didn't approve this payment right now. Please try again shortly, or use another card if you need to complete the order now.”
The second one reduces uncertainty. It doesn't over-explain. It doesn't expose sensitive details. It gives the customer momentum.
Useful message patterns for common declines
You don't need dozens of templates. Start with a few solid ones.
| Decline scenario | Better customer message |
|---|---|
| Insufficient funds | “This card doesn't appear to have enough available funds right now. Please try again later or use a different card.” |
| Expired card | “This card looks expired. Please update the expiration date or use another payment method.” |
| Incorrect CVC | “The security code didn't match. Please check the CVC and try again.” |
| Authentication required | “Your bank requires an extra verification step. Please complete authentication to finish checkout.” |
| Generic decline | “Your bank didn't approve this payment right now. Please try again shortly or use another card.” |
How to use popups and inline content effectively
For Divi builders, the practical move is to trigger contextual help instead of generic interruption. You can show a popup, slide-in, or inline content block that appears when checkout returns a known failure state.
A good implementation usually looks like this:
- Capture the decline reason from the Stripe or WooCommerce error context.
- Map that reason to a message variant.
- Display a targeted content area instead of a generic site-wide alert.
- Include one clear action, such as retry, update details, or choose another method.
If you're building those targeted experiences inside a Divi site, this tutorial on how to display content using Divi Areas Pro is useful because it shows the mechanics of getting condition-based content into the right place.
UX choices that help instead of interrupt
A few practical rules make these messages perform better:
- Keep the customer on the checkout page: Don't bounce them to a separate account page unless they must update a stored method.
- Preserve form state: Re-entering billing details is often what pushes a frustrated buyer to quit.
- Use inline help for field-level errors: CVC and expiry problems don't need a modal.
- Use popups for decision points: Generic declines and repeated failures benefit from a clear recovery choice.
This is one of those areas where design and payment logic need to cooperate. The best payment handling isn't just technically correct. It helps the customer recover without feeling blamed or blocked.
Server-Side Handling for WooCommerce Developers
Front-end messaging helps in the moment. Server-side handling is what keeps your recovery process consistent after the customer leaves the page.
If you're responsible for the build, treat every decline as a structured event. That means logging it, tagging it, and using it to trigger the next step automatically. When you don't, support has to reconstruct what happened from scattered order notes and partial payment records.
Log the right details every time
At minimum, store these values when a payment fails:
- Order ID
- Customer ID or email
- Stripe decline code
- Timestamp
- Payment intent or charge reference
- Order total
- Whether the failure happened at checkout or on a renewal attempt
Those fields let you answer useful questions later. Are the same users failing repeatedly? Is one decline code dominating? Are renewal failures different from first-purchase failures? You don't need advanced reporting on day one, but you do need clean raw data.
Use webhooks instead of relying on the checkout session
The checkout page is a weak place to base all your logic. Users close tabs, mobile sessions break, and JavaScript errors happen. Server-side webhook handling is more reliable because Stripe sends the event whether the customer sticks around or not.
For WooCommerce developers, the key pattern is straightforward:
- Listen for failed payment events in your Stripe integration.
- Parse the decline code from the event payload.
- Update order metadata so support and automation can act on it.
- Trigger the right response based on code category.
Those responses can include:
- Emailing the customer with a code-aware explanation
- Tagging the user account for follow-up
- Creating an admin task for high-value failed orders
- Sending a Slack or internal notification when specific failure types spike
Build your logic in a maintainable place
Avoid stuffing all payment-failure logic into theme files or scattered snippets. If the store has any complexity, move this behavior into a small custom plugin or a dedicated integration layer. That keeps the logic portable, testable, and less likely to break during theme updates.
If you need a blueprint for that kind of setup, this overview of custom WordPress plugin development is a good reference point for separating business logic from presentation.
The store owner shouldn't need to inspect Stripe manually to understand why an order failed. Your system should already know and act on it.
What to automate first
Start with the smallest useful automations:
| Trigger | Action |
|---|---|
| Incorrect CVC or expired card | Send a payment update email |
| Authentication required | Send a completion reminder quickly |
| Soft issuer decline | Queue a retry and hold customer messaging unless failure repeats |
| Repeated hard decline | Flag for support or request a new card immediately |
That gets you out of reactive mode without overengineering the stack. Once the workflow is reliable, you can add reporting, segmentation, and more refined retry windows.
Proactive Strategies to Reduce Your Decline Rate
Recovery matters. Prevention matters more.
If your store sees frequent declines, the answer usually isn't just "improve retries." Retries solve temporary failures. They don't fix structural problems in how payments are routed, authenticated, or presented to customers.
Fix the infrastructure issues first
For global merchants, up to 40% of cross-border declines are caused by a lack of local acquiring, and McKinsey data shows that using local acquiring networks can cut these declines by 35%. That's why retry-only advice misses the bigger issue for stores selling internationally. In many cases, the smarter long-term move is to reduce the chance of the decline happening at all.
If you serve customers across regions, review these areas:
- Local acquiring availability: International transactions can trigger fraud filters more easily when they're processed far from the customer's market.
- Local payment methods: Giving customers options beyond cards can lower friction and bypass avoidable card failures.
- Authentication flow quality: If SCA or 3D Secure feels broken, approvals suffer before the bank is ever fully the problem.
Offer payment methods that fit the customer
A checkout that only supports one card path forces every buyer into the same risk and approval funnel. That's fine for some stores. It's weak for international or recurring-payment businesses.
Where relevant, offer local methods such as SEPA, iDEAL, or Boleto instead of asking every customer to pass through the same card logic. This is especially valuable when you already see a pattern of cross-border generic declines.
Reduce avoidable billing failures
Some decline prevention is much less technical:
- Ask customers to keep payment details current
- Remind them before renewal dates
- Make payment update links easy to find
- Use clear billing descriptors so charges are recognizable
This matters a lot for organizations with repeat billing models. If you're looking at recurring collections outside traditional e-commerce, this piece on automated payments for sports clubs is a useful example of how predictable billing operations and member-friendly payment systems reduce admin friction before failed payments pile up.
The practical mindset shift
Most stores react to decline codes only after they show up. Better stores treat them as feedback on payment design.
That means asking harder questions:
- Are legitimate international customers being pushed through the wrong acquiring setup?
- Are we relying too heavily on cards when local methods would fit better?
- Are we making it too hard to update payment details?
- Are our renewal and checkout flows clear enough before the payment even runs?
If you fix those inputs, decline handling gets easier because fewer orders enter the failure path to begin with.
Frequently Asked Questions About Stripe Declines
Can I safely retry every declined Stripe payment
No. Some Stripe decline codes are soft declines and may recover. Others are hard declines and shouldn't be retried automatically. If you retry everything, you annoy customers, waste attempts, and can create compliance risk with card network rules.
What does generic_decline usually mean in practice
It means the payment was rejected without a specific reason from the issuer, or the payment was blocked by risk controls. In practical WooCommerce terms, treat it as a recoverable signal first, not an automatic dead end.
Should customers always be told to contact their bank
Not immediately in every case. If the decline is temporary or likely tied to timing, a careful retry or a prompt to try another card may be the better first move. Telling everyone to call their bank creates unnecessary friction.
What's the best thing to show on the checkout page after a decline
Show a message tied to the likely problem and the next action. "Update expiry date," "complete authentication," and "try another card" are useful. "Your card was declined" isn't.
Where should WooCommerce store the decline information
Store it in order metadata and logs that support staff can access without digging through Stripe manually. If you can tie it to automated emails, retries, and support alerts, even better.
Do Stripe decline codes matter if I already have subscription dunning
Yes. Dunning without code-aware handling is generic by design. Stripe decline codes tell you whether to retry automatically, request a new card, or guide the customer through a very specific fix.
If you're building Divi stores and want better control over checkout messaging, popups, targeted content, and WooCommerce user flows, Divimode is worth exploring. Its tools help you turn vague failure moments into guided recovery experiences that feel clearer for shoppers and easier to manage for developers.