Add a defensive check to prevent a fatal error during subscription renewal when Avatax is active. #758
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Summary
We recently received a report from a customer that a subscription renewal fails with a fatal error when using PayPal Braintree. Due to the conflict between WooCommerce Subscriptions, PayPal Braintree, and AvaTax.
Error Logs:
failed-scheduled-actions-2025-04-07(2).log
2025-04-07T20:00:08+00:00 ERROR scheduled action 2012663 (subscription payment) failed to finish processing due to the following exception: Call to a member function get_billing_country() on null in /public_html/wp-content/plugins/woocommerce-gateway-paypal-powered-by-braintree/vendor/skyverge/wc-plugin-framework/woocommerce/payment-gateway/External_Checkout/apple-pay/class-sv-wc-payment-gateway-apple-pay.php:707 CONTEXT: {"action_args":"subscription_id: 33736","error_trace":"#0 /public_html/wp-includes/class-wp-hook.php(324): SkyVerge\WooCommerce\PluginFramework\v5_12_7\SV_WC_Payment_Gateway_Apple_Pay->set_customer_taxable_address()n#1 /public_html/wp-includes/plugin.php(205): WP_Hook->apply_filters()n#2 /public_html/wp-content/plugins/woocommerce-avatax/src/class-wc-avatax-order-handler.php(1433): apply_filters()n#3 /public_html/wp-content/plugins/woocommerce-avatax/src/class-wc-avatax-order-handler.php(1382): WC_AvaTax_Order_Handler->get_taxable_address()n#4 /public_html/wp-content/plugins/woocommerce-avatax/src/integrations/class-wc-avatax-integrations.php(148): WC_AvaTax_Order_Handler->is_order_taxable()n#5 /public_html/wp-includes/class-wp-hook.php(324): WC_AvaTax_Integrations->recalculate_renewal_taxes()n#6 /public_html/wp-includes/plugin.php(205): WP_Hook->apply_filters()n#7 /public_html/wp-content/plugins/woocommerce-subscriptions/vendor/woocommerce/subscriptions-core/includes/wcs-renewal-functions.php(45): apply_filters()n#8 /public_html/wp-content/plugins/woocommerce-subscriptions/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php(143): wcs_create_renewal_order()n#9 /public_html/wp-content/plugins/woocommerce-subscriptions/vendor/woocommerce/subscriptions-core/includes/class-wc-subscriptions-manager.php(116): WC_Subscriptions_Manager::process_renewal()n#10 /public_html/wp-includes/class-wp-hook.php(324): WC_Subscriptions_Manager::prepare_renewal()n#11 /public_html/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters()n#12 /public_html/wp-includes/plugin.php(565): WP_Hook->do_action()n#13 /public_html/wp-content/plugins/woocommerce/packages/action-scheduler/classes/actions/ActionScheduler_Action.php(86): do_action_ref_array()n#14 /public_html/wp-content/plugins/woocommerce/packages/action-scheduler/classes/abstracts/ActionScheduler_Abstract_QueueRunner.php(102): ActionScheduler_Action->execute()n#15 /public_html/wp-content/plugins/woocommerce/packages/action-scheduler/classes/ActionScheduler_QueueRunner.php(188): ActionScheduler_Abstract_QueueRunner->process_action()n#16 /public_html/wp-content/plugins/woocommerce/packages/action-scheduler/classes/ActionScheduler_QueueRunner.php(158): ActionScheduler_QueueRunner->do_batch()n#17 /public_html/wp-includes/class-wp-hook.php(324): ActionScheduler_QueueRunner->run()n#18 /public_html/wp-includes/class-wp-hook.php(348): WP_Hook->apply_filters()n#19 /public_html/wp-includes/plugin.php(565): WP_Hook->do_action()n#20 phar:///usr/local/bin/wp/vendor/wp-cli/cron-command/src/Cron_Event_Command.php(358): do_action_ref_array()n#21 phar:///usr/local/bin/wp/vendor/wp-cli/cron-command/src/Cron_Event_Command.php(238): Cron_Event_Command::run_event()n#22 [internal function]: Cron_Event_Command->run()n#23 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Dispatcher/CommandFactory.php(100): call_user_func()n#24 [internal function]: WP_CLI\Dispatcher\CommandFactory::WP_CLI\Dispatcher\{closure}()n#25 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Dispatcher/Subcommand.php(497): call_user_func()n#26 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Runner.php(441): WP_CLI\Dispatcher\Subcommand->invoke()n#27 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Runner.php(464): WP_CLI\Runner->run_command()n#28 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Runner.php(1296): WP_CLI\Runner->run_command_and_exit()n#29 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/WP_CLI/Bootstrap/LaunchRunner.php(28): WP_CLI\Runner->start()n#30 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/bootstrap.php(83): WP_CLI\Bootstrap\LaunchRunner->process()n#31 phar:///usr/local/bin/wp/vendor/wp-cli/wp-cli/php/wp-cli.php(32): WP_CLI\bootstrap()n#32 phar:///usr/local/bin/wp/php/boot-phar.php(20): include('phar:///usr/loc...')n#33 /usr/local/bin/wp(4): include('phar:///usr/loc...')n#34 {main}"}The log seems to show a conflict with WooCommerce Subscriptions, PayPal Braintree (It uses this framework), and AvaTax.
Avatax is triggering a core WooCommerce hook:
And the this plugin framework is hooked into that, and runs this code:
$billing_country = WC()->customer->get_billing_country();However, when this error occurs,
WC()->customeris not set or is null. While there may be some debate about where the responsibility lies, I believe it’s always best to use defensive programming when accessing any top-levelWC()properties likeWC()->customer, and to ensure they exist.Therefore, this PR adds a defensive check to prevent a potential fatal error during subscription renewal.
UI Changes
N/A
QA
Setup
Steps
Before merge