Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 35 additions & 16 deletions inc/gateways/class-paypal-rest-gateway.php
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ public function hooks(): void {
// Hide PayPal from checkout when currency is not supported
add_filter('wu_get_active_gateways', [$this, 'maybe_remove_for_unsupported_currency']);

// Hide PayPal from checkout when merchant cannot receive payments
// Hide PayPal from checkout when merchant status is invalid (payments_receivable or email_confirmed false)
add_filter('wu_get_active_gateways', [$this, 'maybe_remove_for_invalid_merchant_status']);

// Register PayPal checkout scripts (button branding)
Expand Down Expand Up @@ -250,30 +250,49 @@ public function maybe_remove_for_unsupported_currency(array $gateways): array {
}

/**
* Removes PayPal from the active gateways list when the merchant cannot receive payments.
* Checks whether the connected PayPal merchant account is in a valid state to receive payments.
*
* PayPal requires that merchants with `payments_receivable=false` or
* `email_confirmed=false` are blocked from processing payments until
* their account setup is complete.
* Returns true only when both conditions are met for the current mode (sandbox or live):
* - payments_receivable is truthy
* - email_confirmed is truthy
*
* Hooked to 'wu_get_active_gateways'.
* When no OAuth merchant is connected the check is skipped (returns true) so that
* manual-credentials setups are not affected.
*
* @since 2.0.0
* @param array $gateways The registered active gateways.
* @return array
* @return bool
*/
public function maybe_remove_for_invalid_merchant_status(array $gateways): array {
public function is_merchant_status_valid(): bool {

// Only applies when connected via OAuth
if (empty($this->merchant_id)) {
return $gateways;
$mode_prefix = $this->test_mode ? 'sandbox' : 'live';

// Only enforce when an OAuth merchant is connected.
$merchant_id = wu_get_setting("paypal_rest_{$mode_prefix}_merchant_id", '');

if (empty($merchant_id)) {
return true;
}

$mode_prefix = $this->test_mode ? 'sandbox' : 'live';
$payments_receivable = wu_get_setting("paypal_rest_{$mode_prefix}_payments_receivable", true);
$email_confirmed = wu_get_setting("paypal_rest_{$mode_prefix}_email_confirmed", true);
$payments_receivable = wu_get_setting("paypal_rest_{$mode_prefix}_payments_receivable", false);
$email_confirmed = wu_get_setting("paypal_rest_{$mode_prefix}_email_confirmed", false);

return (bool) $payments_receivable && (bool) $email_confirmed;
}

/**
* Removes PayPal from the active gateways list when the merchant account status is invalid.
*
* Hooked to 'wu_get_active_gateways'. Hides the gateway when payments_receivable or
* email_confirmed is false so customers cannot attempt a payment that would be rejected
* by PayPal.
*
* @since 2.0.0
* @param array $gateways The registered active gateways.
* @return array
*/
public function maybe_remove_for_invalid_merchant_status(array $gateways): array {

if (! $payments_receivable || ! $email_confirmed) {
if (! $this->is_merchant_status_valid()) {
unset($gateways['paypal-rest']);
}

Expand Down
195 changes: 195 additions & 0 deletions tests/WP_Ultimo/Gateways/PayPal_REST_Gateway_Test.php
Original file line number Diff line number Diff line change
Expand Up @@ -719,6 +719,201 @@ public function test_maybe_remove_for_unsupported_currency_preserves_other_gatew
$this->assertArrayHasKey('manual', $result);
}

// -------------------------------------------------------------------------
// is_merchant_status_valid()
// -------------------------------------------------------------------------

/**
* Test merchant status is valid when no OAuth merchant is connected.
*
* Manual-credentials setups must not be blocked by the merchant status check.
*/
public function test_is_merchant_status_valid_without_oauth_merchant(): void {

// No merchant ID set — manual credentials only
wu_save_setting('paypal_rest_sandbox_merchant_id', '');

$gateway = new PayPal_REST_Gateway();
$gateway->init();

$this->assertTrue($gateway->is_merchant_status_valid());
}

/**
* Test merchant status is valid when both flags are true.
*/
public function test_is_merchant_status_valid_both_flags_true(): void {

wu_save_setting('paypal_rest_sandbox_merchant_id', 'MERCHANT123');
wu_save_setting('paypal_rest_sandbox_payments_receivable', true);
wu_save_setting('paypal_rest_sandbox_email_confirmed', true);
wu_save_setting('paypal_rest_sandbox_mode', 1);

$gateway = new PayPal_REST_Gateway();
$gateway->init();

$this->assertTrue($gateway->is_merchant_status_valid());
}

/**
* Test merchant status is invalid when payments_receivable is false.
*/
public function test_is_merchant_status_invalid_when_payments_receivable_false(): void {

wu_save_setting('paypal_rest_sandbox_merchant_id', 'MERCHANT123');
wu_save_setting('paypal_rest_sandbox_payments_receivable', false);
wu_save_setting('paypal_rest_sandbox_email_confirmed', true);
wu_save_setting('paypal_rest_sandbox_mode', 1);

$gateway = new PayPal_REST_Gateway();
$gateway->init();

$this->assertFalse($gateway->is_merchant_status_valid());
}

/**
* Test merchant status is invalid when email_confirmed is false.
*/
public function test_is_merchant_status_invalid_when_email_confirmed_false(): void {

wu_save_setting('paypal_rest_sandbox_merchant_id', 'MERCHANT123');
wu_save_setting('paypal_rest_sandbox_payments_receivable', true);
wu_save_setting('paypal_rest_sandbox_email_confirmed', false);
wu_save_setting('paypal_rest_sandbox_mode', 1);

$gateway = new PayPal_REST_Gateway();
$gateway->init();

$this->assertFalse($gateway->is_merchant_status_valid());
}

/**
* Test merchant status is invalid when both flags are false.
*/
public function test_is_merchant_status_invalid_when_both_flags_false(): void {

wu_save_setting('paypal_rest_sandbox_merchant_id', 'MERCHANT123');
wu_save_setting('paypal_rest_sandbox_payments_receivable', false);
wu_save_setting('paypal_rest_sandbox_email_confirmed', false);
wu_save_setting('paypal_rest_sandbox_mode', 1);

$gateway = new PayPal_REST_Gateway();
$gateway->init();

$this->assertFalse($gateway->is_merchant_status_valid());
}

/**
* Test merchant status check uses live settings in live mode.
*/
public function test_is_merchant_status_valid_uses_live_settings_in_live_mode(): void {

wu_save_setting('paypal_rest_sandbox_mode', 0);
wu_save_setting('paypal_rest_live_merchant_id', 'LIVE_MERCHANT');
wu_save_setting('paypal_rest_live_payments_receivable', false);
wu_save_setting('paypal_rest_live_email_confirmed', true);

$gateway = new PayPal_REST_Gateway();
$gateway->init();

$this->assertFalse($gateway->is_merchant_status_valid());
}

// -------------------------------------------------------------------------
// maybe_remove_for_invalid_merchant_status()
// -------------------------------------------------------------------------

/**
* Test gateway is kept when merchant status is valid.
*/
public function test_maybe_remove_for_invalid_merchant_status_keeps_when_valid(): void {

wu_save_setting('paypal_rest_sandbox_merchant_id', 'MERCHANT123');
wu_save_setting('paypal_rest_sandbox_payments_receivable', true);
wu_save_setting('paypal_rest_sandbox_email_confirmed', true);
wu_save_setting('paypal_rest_sandbox_mode', 1);

$gateway = new PayPal_REST_Gateway();
$gateway->init();
$gateways = ['paypal-rest' => $gateway, 'stripe' => 'stripe'];
$result = $gateway->maybe_remove_for_invalid_merchant_status($gateways);

$this->assertArrayHasKey('paypal-rest', $result);
}

/**
* Test gateway is removed when payments_receivable is false.
*/
public function test_maybe_remove_for_invalid_merchant_status_removes_when_payments_receivable_false(): void {

wu_save_setting('paypal_rest_sandbox_merchant_id', 'MERCHANT123');
wu_save_setting('paypal_rest_sandbox_payments_receivable', false);
wu_save_setting('paypal_rest_sandbox_email_confirmed', true);
wu_save_setting('paypal_rest_sandbox_mode', 1);

$gateway = new PayPal_REST_Gateway();
$gateway->init();
$gateways = ['paypal-rest' => $gateway, 'stripe' => 'stripe'];
$result = $gateway->maybe_remove_for_invalid_merchant_status($gateways);

$this->assertArrayNotHasKey('paypal-rest', $result);
$this->assertArrayHasKey('stripe', $result);
}

/**
* Test gateway is removed when email_confirmed is false.
*/
public function test_maybe_remove_for_invalid_merchant_status_removes_when_email_confirmed_false(): void {

wu_save_setting('paypal_rest_sandbox_merchant_id', 'MERCHANT123');
wu_save_setting('paypal_rest_sandbox_payments_receivable', true);
wu_save_setting('paypal_rest_sandbox_email_confirmed', false);
wu_save_setting('paypal_rest_sandbox_mode', 1);

$gateway = new PayPal_REST_Gateway();
$gateway->init();
$gateways = ['paypal-rest' => $gateway, 'stripe' => 'stripe'];
$result = $gateway->maybe_remove_for_invalid_merchant_status($gateways);

$this->assertArrayNotHasKey('paypal-rest', $result);
$this->assertArrayHasKey('stripe', $result);
}

/**
* Test gateway is kept when no OAuth merchant is connected (manual credentials).
*/
public function test_maybe_remove_for_invalid_merchant_status_keeps_without_oauth(): void {

// No merchant ID — manual credentials only
wu_save_setting('paypal_rest_sandbox_merchant_id', '');
wu_save_setting('paypal_rest_sandbox_payments_receivable', false);
wu_save_setting('paypal_rest_sandbox_email_confirmed', false);

$gateways = ['paypal-rest' => $this->gateway, 'stripe' => 'stripe'];
$result = $this->gateway->maybe_remove_for_invalid_merchant_status($gateways);

$this->assertArrayHasKey('paypal-rest', $result);
}

/**
* Test other gateways are not affected by merchant status check.
*/
public function test_maybe_remove_for_invalid_merchant_status_preserves_other_gateways(): void {

wu_save_setting('paypal_rest_sandbox_merchant_id', 'MERCHANT123');
wu_save_setting('paypal_rest_sandbox_payments_receivable', false);
wu_save_setting('paypal_rest_sandbox_email_confirmed', false);
wu_save_setting('paypal_rest_sandbox_mode', 1);

$gateway = new PayPal_REST_Gateway();
$gateway->init();
$gateways = ['paypal-rest' => $gateway, 'stripe' => 'stripe', 'manual' => 'manual'];
$result = $gateway->maybe_remove_for_invalid_merchant_status($gateways);

$this->assertArrayHasKey('stripe', $result);
$this->assertArrayHasKey('manual', $result);
}

// -------------------------------------------------------------------------
// get_checkout_label_html()
// -------------------------------------------------------------------------
Expand Down
Loading