No. A WooCommerce bank transfer order should fire its Purchase event when the order is marked paid — when it moves from On hold to Processing — not when the shopper clicks Place order. Until the transfer lands, the order is a promise. Counting it as a sale inflates your purchase count and teaches Meta to find people who order but never pay.
The early fire isn’t a bug in your pixel. WooCommerce’s built-in bank transfer method puts the order on hold and sends the shopper to the order-received page immediately, with your bank details on it. Any pixel on that page sees a finished checkout.
Why does the pixel fire before a bank transfer is paid?
Because WooCommerce shows the thank-you page as soon as the order is placed, and the payment happens later, outside your site. The built-in “Direct bank transfer” gateway (called BACS in the code) sets the order to On hold, empties the cart and redirects to the order-received page. A Purchase fired on that page fires for every order, paid or not.
You can see it in WooCommerce’s own source. In the bank transfer gateway’s process_payment() function, an order with a total above zero is set to On hold with the note “Awaiting BACS payment”, then the shopper is sent to the thank-you page. The gateway also hooks the thank-you page to print your account details. The cheque gateway does the same with “Awaiting check payment.”
So the sequence is:
- Shopper places the order. Status: On hold.
- Thank-you page loads with your bank details. Your pixel fires Purchase here.
- Days pass. Some shoppers transfer the money. Some never do.
- You see the money in your bank and change the order to Processing. This is the actual sale.
A server-side setup that fires when the order is created has the same problem, just on the server. The trigger is what matters, not the channel.
What about boleto and Pix?
Boleto and Pix are Brazilian payment methods, and WooCommerce supports them through third-party gateway plugins, not the core. Most leave the order in Pending payment or On hold until the gateway confirms the payment. A boleto has to be paid at a bank or app before its due date, so the gap can run days. Check your gateway’s settings to see which status it uses before payment and which it sets after.
What does firing Purchase at order placement cost you?
It costs you accurate numbers and a clean optimisation signal. Every unpaid bank transfer order becomes a Purchase in Events Manager, so reported purchases and ROAS sit above what you were paid. Worse, Meta’s delivery system learns from those events, so it looks for more people who place an order and walk away from the payment.
Two things make it worse with bank transfers specifically:
- On-hold orders don’t clean themselves up. WooCommerce’s automatic cancellation of unpaid orders only looks at orders in Pending payment, and only runs when stock management is on and a hold-stock time is set (the option defaults to 60 minutes). Bank transfer orders sit in On hold, so they stay there until you cancel them. Your order list quietly fills with unpaid orders that Meta already counted.
- Meta doesn’t un-count them. Cancelling the order in WooCommerce doesn’t remove a Purchase Meta already received. Sending another event to “correct” it just adds a second purchase.
If your checkout offers only cards, this barely matters. If bank transfer is a meaningful share of your orders — as it is in some markets and in many B2B stores — it can skew every campaign decision you make off Ads Manager.
What does waiting for payment cost you?
It costs you speed. A Purchase sent at payment reaches Meta hours or days after the click, and Meta optimises on fewer, later events. Meta also enforces a limit: a server event’s event_time can’t be more than seven days in the past when you send it. A transfer that clears after a week needs handling.
The delay matters less than it sounds. Meta compares the event’s time with the click’s time, so a sale two days late still lands against the right ad if it falls inside your attribution window. What you lose is fast feedback: results fill in over days, not hours.
What if the transfer arrives more than seven days later?
Then an event stamped with the original order time can’t be sent as-is. Use the time of payment as the event time — that’s when the sale actually happened — or clamp it to the edge of the window. The 7-day event time window in Meta CAPI explains the rule and the options. The simplest fix is operational: reconcile your bank account daily, not monthly.
So when should the Purchase event fire?
Fire Purchase when the order reaches Processing or Completed, and nothing earlier. If you want an earlier signal for Meta to learn from, send a different event at order placement — AddPaymentInfo or a custom “order placed” event — never a second Purchase. That keeps your purchase count equal to money received, and gives the algorithm something to work with in the meantime.

Here’s how the options compare:
| Setup | Purchase count | Signal speed | Risk |
|---|---|---|---|
| Purchase on thank-you page | Inflated by unpaid orders | Immediate | Meta optimises toward non-payers |
| Purchase on order created (server) | Inflated by unpaid orders | Immediate | Same problem, harder to spot |
| Purchase on Processing | Matches money received | Delayed until marked paid | Late sends past 7 days |
| Early non-Purchase event + Purchase on Processing | Matches money received | Early proxy, true sale later | Two events to maintain |
What about cash on delivery?
Cash on delivery works differently, and it catches people out. WooCommerce’s built-in COD gateway sets the order to Processing at checkout by default (On hold only if the order contains a downloadable item). So any setup that sends Purchase on Processing sends a COD order when it’s placed, before the courier collects. If you want COD to wait, the core exposes a filter, woocommerce_cod_process_payment_order_status, to start those orders On hold instead.
How do you move the Purchase to the moment of payment?
Stop firing Purchase on the thank-you page, and send it from the order record when the status changes to Processing. A server event triggered by that status change carries the payment decision with it, and it doesn’t depend on the shopper’s browser. Then test it with one real bank transfer order.
- Check your pixel plugin’s status options. Some plugins let you choose which order statuses fire Purchase. Limit it to Processing and Completed.
- Switch off the thank-you page Purchase. A pixel on the order-received page can’t know if the transfer will arrive. Turn its Purchase off or make it conditional on a paid status.
- Send Purchase server-side on the status change. This is the architecture in the guide to tracking WooCommerce purchases server-side to Meta, delivered through the Conversions API. It also fixes orders whose buyers never see the thank-you page, like WooCommerce orders paid with PayPal.
- Keep one source per event. If two tools send Purchase with different event ids, Meta counts both.
- Test it. Place a bank transfer order and watch Events Manager’s test events tool — the same check used to confirm your Meta CAPI is working. Nothing should arrive. Mark it Processing; one Purchase should.
Because the sale may come days after the click, attribution has to survive the gap. The buyer’s email on the order is what ties it back to the session — the same problem as attributing a purchase weeks after the ad click.
How does PartialLeads handle WooCommerce bank transfer orders?
PartialLeads sends a Purchase only when a WooCommerce order is paid — Processing or Completed. It receives the store’s order webhooks, including later status updates, so a bank transfer order sits On hold and is not sent, then is sent when you mark it Processing. The order record, not a page visit, decides when the conversion goes out.
Nothing fires on the thank-you page. The PartialLeads WooCommerce plugin forwards view product, add to cart and begin checkout server-side to Meta, Pinterest and TikTok. The Purchase is owned by the order webhooks. An on-hold order produces no Purchase however many times the order-received page is loaded.
What it sends when the order is paid. One Purchase per paid order, at the full order total including tax and shipping after discounts, with hashed email and phone from the order. It goes to each connected platform — Meta, Pinterest, TikTok — and as a row in the Google Sheet you import into Google Ads. Each event has a deterministic event_id, so a redelivered webhook can’t send the same order twice. If a transfer is marked paid late enough that the event would fall outside Meta’s seven-day window, the event time is clamped into the window rather than rejected.
Tying the late sale back to the click. The order’s email and phone are matched to the visitor’s earlier sessions, so a Purchase marked paid three days later still carries the campaign that brought the buyer in.

Where you see it working. The Purchases ledger lists each paid order with its value, source and session match; its count should equal your Processing plus Completed orders for the same days. The Meta CAPI activity log shows each server Purchase with its status and timestamp, so you can see it went out when the order was marked paid, not when it was placed. The Attribution report’s Time to Purchase shows how long buyers take from first touch to paid order.
The honest constraints. PartialLeads follows WooCommerce’s status: a COD order your store sets to Processing at checkout is sent then. An order is recorded once, so editing a paid order later doesn’t update or re-send it. Event ids are PartialLeads’ own and don’t deduplicate against another plugin’s browser pixel — if Meta for WooCommerce or PixelYourSite still fires Purchase on the thank-you page, switch theirs off or unpaid orders keep counting through them. WooCommerce Subscriptions renewals are recorded but not sent. PartialLeads controls which orders are sent and when; how Meta matches and attributes them is Meta’s half.
| What breaks | The mechanism | Where you see it in the dashboard |
|---|---|---|
| Thank-you page fires Purchase before payment | Plugin fires nothing on order-received; Purchase comes from order webhooks | Meta CAPI activity log |
| On-hold bank transfer orders counted as sales | Purchase sent only at Processing or Completed | Purchases ledger |
| Transfer paid days after the order | Status-update webhook triggers the send; event time clamped to Meta’s 7-day window | CAPI activity log timestamps |
| Late sale loses its campaign | Order email and phone matched to earlier sessions | Attribution report, Time to Purchase |
| Another plugin still fires Purchase | No cross-tool dedup — keep one source per event | Leads-list API column vs Events Manager |
Tell us what's broken. We'll fix your tracking — free.
Describe the tracking/attribution problem you're stuck on and we'll map it to a fix: server-side conversions to Meta, Google, TikTok and Pinterest, plus first-party tracking that survives Safari. No code required.
Sources
- https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/plugins/woocommerce/includes/gateways/bacs/class-wc-gateway-bacs.php
- https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/plugins/woocommerce/includes/gateways/cheque/class-wc-gateway-cheque.php
- https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/plugins/woocommerce/includes/gateways/cod/class-wc-gateway-cod.php
- https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/plugins/woocommerce/includes/wc-order-functions.php
- https://raw.githubusercontent.com/woocommerce/woocommerce/trunk/plugins/woocommerce/includes/data-stores/class-wc-order-data-store-cpt.php
- https://developers.facebook.com/docs/marketing-api/conversions-api/parameters/server-event
- https://developers.facebook.com/docs/marketing-api/conversions-api/deduplicate-pixel-and-server-events