WooCommerce stores the sale as an order. Stripe settles the money as a batch. That is why order #1842 seems to disappear the moment you open a payout log: the order reference lives on the storefront side, while the payout rows are organized around charges, refunds, fees, and payout IDs.
Matching WooCommerce order references to Stripe payout logs becomes reliable when you stop looking for one shared column and build the bridge in the right sequence: WooCommerce order reference -> Stripe payment identifier -> Stripe payout row. Once that bridge exists, the mismatch stops feeling random.
Why the order reference does not appear where you expect
The first mistake is assuming the WooCommerce order number should appear directly inside the payout summary. In most cases it does not. The payout log is not an order ledger. It is a settlement ledger.
| What you see in WooCommerce | What you see in Stripe | What each field actually represents | Why a raw match fails |
|---|---|---|---|
Order #1842 | po_1Q8... | One order versus one payout batch | A single payout can contain many orders |
Transaction ID ch_3Nk... | Source ID ch_3Nk... | The same card charge | This is usually the real bridge key, not the order number |
Refund on order #1842 | re_1AB... | A later reversal of the original payment | Refund rows do not reuse the order number |
Order paid date 2026-05-06 | Payout date 2026-05-08 | Sale date versus settlement date | The cash event happened later |
Order total 124.00 | Net impact 119.82 | Gross customer payment versus net cash after fees | The payout row is lower on purpose |
That table explains most failed reconciliations.
The WooCommerce order reference tells you which sale you are dealing with. The Stripe payout log tells you how money moved through Stripe and into a payout. Those are connected, but they are not stored at the same level.
Do not match the order directly to the payout ID
The payout ID is too far downstream to be your first match key.
One payout can include:
- dozens of WooCommerce orders
- one or more refunds from earlier days
- Stripe fee rows
- dispute or adjustment rows
- transactions that settled together even though the orders were created on different dates
That means the payout ID answers a bank-level question: which group of Stripe activity became one deposit? It does not answer the order-level question: which WooCommerce order created this charge?
To match WooCommerce references reliably, split the Stripe export by row type before you try to join anything:
| Stripe row type | Should it map back to a WooCommerce order? | How to use it |
|---|---|---|
charge | Yes | Match it to the WooCommerce payment record |
refund | Yes, but indirectly | Attach it back to the original order as a later reversal |
stripe_fee | No | Use it to explain the gross-to-net difference |
dispute or adjustment | Sometimes | Treat it as an exception or downstream reduction |
payout | No | Use it only for the batch and bank-level match |
This is the step most guides skip. They explain what a Stripe payout is, or where Stripe IDs live inside WooCommerce, but they do not tell you that some payout rows should never be forced to match an order at all.
Build the bridge table before you reconcile
If you try to solve this with one giant spreadsheet tab, the logic gets muddy fast. A bridge table is cleaner.
Pull these fields from WooCommerce first:
| WooCommerce field | Why you need it |
|---|---|
| Order number or order name | The business-facing reference you care about |
| Paid date and time | Narrows the Stripe search window |
| Order total | Useful supporting evidence, never the only key |
| Refund status | Explains later negative rows |
| Payment method | Confirms the order belongs in the Stripe set |
| Transaction ID if present | Usually the strongest bridge into Stripe |
| Customer email | Last-resort support evidence |
Then pull these fields from Stripe transaction or payout detail:
| Stripe field | Why you need it |
|---|---|
| Payout ID | Tells you which settlement batch the row belongs to |
| Balance transaction type | Separates charges, refunds, fees, and adjustments |
| Source ID | The object-level reference such as ch_ or re_ |
| Created date | Shows when the underlying Stripe event happened |
| Available or payout date | Shows when funds moved toward settlement |
| Gross, fee, and net | Explains why cash differs from order total |
| Currency | Catches false mismatches in multi-currency setups |
| Description or metadata fields | Useful when the order reference was passed through |
Your working bridge table should look more like this:
| Woo order reference | Woo transaction ID | Stripe source ID | Stripe row type | Payout ID | Status |
|---|---|---|---|---|---|
#1842 | ch_3Nk... | ch_3Nk... | charge | po_1Q8... | Matched |
#1843 | ch_3Nl... | ch_3Nl... | charge | po_1Q8... | Matched |
#1842 | ch_3Nk... | re_1AB... | refund | po_1Q9... | Refunded later |
| — | — | fee_... or fee line | stripe_fee | po_1Q8... | Explanation only |
That table does two important things. It proves which Stripe object belongs to which WooCommerce order. It also makes clear which Stripe rows are not supposed to point back to a WooCommerce order reference.
Where to find the Stripe identifier when WooCommerce does not export it cleanly
This is the part that usually decides whether the job takes twenty minutes or two hours.
If the WooCommerce export already includes the Stripe transaction ID, use it. That is the cleanest route. But many exports do not expose it in a convenient column, so you need fallback evidence.
Use the sources in this order:
| Evidence source | What you can recover | Reliability |
|---|---|---|
| Order details screen | Transaction or payment identifier linked to Stripe | High |
| Order notes | Charge, refund, or payment lifecycle IDs written during processing | High when present |
| WooCommerce Stripe logs | Order number beside ch_, pi_, cus_, or src_ values | High |
| Customer email + exact amount + time window | Likely Stripe charge candidate | Medium |
| Order total alone | Possible candidate only | Low |
If the order notes are empty, do not assume the payment never existed. The order can still be real while the metadata written back from Stripe is incomplete. That is why the logs matter. In practice, the log often gives you the crucial pair you need: WooCommerce order number on one side, Stripe charge or payment intent on the other.
Once you recover that Stripe-side identifier, the rest of the match becomes much more stable. Search the Stripe export for the ch_ or pi_ value, confirm the amount and date, then capture the payout ID attached to that row.
The workflow that holds up when the payout includes real noise
This is the sequence that avoids most false mismatches.
- Match the payout batch to the bank first. The payout log belongs to a cash-settlement workflow, so confirm which payout actually landed before you start tracing individual orders. If that layer is still unclear, start with how to match a Stripe payout CSV to your bank statement.
- Open the Stripe rows inside that one payout. Separate
charge,refund,stripe_fee, and any adjustment rows before you attempt to join them to WooCommerce. - Match
chargerows back to WooCommerce using the Stripe transaction ID or payment intent. This is the real order-to-Stripe bridge. - Match
refundrows back to the original WooCommerce order as a later reversal. Do not treat negative payout rows as missing sales. - Keep fee rows out of the order match. They belong in the payout explanation, not in the order bridge.
- Write the result as a classification, not as one big unmatched list.
That last point matters. A working reconciliation should not end with forty red cells and a guess. It should end with categories that explain what each difference means.
The failure modes that make good matches look broken
Even with the right bridge key, a few patterns still create noise.
The same amount appears more than once.
Two WooCommerce orders can both be 89.00. If you match on amount alone, you can attach the wrong Stripe charge to the wrong order. Use amount only as support, never as the primary key.
The order date and payout date belong to different periods.
Late-night orders, weekends, and payout schedules push settlement into the next day or even the next month. That is normal. The sale date and the bank date answer different questions.
A refund lands in a different payout from the original order.
This is one of the most common reasons operators think the bridge is wrong. The original charge matched correctly. The later refund reduced a different payout batch. Both rows are correct, but they belong to different moments in the cash flow.
Fee rows get forced into the order match.
Stripe fees explain why net cash is lower. They do not represent separate WooCommerce orders. If you force them into the same lookup, you create fake missing-order exceptions.
One export shows the charge ID and another shows the payment intent.
This is still workable, but it is not a direct text match. You may need to recover the missing ID from order notes or logs before the bridge becomes trustworthy.
This is the same structural slowdown described in why reconciling Shopify orders against Stripe payouts is slow. The storefront changes. The mismatch pattern stays the same: order-level data on one side, settlement-level data on the other.
What a finished reconciliation should actually show
A usable output proves the path from order reference to Stripe payment to payout batch. It does not stop at "matched" or "not matched."
| Category | What it means |
|---|---|
| Matched charge | WooCommerce order and Stripe charge agree |
| Refunded later | Original order matched, later refund reduced a later payout |
| Fee explanation | Cash is lower because Stripe deducted fees |
| Timing difference | Sale and payout belong to different dates or periods |
| Adjustment or dispute | Settlement changed for a reason outside the original order row |
| Unresolved | Missing ID, conflicting amount, or incomplete evidence |
When you lay the output out that way, the next person does not need to reopen WooCommerce and Stripe from scratch. They can follow the chain:
- which WooCommerce order reference was tested
- which Stripe object confirmed the payment
- which payout absorbed that payment
- which rows reduced the cash before it reached the bank
- which exceptions still need action
That is the standard to aim for. Not a workbook that happens to balance this month. A match path you can defend next month.
When rebuilding the bridge every month stops being worth it
The manual approach works when payout volume is low and the export structure stays stable. It stops working well when every close repeats the same recovery steps:
- find missing Stripe IDs in order exports
- search logs for
ch_orpi_values - break payout rows into charge, refund, and fee buckets
- rebuild the same bridge table again
- rewrite the same explanations for timing and net settlement
At that point, the real work is no longer accounting judgment. It is file translation.
The core fix is not finding a smarter VLOOKUP. It is accepting that WooCommerce order references and Stripe payout logs describe different layers of the same money flow, then bridging those layers in the right order.
