A customer completes checkout. Stripe shows a successful payment, but the WooCommerce order is still Pending payment, On hold, or Failed.
The tempting response is to change the order status immediately. That can create a second problem: fulfillment may begin before you have confirmed the exact payment, amount, currency, refund state, and customer order.
This checklist keeps diagnosis separate from state-changing actions.
First: treat both systems as evidence
Neither a WooCommerce status label nor a Stripe dashboard row tells the whole story on its own.
Before changing anything, record:
- the WooCommerce order status and timestamps;
- the exact Stripe PaymentIntent or charge reference stored on the order;
- the Stripe object's current status;
- gross amount and currency in both systems;
- refunded total in both systems;
- relevant WooCommerce order notes;
- Stripe event and webhook-delivery timestamps.
Do not paste customer data, card data, API keys, webhook secrets, or full payment identifiers into a public support thread.
1. Confirm that you are comparing the same payment
A status comparison is useful only when both records refer to the same transaction.
Check that:
- the Stripe object ID on the order resolves to the object you are viewing;
- the WooCommerce gateway and the Stripe object use the same test or live mode;
- the store is connected to the same Stripe account that originally created the payment;
- a retry or a second checkout attempt did not create a newer PaymentIntent.
If the reference is missing or points to another account or mode, stop there. The provider read is not authoritative for that order until the identity problem is resolved.
2. Allow for asynchronous updates
WooCommerce payment updates are not always synchronous with the browser returning from checkout. Webhooks, redirects, background actions, and retries can complete at different times.
Use a short grace period before calling a status difference an incident. Five minutes is a practical starting point for an automated comparison, but your payment methods and checkout flow may need a different threshold.
The goal is to avoid alerting on a normal, temporary transition while still catching an order that remains inconsistent.
3. Inspect the full webhook path
In Stripe, find the event related to the exact PaymentIntent or charge and review its delivery history.
Then check:
- whether Stripe sent the event to the expected endpoint;
- the response code and response time;
- whether a retry later succeeded;
- whether a proxy, WAF, cache, maintenance mode, or security rule could intercept the request;
- the WooCommerce Stripe logs at the same timestamp;
- PHP and server logs for an exception after the endpoint accepted the request.
An HTTP 2xx response is useful evidence, but it does not by itself prove that every downstream order update finished successfully.
4. Compare normalized financial values
Do not compare formatted strings such as $149.00 and 149.
Normalize the values first:
- convert the WooCommerce order total to the currency's smallest unit;
- compare it with the authoritative Stripe amount for the exact object;
- compare lowercase ISO currency codes;
- compare the total refunded amount only when the Stripe response provides authoritative refund data.
Amount and currency differences deserve immediate investigation. They do not automatically prove that Stripe charged the wrong amount; manual edits, unusual capture flows, retries, and integration failures can also produce data drift.
5. Distinguish the common disagreement patterns
Stripe succeeded; WooCommerce remains unpaid
Verify the exact successful provider object, then inspect order notes, webhook delivery, gateway logs, and any exception between webhook receipt and the order-status write.
WooCommerce says paid; Stripe reports a terminal failure
Look for a later retry or a replacement PaymentIntent before concluding that the order is unpaid. A stale reference can make a correct order look wrong.
Refund totals differ
Confirm whether the refund happened in WooCommerce, directly in Stripe, or through another integration. Check partial refunds and whether both systems have processed the latest update.
No usable Stripe reference
Treat this as an identity problem, not a payment-status conclusion. Investigate how the order was created and whether another gateway or extension stores identifiers differently.
6. Automate comparison, not remediation
A safe monitor can run a bounded, read-only scan over recent Stripe orders and report:
- status disagreements after a grace period;
- gross amount or currency mismatches;
- refund-total mismatches;
- stale pending orders;
- missing or unreadable Stripe references.
Keep the first version read-only. Automatically changing order state, replaying webhooks, capturing funds, or issuing refunds turns a visibility tool into a financial-action system with a much larger failure surface.
7. Decide the action only after identifying the cause
A mismatch is a prompt to investigate, not a repair instruction.
Before fulfillment or a financial change, confirm:
- which exact payment belongs to the order;
- whether the customer was charged and for what amount;
- whether a newer event or retry exists;
- whether the order notes and logs support the intended state;
- whether another integration will repeat or reverse your manual action.
Document the evidence and the action taken so the next incident is easier to diagnose.
A free read-only implementation
Disclosure: I maintain Payment Truth for WooCommerce, a GPL-licensed plugin that implements this bounded comparison for stores using the official WooCommerce Stripe Gateway. It uses the gateway's existing connection, stores no additional Stripe secret key, and never moves money or changes an order.
We are currently looking for five store owners, maintainers, or agencies to run one privacy-safe scan and share candid feedback, including healthy or empty results. The ten-minute validation steps explain exactly what to share and what never to post publicly.
Top comments (1)
This is a solid approach because payment reconciliation should start as an evidence problem, not an order-status problem.
The strongest point for me is keeping the first layer completely read-only. A monitor that verifies PaymentIntent identity, live/test mode, amount, currency, refunds, webhook delivery, and timing can surface inconsistencies without introducing a second financial failure.
I also like the distinction between “Stripe says succeeded” and “this exact Stripe payment belongs to this exact WooCommerce order.” That identity check is easy to overlook, especially when retries or multiple PaymentIntents are involved.
The webhook section is important too: a successful HTTP response only tells you the endpoint accepted the request. It doesn't necessarily prove the entire downstream order update completed successfully.
This is a good pattern for payment automation generally: observe → correlate → verify → act, rather than automatically repairing every discrepancy.
I’d be interested in discussing edge cases around reconciliation and payment integrations. Feel free to reach me on Tg_coolsoftDev.