Skip to content
97 changes: 97 additions & 0 deletions tests/unit/Helpers/CheckoutHelperTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

namespace SkyVerge\WooCommerce\PluginFramework\v6_0_2\Tests\unit\Helpers;

use Generator;
use Mockery;
use SkyVerge\WooCommerce\PluginFramework\v6_0_2\Helpers\CheckoutHelper;
use SkyVerge\WooCommerce\PluginFramework\v6_0_2\Tests\TestCase;
use WP_Mock;

/**
* @coversDefaultClass \SkyVerge\WooCommerce\PluginFramework\v6_0_2\Helpers\CheckoutHelper
*/
final class CheckoutHelperTest extends TestCase
{
/**
* @covers ::isCountryAllowedToOrder
* @dataProvider countryCodeProvider
*/
public function testCanDetermineIsCountryAllowedToOrder(
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_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,
];
}
}
52 changes: 52 additions & 0 deletions woocommerce/Helpers/CheckoutHelper.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace SkyVerge\WooCommerce\PluginFramework\v6_0_2\Helpers;

class CheckoutHelper
{
/**
* Determines whether the provided country code is allowed to place an order.
*
* @since 6.0.1
*
* @param string $countryCode recommended to pass through the *billing* address
* @return bool
*/
public static function isCountryAllowedToOrder(string $countryCode): bool
{
if (empty($countryCode)) {
return false;
}

if (WC() && WC()->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;
}
}
1 change: 1 addition & 0 deletions woocommerce/changelog.txt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' ) ) :
Expand Down Expand Up @@ -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 );
Expand Down Expand Up @@ -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 );
Expand All @@ -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.
*
Expand Down
Loading