diff --git a/tests/unit/Helpers/CheckoutHelperTest.php b/tests/unit/Helpers/CheckoutHelperTest.php new file mode 100644 index 000000000..029c86b77 --- /dev/null +++ b/tests/unit/Helpers/CheckoutHelperTest.php @@ -0,0 +1,97 @@ +countries = $wcCountriesAvailable ? $countriesMock : null; + + WP_Mock::userFunction('WC') + ->andReturn($wcMock); + + $countriesMock->allows('get_allowed_countries') + ->andReturn($allowedCountries); + + $this->assertSame($expected, CheckoutHelper::isCountryAllowedToOrder($countryCode)); + } + + /** + * @covers ::isCountryAllowedForShipping + * @dataProvider countryCodeProvider + */ + public function testCanDetermineIsCountryAllowedForShipping( + string $countryCode, + bool $wcCountriesAvailable, + array $allowedCountries, + bool $expected + ) { + $wcMock = Mockery::mock('WooCommerce'); + + $countriesMock = Mockery::mock('WC_Countries'); + $wcMock->countries = $wcCountriesAvailable ? $countriesMock : null; + + WP_Mock::userFunction('WC') + ->andReturn($wcMock); + + $countriesMock->allows('get_shipping_countries') + ->andReturn($allowedCountries); + + $this->assertSame($expected, CheckoutHelper::isCountryAllowedForShipping($countryCode)); + } + + /** + * @see testCanDetermineIsCountryAllowedToOrder + */ + public function countryCodeProvider() : Generator + { + yield 'empty country code' => [ + 'countryCode' => '', + 'wcCountriesAvailable' => false, + 'allowedCountries' => [], + 'expected' => false, + ]; + + yield 'WC countries not available' => [ + 'countryCode' => 'GB', + 'wcCountriesAvailable' => false, + 'allowedCountries' => [], + 'expected' => true, + ]; + + yield 'not on allow list' => [ + 'countryCode' => 'GB', + 'wcCountriesAvailable' => true, + 'allowedCountries' => ['US' => 'United States', 'FR' => 'France'], + 'expected' => false, + ]; + + yield 'is on allow list' => [ + 'countryCode' => 'GB', + 'wcCountriesAvailable' => true, + 'allowedCountries' => ['US' => 'United States', 'FR' => 'France', 'GB' => 'Great Britain'], + 'expected' => true, + ]; + } +} diff --git a/woocommerce/Helpers/CheckoutHelper.php b/woocommerce/Helpers/CheckoutHelper.php new file mode 100644 index 000000000..238029289 --- /dev/null +++ b/woocommerce/Helpers/CheckoutHelper.php @@ -0,0 +1,52 @@ +countries) { + $allowed_countries = WC()->countries->get_allowed_countries(); + + return array_key_exists($countryCode, $allowed_countries); + } + + return true; + } + + /** + * Determines whether the provided country code is allowed for shipping. + * + * @since 6.0.1 + * + * @param string $countryCode recommended to pass through the *shipping* address + * @return bool + */ + public static function isCountryAllowedForShipping(string $countryCode): bool + { + if (empty($countryCode)) { + return false; + } + + if (WC() && WC()->countries) { + $shipping_countries = WC()->countries->get_shipping_countries(); + + return array_key_exists($countryCode, $shipping_countries); + } + + return true; + } +} diff --git a/woocommerce/changelog.txt b/woocommerce/changelog.txt index 2a4c490cb..ee055ed85 100644 --- a/woocommerce/changelog.txt +++ b/woocommerce/changelog.txt @@ -1,6 +1,7 @@ *** SkyVerge WooCommerce Plugin Framework Changelog *** 2026.nn.nn - version 6.0.2 + * Fix - Apple Pay: Check store country restrictions for selling/shipping 2025.nn.nn - version 6.0.1 * New: Introduced a new ScriptHelper class with addInlineScript() method diff --git a/woocommerce/payment-gateway/External_Checkout/apple-pay/class-sv-wc-payment-gateway-apple-pay-ajax.php b/woocommerce/payment-gateway/External_Checkout/apple-pay/class-sv-wc-payment-gateway-apple-pay-ajax.php index 09fef7c17..2bc3a6f70 100644 --- a/woocommerce/payment-gateway/External_Checkout/apple-pay/class-sv-wc-payment-gateway-apple-pay-ajax.php +++ b/woocommerce/payment-gateway/External_Checkout/apple-pay/class-sv-wc-payment-gateway-apple-pay-ajax.php @@ -24,6 +24,8 @@ namespace SkyVerge\WooCommerce\PluginFramework\v6_0_2; +use SkyVerge\WooCommerce\PluginFramework\v6_0_2\Helpers\CheckoutHelper; + defined( 'ABSPATH' ) or exit; if ( ! class_exists( '\\SkyVerge\\WooCommerce\\PluginFramework\\v6_0_2\\SV_WC_Payment_Gateway_Apple_Pay_AJAX' ) ) : @@ -180,6 +182,16 @@ public function recalculate_totals() { $city = $contact['locality']; $postcode = $contact['postalCode']; + // validate country against WooCommerce selling and shipping settings + if ( $country ) { + /* + * Validate country against WooCommerce selling and shipping settings. + * Apple Pay contact info is primarily shipping address, but since we don't have both, we'll use + * the shipping address to validate both billing and shipping settings. + */ + $this->validateAllowedCountry( $country, $country ); + } + WC()->customer->set_shipping_city( $city ); WC()->customer->set_shipping_state( $state ); WC()->customer->set_shipping_country( $country ); @@ -238,6 +250,12 @@ public function process_payment() { try { + // final validation check: ensure billing/shipping country is still allowed + $billing_country = WC()->customer->get_billing_country(); + $shipping_country = WC()->customer->get_shipping_country(); + + $this->validateAllowedCountry( $billing_country ?: '', $shipping_country ?: '' ); + $result = $this->get_handler()->process_payment(); wp_send_json_success( $result ); @@ -254,6 +272,48 @@ public function process_payment() { } + /** + * Validates that the provided countries are allowed for billing and shipping. + * + * @since 6.0.1 + * + * @param string $billingCountry billing country code (empty string if not provided) + * @param string $shippingCountry shipping country code (empty string if not provided) + * @throws \Exception if validation fails + */ + protected function validateAllowedCountry(string $billingCountry, string $shippingCountry) + { + + // validate billing country for orders (if provided) + if (! empty($billingCountry) && ! CheckoutHelper::isCountryAllowedToOrder($billingCountry)) { + + $this->get_handler()->log("Apple Pay: Billing country '{$billingCountry}' is not allowed for orders"); + + throw new \Exception( + sprintf( + /* translators: %s country code. */ + esc_html__('Sorry, we do not allow orders from the provided country (%s)', 'woocommerce'), + esc_html($billingCountry) + ) + ); + } + + // validate shipping country for shipping (if provided) + if (! empty($shippingCountry) && ! CheckoutHelper::isCountryAllowedForShipping($shippingCountry)) { + + $this->get_handler()->log("Apple Pay: Shipping country '{$shippingCountry}' is not allowed for shipping"); + + throw new \Exception( + sprintf( + /* translators: %s country code. */ + esc_html__('Sorry, we do not ship orders to the provided country (%s)', 'woocommerce'), + esc_html($shippingCountry) + ) + ); + } + } + + /** * Gets the Apple Pay handler instance. *