joyryde
Forum Replies Created
-
OK, thank you Rodica!
Please note that we also reported a catastrophic flaw in your Otter Blocks plugin yesterday as well. We had to revert it to the old version and spent 2 hours repairing the damage on our website yesterday.
https://ww.wp.xz.cn/support/topic/advanced-columns-block-drops-wrapper-html-on-block-validation-recovery/The cause of the block validation bug is definitively Otter Blocks 3.1.9, installed April 24. The 3.1.9 release changed how block attributes are stored (specifically the animation/editor view fix in its changelog), which causes WP’s block validator to see a mismatch between old stored HTML and what 3.1.9 now generates — triggering the “block recovery” that strips the wrapper.
For all users of this plugin, you must downgrade this plugin to 3.1.8 or you will lose everything on any page if you attempt to edit any page.Due to these flaws in your plugin, nobody can update ANY PAGE on the entire website without crashing the entire page and losing all of its content. This is URGENT.
Fantastic, we will, thank you!
Thank you for clarifying — you’re correct on both points, and we want to update our diagnosis.
After re-examining our server, the root cause was an asset optimizer, deferring
wp-hooks. We had it in a list of WP core utility scripts marked safe to defer, and this had never caused problems until v3.3.107.Looking at your changelog, the relevant change in 3.3.107 is the “improved load time performance of express checkout buttons on cart block and checkout block.” Our working theory is that this performance optimization moved
checkout-modules.jsto execute earlier in the page — synchronously, before DOMContentLoaded — while our deferredwp-hooksexecutes after. Before 3.3.107, even withwp-hooksdeferred, the scripts apparently resolved in a compatible order. After 3.3.107,checkout-modules.jsruns before the deferredwp-hooksexecutes, leavingwp.hooksundefined.We fixed it by removing
wp-hooksfrom our defer list. It’s fully resolved on our end.One heads-up for other customers: any store with a frontend script optimizer that defers
wp-hookswould have hit silent CC payment failures after the 3.3.107 update. Sincewp-hookswas likely benign to defer before the loading sequence changed, this won’t be obvious to diagnose. A note in the changelog — something like “Express checkout scripts now load synchronously earlier in the page; ensurewp-hooksis not deferred” — would save other affected stores several days of lost revenue.OK, we audited every custom code on the site, icnluding yours that you wrote for us. None of them interact with WooCommerce tax rates or the customer’s billing/shipping state.
What we found: the specific incident that we reported to you (Order
#329043, April 18) involved a customer whose Stripe payment (Through your Stripe Plugin) failed 7 minutes earlier. When they switched to PayPal, your plugin’sCartOrderREST endpoint rancalculate_totals()to build the PayPal order. At that moment,WC()->customerappears to have had no valid shipping state — the cart calculated $0 tax, the PayPal order was created with that total, and the WC order was created to match.Every other PayPal order we have (70+ per week) in a taxable state has correct tax. This appears to be an edge case tied to WC session state after a failed payment on a different gateway.
Is there a guard in the CartOrder endpoint that validates the customer state is populated before creating the PayPal order? Could it fall back to the billing state if shipping state is empty? If not, there should be.
We checked the wc-ppcp logs as you suggested. The April 18 log for order
#329043has rotated off our server, so we can’t show you the exact payload for that order.However, from our available logs (April 28 – May 4), we can confirm that in the initial CartOrder creation request, tax is correctly populated for states in our table — so the CartOrder step appears to be working as you described.
Your answer to question 2 is actually what points to where the problem is. You said: “before the PayPal plugin captures/authorizes the payment, it performs a patch request using the WooCommerce order object to ensure all totals match.”
Our WooCommerce order
#329043has_order_tax = 0in the database. If the PATCH reads from the WC order object, and the WC order already has $0 tax at that point, then the PATCH would propagate $0 to PayPal — which is exactly the outcome we see.That means the question has shifted: why does WooCommerce create the order with $0 tax for the PayPal flow but not the Stripe flow, if the address is correctly in scope during checkout?
Two things that are PayPal-specific in the flow you described, and not present in a Stripe checkout:
- The CartOrder REST call fires and calls
calculate_totals()before the WC checkout form is submitted - The PayPal wallet opens and the customer completes payment before the checkout form POST
Is it possible that the CartOrder’s
calculate_totals()call in step 3 writes something to the WC session that then affects the WC checkout’scalculate_totals()result in step 8? Specifically: could the CartOrder step be caching cart tax data to the session in a state that isn’t overwritten when WooCommerce runs its own calculation during the subsequent checkout form POST?The WC order has the correct address (validation passed) but $0 tax. That combination — valid address, $0 tax — is exactly what you’d get if the address is present during form validation but the tax location isn’t correctly set on
WC()->customerwhencalculate_taxes()runs in step 8. What’s unique to the PayPal flow at that point is that the session has already been touched by CartOrder’s earlier REST request.You’re right on three counts:
- This doesn’t involve the Express Checkout flow — our staff already confirmed in the original post it’s the Place Order button.
- The address doesn’t come from PayPal’s API response — the Disable PayPal Shipping Address option is enabled.
- Since the order processes and contains the correct address, that rules out an empty address during calculation.
The flow you’ve described is clear and the WooCommerce logic in it is correct.
What still needs an explanation is this: tax calculates correctly on every other payment method and is $0 only when your plugin is the active gateway. That’s a consistently reproducible, gateway-specific outcome. If the WooCommerce checkout flow runs identically regardless of which gateway is selected, there shouldn’t be a gateway-specific tax result.
The part of the flow that is specific to your plugin is Step 3 — the request to the CartOrder class that creates the PayPal order before the wallet opens. That request runs in its own context, uses the cart totals to set the PayPal order amount, and happens before WooCommerce runs its final
calculate_totals()in Step 8.Two specific questions:
- When the CartOrder class calculates the PayPal order amount in Step 3, does it call
WC()->cart->calculate_totals()or read cart totals from the session — and at that point in the request, is the customer’s tax address guaranteed to be in the WC session? - After the PayPal order is created with a specific amount in Step 3, does your plugin apply any filter or hook to prevent WooCommerce from recalculating a different cart total in Step 8 — to ensure the final order total matches the amount already authorized with PayPal?
If the answer to question 2 is yes in any form, that’s the mechanism. The PayPal order amount gets locked to whatever the cart totals were in Step 3, and if tax was absent at that point, it stays absent.
You’re correct that WooCommerce runs
calculate_totals()— nobody is disputing that. The issue is whatWC()->customercontains at the moment that calculation runs. Tax is derived fromWC()->customer->get_billing_country(),get_billing_state(), etc. If those are empty or not yet set, the result is $0.In the standard checkout flow (customer fills out the WC form and clicks Place Order), the billing address is in the WC session by the time
calculate_totals()fires, because the customer populated it on the checkout page. That works correctly.In the PayPal Express flow (customer clicks the PayPal button on the cart or product page and completes payment on PayPal’s side before reaching the WC checkout form), the customer’s address comes from PayPal’s API response — not from a WC checkout form POST. Your plugin receives that address from PayPal after the customer approves, then sets it on the order.
The question is: at what point in your plugin’s hook execution does the WC customer’s billing/shipping address get written to
WC()->customeror the WC session — and does that happen before or aftercalculate_totals()runs?If your plugin writes the PayPal-sourced address to order meta after
calculate_totals()has already fired, the tax calculation sees an empty address and returns $0, while the saved order correctly shows the address. That’s precisely the symptom: correct address in the order, $0 tax.This is not a theoretical edge case. The bug is 100% reproducible using Express checkout (PayPal button on cart/product page) for orders shipping to taxable US states. It does not occur when customers use the standard WC checkout form with PayPal as the payment method. That difference in behavior points directly at the Express flow — which is your plugin’s code, not WooCommerce’s.
Can you show where in your plugin’s execution order the customer tax address is set relative to
calculate_totals()?Thank you for your response. However, we have definitive proof that the plugin is directly responsible for $0 tax on PayPal orders. We’d like to share a controlled comparison that eliminates every other variable.
The Evidence: Two Orders, One Customer, Seven Minutes Apart
On April 18, 2026, the same customer placed two orders for an identical product, shipping to the exact same address. He attempted to use your Stripe plugin to pay, and it failed, so he apparently switched to your PayPal plugin to attempt to complete the payment.
Order #329042 — Credit Card (Stripe)
- Customer: Michael P. (user #33356)
- Shipping: Mesa, AZ 85207
- Product: Norcold N20DC — $5,259.28
- Placed: 15:11
- Sales Tax: $436.52 ✓
Order #329043 — PayPal (your plugin)
- Customer: Michael P. (user #33356) — same customer
- Shipping: Mesa, AZ 85207 — same address
- Product: Norcold N20DC — $5,259.28 — same product
- Placed: 15:18 — seven minutes later
- Sales Tax: $0.00 ✗
Same customer. Same shipping address. Same product. Same price. Same day. The only difference is the payment method. The credit card order correctly calculated $436.52 in Arizona sales tax. The PayPal order calculated $0.
Ruling Out Other Causes
- Tax exemption: This customer has no tax-exempt status on their account.
- Nexus: Arizona is one of our 22 configured nexus states with active tax rates.
- WooCommerce automated taxes: Enabled and functioning — as proven by the CC order calculating correctly.
- “Validate Checkout Fields” setting: Already set to
yesin our configuration. This was your suggestion, and it was already enabled before these orders were placed. It did not prevent the problem. Your plugin has been in use since the day you released it because you wanted us to test it for you and promote it for you.
Why This Points to the Plugin’s Checkout Flow
You stated that
CartOrder::handle_post_request()“has no bearing on how orders are created” and that the plugin “doesn’t interact with the order creation logic.” But the evidence contradicts this. When the same customer with the same address buys the same product minutes apart, and the only variable is the payment gateway, the gateway’s checkout flow is necessarily the cause.The standard WooCommerce checkout collects the shipping address, passes it to the tax calculation engine, and then creates the order with the correct tax. When your plugin’s PayPal button flow is used instead, tax is calculated at $0 — which is consistent with tax being calculated before the shipping address is available to WooCommerce’s tax engine.
The Impact
This is not a theoretical concern. We have collected sales tax liability in Arizona and our other nexus states. Every PayPal order that ships to a nexus state without tax means we owe that tax out of pocket. On this single order alone, that’s $436.52 in lost tax revenue.
We need this addressed. The
validate_checkoutsetting is not solving it. Something in the PayPal button checkout flow is bypassing or pre-empting the tax calculation that works correctly through the standard checkout with other payment methods.Hi,
We ended up just writing our own plugin for this, as this one has caused many issues over the last couple of years. Thank you.Done, thanks!
Thank you
UPDATE:
Hi,
I’m writing to report a confirmed bug in the Xoo Waitlist plugin affecting our storefront (nomadicsupply.com). Despite the plugin being configured to show the waitlist button for Backorder products only, the button was rendering on in-stock products across our catalog.
After investigating the issue ourselves — without any assistance from your support team — we identified two separate bugs:
- DATA CORRUPTION BUG
The xoo-wl-general-options[m-btn-show] array in the WordPress database contained a corrupted empty string (“”) alongside the saved stock status values. PHP’s in_array() function uses loose type comparison by default, which causes an empty string to match against any stock status value. As a result, the waitlist button was shown on every product regardless of stock status, directly contradicting the plugin settings.
We fixed this ourselves by directly cleaning the database option via WP-CLI:
$opts = get_option(“xoo-wl-general-options”);
$opts[“m-btn-show”] = array_values(array_filter($opts[“m-btn-show”], function($v){ return $v !== “”; }));
update_option(“xoo-wl-general-options”, $opts);- LOGIC BUG IN BUTTON VISIBILITY CHECK
After fixing the data corruption, the button was still appearing on in-stock products. Reading your plugin source code (xoo-wl-functions.php), we found the following logic:
else if ( in_array( ‘backorder’, $setting, true ) && $product->backorders_allowed() ) {
$showBtn = true;
}The problem: backorders_allowed() returns true whenever backorders are set to “Allow” OR “Allow, but notify customer” — regardless of whether the product actually has stock on hand. This means any product with backorders enabled will show the waitlist button even when it has physical inventory available and is clearly marked “In Stock.”
This is incorrect behavior. A product showing “2 in stock” should never display a waitlist button. The check should account for actual stock quantity, not just whether backorders are permitted.
We were forced to implement our own workaround via an mu-plugin using your own filter hook:
add_filter( ‘xoo_wl_product_should_show_waitlist’, function( $show, $product ) {
if ( $show && $product->is_in_stock() && $product->get_stock_quantity() > 0 ) {
return false;
}
return $show;
}, 10, 2 );This should not be necessary. Both of these are bugs in your plugin that your support team refused to help with.
We would appreciate:
- Acknowledgment of both bugs
- A fix in a future update that sanitizes the m-btn-show array on save and corrects the backorders_allowed() logic to account for actual stock quantity
- Confirmation of when a patched version will be available
Hi Laca,
We see that the plugin is on Version 1.7.9 on our website. We have not updated it recently (we only update manually), is there a newer version with these updates that we are not seeing?
- The CartOrder REST call fires and calls