mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-06 18:16:38 +08:00
Add failure registry to API module.
Add product status failure handling to GooglePay.
This commit is contained in:
parent
904beafa1f
commit
1581f34a44
8 changed files with 177 additions and 30 deletions
|
@ -9,6 +9,7 @@ declare(strict_types=1);
|
||||||
|
|
||||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||||
|
|
||||||
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry;
|
||||||
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
|
use WooCommerce\PayPalCommerce\Common\Pattern\SingletonDecorator;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingSubscriptions;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts;
|
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\CatalogProducts;
|
||||||
|
@ -120,7 +121,8 @@ return array(
|
||||||
$container->get( 'woocommerce.logger.woocommerce' ),
|
$container->get( 'woocommerce.logger.woocommerce' ),
|
||||||
$container->get( 'api.factory.sellerstatus' ),
|
$container->get( 'api.factory.sellerstatus' ),
|
||||||
$container->get( 'api.partner_merchant_id' ),
|
$container->get( 'api.partner_merchant_id' ),
|
||||||
$container->get( 'api.merchant_id' )
|
$container->get( 'api.merchant_id' ),
|
||||||
|
$container->get( 'api.helper.failure-registry' )
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
'api.factory.sellerstatus' => static function ( ContainerInterface $container ) : SellerStatusFactory {
|
'api.factory.sellerstatus' => static function ( ContainerInterface $container ) : SellerStatusFactory {
|
||||||
|
@ -846,6 +848,10 @@ return array(
|
||||||
$purchase_unit_sanitizer = $container->get( 'api.helper.purchase-unit-sanitizer' );
|
$purchase_unit_sanitizer = $container->get( 'api.helper.purchase-unit-sanitizer' );
|
||||||
return new OrderTransient( $cache, $purchase_unit_sanitizer );
|
return new OrderTransient( $cache, $purchase_unit_sanitizer );
|
||||||
},
|
},
|
||||||
|
'api.helper.failure-registry' => static function( ContainerInterface $container ): FailureRegistry {
|
||||||
|
$cache = new Cache( 'ppcp-paypal-api-status-cache' );
|
||||||
|
return new FailureRegistry( $cache );
|
||||||
|
},
|
||||||
'api.helper.purchase-unit-sanitizer' => SingletonDecorator::make(
|
'api.helper.purchase-unit-sanitizer' => SingletonDecorator::make(
|
||||||
static function( ContainerInterface $container ): PurchaseUnitSanitizer {
|
static function( ContainerInterface $container ): PurchaseUnitSanitizer {
|
||||||
$settings = $container->get( 'wcgateway.settings' );
|
$settings = $container->get( 'wcgateway.settings' );
|
||||||
|
|
|
@ -10,6 +10,7 @@ declare(strict_types=1);
|
||||||
namespace WooCommerce\PayPalCommerce\ApiClient;
|
namespace WooCommerce\PayPalCommerce\ApiClient;
|
||||||
|
|
||||||
use WC_Order;
|
use WC_Order;
|
||||||
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\OrderTransient;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Container\ServiceProvider;
|
||||||
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
use WooCommerce\PayPalCommerce\Vendor\Dhii\Modular\Module\ModuleInterface;
|
||||||
|
@ -81,6 +82,18 @@ class ApiModule implements ModuleInterface {
|
||||||
10,
|
10,
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
add_action(
|
||||||
|
'woocommerce_paypal_payments_clear_apm_product_status',
|
||||||
|
function () use ( $c ) {
|
||||||
|
$failure_registry = $c->has( 'api.helper.failure-registry' ) ? $c->get( 'api.helper.failure-registry' ) : null;
|
||||||
|
|
||||||
|
if ( $failure_registry instanceof FailureRegistry ) {
|
||||||
|
$failure_registry->clear_failures( FailureRegistry::SELLER_STATUS_KEY );
|
||||||
|
}
|
||||||
|
},
|
||||||
|
10,
|
||||||
|
2
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -15,6 +15,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Entity\SellerStatus;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Factory\SellerStatusFactory;
|
use WooCommerce\PayPalCommerce\ApiClient\Factory\SellerStatusFactory;
|
||||||
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class PartnersEndpoint
|
* Class PartnersEndpoint
|
||||||
|
@ -65,6 +66,13 @@ class PartnersEndpoint {
|
||||||
*/
|
*/
|
||||||
private $merchant_id;
|
private $merchant_id;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The failure registry.
|
||||||
|
*
|
||||||
|
* @var FailureRegistry
|
||||||
|
*/
|
||||||
|
private $failure_registry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* PartnersEndpoint constructor.
|
* PartnersEndpoint constructor.
|
||||||
*
|
*
|
||||||
|
@ -74,6 +82,7 @@ class PartnersEndpoint {
|
||||||
* @param SellerStatusFactory $seller_status_factory The seller status factory.
|
* @param SellerStatusFactory $seller_status_factory The seller status factory.
|
||||||
* @param string $partner_id The partner ID.
|
* @param string $partner_id The partner ID.
|
||||||
* @param string $merchant_id The merchant ID.
|
* @param string $merchant_id The merchant ID.
|
||||||
|
* @param FailureRegistry $failure_registry The API failure registry.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
string $host,
|
string $host,
|
||||||
|
@ -81,7 +90,8 @@ class PartnersEndpoint {
|
||||||
LoggerInterface $logger,
|
LoggerInterface $logger,
|
||||||
SellerStatusFactory $seller_status_factory,
|
SellerStatusFactory $seller_status_factory,
|
||||||
string $partner_id,
|
string $partner_id,
|
||||||
string $merchant_id
|
string $merchant_id,
|
||||||
|
FailureRegistry $failure_registry
|
||||||
) {
|
) {
|
||||||
$this->host = $host;
|
$this->host = $host;
|
||||||
$this->bearer = $bearer;
|
$this->bearer = $bearer;
|
||||||
|
@ -89,6 +99,7 @@ class PartnersEndpoint {
|
||||||
$this->seller_status_factory = $seller_status_factory;
|
$this->seller_status_factory = $seller_status_factory;
|
||||||
$this->partner_id = $partner_id;
|
$this->partner_id = $partner_id;
|
||||||
$this->merchant_id = $merchant_id;
|
$this->merchant_id = $merchant_id;
|
||||||
|
$this->failure_registry = $failure_registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -125,9 +136,15 @@ class PartnersEndpoint {
|
||||||
'response' => $response,
|
'response' => $response,
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Register the failure on api failure registry.
|
||||||
|
$this->failure_registry->add_failure( FailureRegistry::SELLER_STATUS_KEY );
|
||||||
|
|
||||||
throw $error;
|
throw $error;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->failure_registry->clear_failures( FailureRegistry::SELLER_STATUS_KEY );
|
||||||
|
|
||||||
$json = json_decode( wp_remote_retrieve_body( $response ) );
|
$json = json_decode( wp_remote_retrieve_body( $response ) );
|
||||||
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
$status_code = (int) wp_remote_retrieve_response_code( $response );
|
||||||
if ( 200 !== $status_code ) {
|
if ( 200 !== $status_code ) {
|
||||||
|
|
88
modules/ppcp-api-client/src/Helper/FailureRegistry.php
Normal file
88
modules/ppcp-api-client/src/Helper/FailureRegistry.php
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Failure registry.
|
||||||
|
*
|
||||||
|
* This class is used to remember API failures.
|
||||||
|
* Mostly to prevent multiple failed API requests.
|
||||||
|
*
|
||||||
|
* @package WooCommerce\PayPalCommerce\ApiClient\Helper
|
||||||
|
*/
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace WooCommerce\PayPalCommerce\ApiClient\Helper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class FailureRegistry
|
||||||
|
*/
|
||||||
|
class FailureRegistry {
|
||||||
|
const CACHE_KEY = 'failure_registry';
|
||||||
|
const CACHE_TIMEOUT = DAY_IN_SECONDS; // If necessary we can increase this.
|
||||||
|
|
||||||
|
const SELLER_STATUS_KEY = 'seller_status';
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Cache.
|
||||||
|
*
|
||||||
|
* @var Cache
|
||||||
|
*/
|
||||||
|
private $cache;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FailureRegistry constructor.
|
||||||
|
*
|
||||||
|
* @param Cache $cache The Cache.
|
||||||
|
*/
|
||||||
|
public function __construct( Cache $cache ) {
|
||||||
|
$this->cache = $cache;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @param int $seconds
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has_failure_in_timeframe( string $key, int $seconds ): bool {
|
||||||
|
$cache_key = $this->cache_key( $key );
|
||||||
|
$failure_time = $this->cache->get( $cache_key );
|
||||||
|
|
||||||
|
if ( ! $failure_time ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$expiration = $failure_time + $seconds;
|
||||||
|
return $expiration > time();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function add_failure( string $key ) {
|
||||||
|
$cache_key = $this->cache_key( $key );
|
||||||
|
$this->cache->set( $cache_key, time(), self::CACHE_TIMEOUT );
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $key
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function clear_failures( string $key ) {
|
||||||
|
$cache_key = $this->cache_key( $key );
|
||||||
|
if ( $this->cache->has( $cache_key ) ) {
|
||||||
|
$this->cache->delete( $cache_key );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build cache key.
|
||||||
|
*
|
||||||
|
* @param string $key The cache key.
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
private function cache_key( string $key ): ?string {
|
||||||
|
return implode( '_', array( self::CACHE_KEY, $key ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -17,7 +17,7 @@ use WC_Order;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
use WooCommerce\PayPalCommerce\ApiClient\Entity\Order;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Class OrderHelper
|
* Class OrderTransient
|
||||||
*/
|
*/
|
||||||
class OrderTransient {
|
class OrderTransient {
|
||||||
const CACHE_KEY = 'order_transient';
|
const CACHE_KEY = 'order_transient';
|
||||||
|
|
|
@ -61,7 +61,8 @@ return array(
|
||||||
return new ApmProductStatus(
|
return new ApmProductStatus(
|
||||||
$container->get( 'wcgateway.settings' ),
|
$container->get( 'wcgateway.settings' ),
|
||||||
$container->get( 'api.endpoint.partners' ),
|
$container->get( 'api.endpoint.partners' ),
|
||||||
$container->get( 'onboarding.state' )
|
$container->get( 'onboarding.state' ),
|
||||||
|
$container->get( 'api.helper.failure-registry' )
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
|
|
|
@ -40,6 +40,17 @@ class GooglepayModule implements ModuleInterface {
|
||||||
*/
|
*/
|
||||||
public function run( ContainerInterface $c ): void {
|
public function run( ContainerInterface $c ): void {
|
||||||
|
|
||||||
|
// Clears product status when appropriate.
|
||||||
|
add_action(
|
||||||
|
'woocommerce_paypal_payments_clear_apm_product_status',
|
||||||
|
function( Settings $settings = null ) use ( $c ): void {
|
||||||
|
$apm_status = $c->get( 'googlepay.helpers.apm-product-status' );
|
||||||
|
assert( $apm_status instanceof ApmProductStatus );
|
||||||
|
|
||||||
|
$apm_status->clear( $settings );
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// Check if the module is applicable, correct country, currency, ... etc.
|
// Check if the module is applicable, correct country, currency, ... etc.
|
||||||
if ( ! $c->get( 'googlepay.eligible' ) ) {
|
if ( ! $c->get( 'googlepay.eligible' ) ) {
|
||||||
return;
|
return;
|
||||||
|
@ -113,22 +124,6 @@ class GooglepayModule implements ModuleInterface {
|
||||||
return $settings;
|
return $settings;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// Clears product status when appropriate.
|
|
||||||
add_action(
|
|
||||||
'woocommerce_paypal_payments_clear_apm_product_status',
|
|
||||||
function( Settings $settings = null ) use ( $c ): void {
|
|
||||||
$apm_status = $c->get( 'googlepay.helpers.apm-product-status' );
|
|
||||||
assert( $apm_status instanceof ApmProductStatus );
|
|
||||||
|
|
||||||
if ( ! $settings instanceof Settings ) {
|
|
||||||
$settings = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$apm_status->clear( $settings );
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\Googlepay\Helper;
|
||||||
|
|
||||||
use Throwable;
|
use Throwable;
|
||||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
|
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\PartnersEndpoint;
|
||||||
|
use WooCommerce\PayPalCommerce\ApiClient\Helper\FailureRegistry;
|
||||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||||
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings;
|
||||||
|
|
||||||
|
@ -60,21 +61,31 @@ class ApmProductStatus {
|
||||||
*/
|
*/
|
||||||
private $onboarding_state;
|
private $onboarding_state;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The API failure registry
|
||||||
|
*
|
||||||
|
* @var FailureRegistry
|
||||||
|
*/
|
||||||
|
private $api_failure_registry;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ApmProductStatus constructor.
|
* ApmProductStatus constructor.
|
||||||
*
|
*
|
||||||
* @param Settings $settings The Settings.
|
* @param Settings $settings The Settings.
|
||||||
* @param PartnersEndpoint $partners_endpoint The Partner Endpoint.
|
* @param PartnersEndpoint $partners_endpoint The Partner Endpoint.
|
||||||
* @param State $onboarding_state The onboarding state.
|
* @param State $onboarding_state The onboarding state.
|
||||||
|
* @param FailureRegistry $api_failure_registry The API failure registry.
|
||||||
*/
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
Settings $settings,
|
Settings $settings,
|
||||||
PartnersEndpoint $partners_endpoint,
|
PartnersEndpoint $partners_endpoint,
|
||||||
State $onboarding_state
|
State $onboarding_state,
|
||||||
|
FailureRegistry $api_failure_registry
|
||||||
) {
|
) {
|
||||||
$this->settings = $settings;
|
$this->settings = $settings;
|
||||||
$this->partners_endpoint = $partners_endpoint;
|
$this->partners_endpoint = $partners_endpoint;
|
||||||
$this->onboarding_state = $onboarding_state;
|
$this->onboarding_state = $onboarding_state;
|
||||||
|
$this->api_failure_registry = $api_failure_registry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -83,34 +94,47 @@ class ApmProductStatus {
|
||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function is_active() : bool {
|
public function is_active() : bool {
|
||||||
|
|
||||||
|
// If not onboarded then makes no sense to check status.
|
||||||
if ( ! $this->is_onboarded() ) {
|
if ( ! $this->is_onboarded() ) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If status was already checked on this request return the same result.
|
||||||
if ( null !== $this->current_status ) {
|
if ( null !== $this->current_status ) {
|
||||||
return $this->current_status;
|
return $this->current_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if status was checked on previous requests.
|
||||||
if ( $this->settings->has( self::SETTINGS_KEY ) && ( $this->settings->get( self::SETTINGS_KEY ) ) ) {
|
if ( $this->settings->has( self::SETTINGS_KEY ) && ( $this->settings->get( self::SETTINGS_KEY ) ) ) {
|
||||||
$this->current_status = wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) );
|
$this->current_status = wc_string_to_bool( $this->settings->get( self::SETTINGS_KEY ) );
|
||||||
return $this->current_status;
|
return $this->current_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
// Check API failure registry to prevent multiple failed API requests.
|
||||||
$seller_status = $this->partners_endpoint->seller_status();
|
if ( $this->api_failure_registry->has_failure_in_timeframe( FailureRegistry::SELLER_STATUS_KEY, HOUR_IN_SECONDS ) ) {
|
||||||
} catch ( Throwable $error ) {
|
|
||||||
// It may be a transitory error, don't persist the status.
|
|
||||||
$this->has_request_failure = true;
|
$this->has_request_failure = true;
|
||||||
$this->current_status = false;
|
$this->current_status = false;
|
||||||
return $this->current_status;
|
return $this->current_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Request seller status via PayPal API.
|
||||||
|
try {
|
||||||
|
$seller_status = $this->partners_endpoint->seller_status();
|
||||||
|
} catch ( Throwable $error ) {
|
||||||
|
$this->has_request_failure = true;
|
||||||
|
$this->current_status = false;
|
||||||
|
return $this->current_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the seller status for the intended capability.
|
||||||
foreach ( $seller_status->products() as $product ) {
|
foreach ( $seller_status->products() as $product ) {
|
||||||
if ( $product->name() !== 'PAYMENT_METHODS' ) {
|
if ( $product->name() !== 'PAYMENT_METHODS' ) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ( in_array( self::CAPABILITY_NAME, $product->capabilities(), true ) ) {
|
if ( in_array( self::CAPABILITY_NAME, $product->capabilities(), true ) ) {
|
||||||
|
// Capability found, persist status and return true.
|
||||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_ENABLED );
|
||||||
$this->settings->persist();
|
$this->settings->persist();
|
||||||
|
|
||||||
|
@ -119,6 +143,7 @@ class ApmProductStatus {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capability not found, persist status and return false.
|
||||||
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED );
|
$this->settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_DISABLED );
|
||||||
$this->settings->persist();
|
$this->settings->persist();
|
||||||
|
|
||||||
|
@ -158,8 +183,10 @@ class ApmProductStatus {
|
||||||
|
|
||||||
$this->current_status = null;
|
$this->current_status = null;
|
||||||
|
|
||||||
$settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_UNDEFINED );
|
if ( $settings->has( self::SETTINGS_KEY ) ) {
|
||||||
$settings->persist();
|
$settings->set( self::SETTINGS_KEY, self::SETTINGS_VALUE_UNDEFINED );
|
||||||
|
$settings->persist();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue