Merge branch 'trunk' into PCP-359-customer-details-not-available-i

This commit is contained in:
dinamiko 2021-11-26 15:30:26 +01:00
commit b3b1237247
12 changed files with 468 additions and 44 deletions

View file

@ -12,6 +12,7 @@ namespace WooCommerce\PayPalCommerce\ApiClient;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\IdentityToken; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\IdentityToken;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\LoginSeller; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\LoginSeller;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint; use WooCommerce\PayPalCommerce\ApiClient\Endpoint\OrderEndpoint;
@ -39,6 +40,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookEventFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookEventFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory; use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache; use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencySupport;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository; use WooCommerce\PayPalCommerce\ApiClient\Repository\ApplicationContextRepository;
use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository; use WooCommerce\PayPalCommerce\ApiClient\Repository\CartRepository;
@ -190,6 +192,13 @@ return array(
$subscription_helper $subscription_helper
); );
}, },
'api.endpoint.billing-agreements' => static function ( ContainerInterface $container ): BillingAgreementsEndpoint {
return new BillingAgreementsEndpoint(
$container->get( 'api.host' ),
$container->get( 'api.bearer' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
'api.repository.paypal-request-id' => static function( ContainerInterface $container ) : PayPalRequestIdRepository { 'api.repository.paypal-request-id' => static function( ContainerInterface $container ) : PayPalRequestIdRepository {
return new PayPalRequestIdRepository(); return new PayPalRequestIdRepository();
}, },
@ -302,4 +311,7 @@ return array(
'api.helpers.dccapplies' => static function ( ContainerInterface $container ) : DccApplies { 'api.helpers.dccapplies' => static function ( ContainerInterface $container ) : DccApplies {
return new DccApplies(); return new DccApplies();
}, },
'api.helpers.currency-support' => static function ( ContainerInterface $container ) : CurrencySupport {
return new CurrencySupport();
},
); );

View file

@ -0,0 +1,134 @@
<?php
/**
* The billing agreements endpoint.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Endpoint
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
use stdClass;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use Psr\Log\LoggerInterface;
/**
* Class BillingAgreementsEndpoint
*/
class BillingAgreementsEndpoint {
use RequestTrait;
/**
* The host.
*
* @var string
*/
private $host;
/**
* The bearer.
*
* @var Bearer
*/
private $bearer;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* BillingAgreementsEndpoint constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
string $host,
Bearer $bearer,
LoggerInterface $logger
) {
$this->host = $host;
$this->bearer = $bearer;
$this->logger = $logger;
}
/**
* Creates a billing agreement token.
*
* @param string $description The description.
* @param string $return_url The return URL.
* @param string $cancel_url The cancel URL.
*
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function create_token( string $description, string $return_url, string $cancel_url ): stdClass {
$data = array(
'description' => $description,
'payer' => array(
'payment_method' => 'PAYPAL',
),
'plan' => array(
'type' => 'MERCHANT_INITIATED_BILLING',
'merchant_preferences' => array(
'return_url' => $return_url,
'cancel_url' => $cancel_url,
'skip_shipping_address' => true,
),
),
);
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/billing-agreements/agreement-tokens';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
),
'body' => wp_json_encode( $data ),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) || ! is_array( $response ) ) {
throw new RuntimeException( 'Not able to create a billing agreement token.' );
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 201 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
return $json;
}
/**
* Checks if reference transactions are enabled in account.
*
* @throws RuntimeException If the request fails (no auth, no connection, etc.).
*/
public function reference_transaction_enabled(): bool {
try {
$this->create_token(
'Checking if reference transactions are enabled',
'https://example.com/return',
'https://example.com/cancel'
);
return true;
} catch ( PayPalApiException $exception ) {
return false;
}
}
}

View file

@ -0,0 +1,70 @@
<?php
/**
* Checks if the current installation uses supported currency.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Helper
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Helper;
/**
* Class CurrencySupport
*/
class CurrencySupport {
/**
* Currencies supported by PayPal.
*
* From https://developer.paypal.com/docs/reports/reference/paypal-supported-currencies/
*
* @var string[]
*/
private $supported_currencies = array(
'AUD',
'BRL',
'CAD',
'CNY',
'CZK',
'DKK',
'EUR',
'HKD',
'HUF',
'ILS',
'JPY',
'MYR',
'MXN',
'TWD',
'NZD',
'NOK',
'PHP',
'PLN',
'GBP',
'RUB',
'SGD',
'SEK',
'CHF',
'THB',
'USD',
);
/**
* Returns whether the given currency is supported.
*
* @param string $currency 3-letter currency code.
* @return bool
*/
public function supports_currency( string $currency ): bool {
return in_array( $currency, $this->supported_currencies, true );
}
/**
* Returns whether the current WC currency is supported.
*
* @return bool
*/
public function supports_wc_currency(): bool {
return $this->supports_currency( get_woocommerce_currency() );
}
}

View file

@ -73,11 +73,11 @@ class ErrorHandler {
clear() clear()
{ {
if (! this.wrapper.classList.contains('woocommerce-error')) { if (this.messagesList === null) {
return; return;
} }
this.wrapper.classList.remove('woocommerce-error');
this.wrapper.innerText = ''; this.messagesList.innerHTML = '';
} }
} }

View file

@ -29,6 +29,15 @@ class PPECHelper {
const PPEC_SETTINGS_OPTION_NAME = 'woocommerce_ppec_paypal_settings'; const PPEC_SETTINGS_OPTION_NAME = 'woocommerce_ppec_paypal_settings';
/**
* Checks if the PayPal Express Checkout plugin was configured previously.
*
* @return bool
*/
public static function is_plugin_configured() {
return is_array( get_option( self::PPEC_SETTINGS_OPTION_NAME ) );
}
/** /**
* Checks if the PayPal Express Checkout plugin is active. * Checks if the PayPal Express Checkout plugin is active.
* *

View file

@ -37,9 +37,11 @@ class Renderer {
foreach ( $items as $item ) { foreach ( $items as $item ) {
?> ?>
<tr> <tr>
<td data-export-label="<?php echo esc_attr( $item['label'] ); ?>"><?php echo esc_attr( $item['label'] ); ?></td> <td data-export-label="<?php echo esc_attr( $item['exported_label'] ?? $item['label'] ); ?>">
<?php echo esc_attr( $item['label'] ); ?>
</td>
<td class="help"><?php echo wc_help_tip( $item['description'] ); ?></td> <td class="help"><?php echo wc_help_tip( $item['description'] ); ?></td>
<td><?php echo esc_attr( $item['value'] ); ?></td> <td><?php echo wp_kses_post( $item['value'] ); ?></td>
</tr> </tr>
<?php <?php
} }

View file

@ -15,10 +15,14 @@ use Interop\Container\ServiceProviderInterface;
use Psr\Container\ContainerInterface; use Psr\Container\ContainerInterface;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer; use WooCommerce\PayPalCommerce\ApiClient\Authentication\PayPalBearer;
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\BillingAgreementsEndpoint;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException; use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Helper\CurrencySupport;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies; use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply; use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
use WooCommerce\PayPalCommerce\Compat\PPEC\PPECHelper;
use WooCommerce\PayPalCommerce\Onboarding\State; use WooCommerce\PayPalCommerce\Onboarding\State;
use WooCommerce\PayPalCommerce\Webhooks\WebhookInfoStorage;
/** /**
* Class StatusReportModule * Class StatusReportModule
@ -44,6 +48,8 @@ class StatusReportModule implements ModuleInterface {
add_action( add_action(
'woocommerce_system_status_report', 'woocommerce_system_status_report',
function () use ( $c ) { function () use ( $c ) {
$settings = $c->get( 'wcgateway.settings' );
assert( $settings instanceof ContainerInterface );
/* @var State $state The state. */ /* @var State $state The state. */
$state = $c->get( 'onboarding.state' ); $state = $c->get( 'onboarding.state' );
@ -51,44 +57,102 @@ class StatusReportModule implements ModuleInterface {
/* @var Bearer $bearer The bearer. */ /* @var Bearer $bearer The bearer. */
$bearer = $c->get( 'api.bearer' ); $bearer = $c->get( 'api.bearer' );
$currency_support = $c->get( 'api.helpers.currency-support' );
assert( $currency_support instanceof CurrencySupport );
/* @var DccApplies $dcc_applies The ddc applies. */ /* @var DccApplies $dcc_applies The ddc applies. */
$dcc_applies = $c->get( 'api.helpers.dccapplies' ); $dcc_applies = $c->get( 'api.helpers.dccapplies' );
/* @var MessagesApply $messages_apply The messages apply. */ /* @var MessagesApply $messages_apply The messages apply. */
$messages_apply = $c->get( 'button.helper.messages-apply' ); $messages_apply = $c->get( 'button.helper.messages-apply' );
$last_webhook_storage = $c->get( 'webhook.last-webhook-storage' );
assert( $last_webhook_storage instanceof WebhookInfoStorage );
$billing_agreements_endpoint = $c->get( 'api.endpoint.billing-agreements' );
assert( $billing_agreements_endpoint instanceof BillingAgreementsEndpoint );
/* @var Renderer $renderer The renderer. */ /* @var Renderer $renderer The renderer. */
$renderer = $c->get( 'status-report.renderer' ); $renderer = $c->get( 'status-report.renderer' );
$had_ppec_plugin = PPECHelper::is_plugin_configured();
$items = array( $items = array(
array( array(
'label' => esc_html__( 'Onboarded', 'woocommerce-paypal-payments' ), 'label' => esc_html__( 'Onboarded', 'woocommerce-paypal-payments' ),
'description' => esc_html__( 'Whether PayPal account is correctly configured or not.', 'woocommerce-paypal-payments' ), 'exported_label' => 'Onboarded',
'value' => $this->onboarded( $bearer, $state ), 'description' => esc_html__( 'Whether PayPal account is correctly configured or not.', 'woocommerce-paypal-payments' ),
'value' => $this->bool_to_html(
$this->onboarded( $bearer, $state )
),
), ),
array( array(
'label' => esc_html__( 'Shop country code', 'woocommerce-paypal-payments' ), 'label' => esc_html__( 'Shop country code', 'woocommerce-paypal-payments' ),
'description' => esc_html__( 'Country / State value on Settings / General / Store Address.', 'woocommerce-paypal-payments' ), 'exported_label' => 'Shop country code',
'value' => wc_get_base_location()['country'], 'description' => esc_html__( 'Country / State value on Settings / General / Store Address.', 'woocommerce-paypal-payments' ),
'value' => wc_get_base_location()['country'],
), ),
array( array(
'label' => esc_html__( 'PayPal card processing available in country', 'woocommerce-paypal-payments' ), 'label' => esc_html__( 'WooCommerce currency supported', 'woocommerce-paypal-payments' ),
'description' => esc_html__( 'Whether PayPal card processing is available in country or not.', 'woocommerce-paypal-payments' ), 'exported_label' => 'WooCommerce currency supported',
'value' => $dcc_applies->for_country_currency() 'description' => esc_html__( 'Whether PayPal supports the default store currency or not.', 'woocommerce-paypal-payments' ),
? esc_html__( 'Yes', 'woocommerce-paypal-payments' ) 'value' => $this->bool_to_html(
: esc_html__( 'No', 'woocommerce-paypal-payments' ), $currency_support->supports_wc_currency()
),
), ),
array( array(
'label' => esc_html__( 'Pay Later messaging available in country', 'woocommerce-paypal-payments' ), 'label' => esc_html__( 'PayPal card processing available in country', 'woocommerce-paypal-payments' ),
'description' => esc_html__( 'Whether Pay Later is available in country or not.', 'woocommerce-paypal-payments' ), 'exported_label' => 'PayPal card processing available in country',
'value' => $messages_apply->for_country() 'description' => esc_html__( 'Whether PayPal card processing is available in country or not.', 'woocommerce-paypal-payments' ),
? esc_html__( 'Yes', 'woocommerce-paypal-payments' ) 'value' => $this->bool_to_html(
: esc_html__( 'No', 'woocommerce-paypal-payments' ), $dcc_applies->for_country_currency()
),
), ),
array( array(
'label' => esc_html__( 'Vault enabled', 'woocommerce-paypal-payments' ), 'label' => esc_html__( 'Pay Later messaging available in country', 'woocommerce-paypal-payments' ),
'description' => esc_html__( 'Whether vaulting is enabled on PayPal account or not.', 'woocommerce-paypal-payments' ), 'exported_label' => 'Pay Later messaging available in country',
'value' => $this->vault_enabled( $bearer ), 'description' => esc_html__( 'Whether Pay Later is available in country or not.', 'woocommerce-paypal-payments' ),
'value' => $this->bool_to_html(
$messages_apply->for_country()
),
),
array(
'label' => esc_html__( 'Webhook status', 'woocommerce-paypal-payments' ),
'exported_label' => 'Webhook status',
'description' => esc_html__( 'Whether we received webhooks successfully.', 'woocommerce-paypal-payments' ),
'value' => $this->bool_to_html( ! $last_webhook_storage->is_empty() ),
),
array(
'label' => esc_html__( 'Vault enabled', 'woocommerce-paypal-payments' ),
'exported_label' => 'Vault enabled',
'description' => esc_html__( 'Whether vaulting is enabled on PayPal account or not.', 'woocommerce-paypal-payments' ),
'value' => $this->bool_to_html(
$this->vault_enabled( $bearer )
),
),
array(
'label' => esc_html__( 'Logging enabled', 'woocommerce-paypal-payments' ),
'exported_label' => 'Logging enabled',
'description' => esc_html__( 'Whether logging of plugin events and errors is enabled.', 'woocommerce-paypal-payments' ),
'value' => $this->bool_to_html(
$settings->has( 'logging_enabled' ) && $settings->get( 'logging_enabled' )
),
),
array(
'label' => esc_html__( 'Reference Transactions', 'woocommerce-paypal-payments' ),
'exported_label' => 'Reference Transactions',
'description' => esc_html__( 'Whether Reference Transactions are enabled for the connected account', 'woocommerce-paypal-payments' ),
'value' => $this->bool_to_html(
$this->reference_transaction_enabled( $billing_agreements_endpoint )
),
),
array(
'label' => esc_html__( 'Used PayPal Checkout plugin', 'woocommerce-paypal-payments' ),
'exported_label' => 'Used PayPal Checkout plugin',
'description' => esc_html__( 'Whether the PayPal Checkout Gateway plugin was configured previously or not', 'woocommerce-paypal-payments' ),
'value' => $this->bool_to_html(
$had_ppec_plugin
),
), ),
); );
@ -112,37 +176,56 @@ class StatusReportModule implements ModuleInterface {
* *
* @param Bearer $bearer The bearer. * @param Bearer $bearer The bearer.
* @param State $state The state. * @param State $state The state.
* @return string * @return bool
*/ */
private function onboarded( $bearer, $state ): string { private function onboarded( Bearer $bearer, State $state ): bool {
try { try {
$token = $bearer->bearer(); $token = $bearer->bearer();
} catch ( RuntimeException $exception ) { } catch ( RuntimeException $exception ) {
return esc_html__( 'No', 'woocommerce-paypal-payments' ); return false;
} }
$current_state = $state->current_state(); $current_state = $state->current_state();
if ( $token->is_valid() && $current_state === $state::STATE_ONBOARDED ) { return $token->is_valid() && $current_state === $state::STATE_ONBOARDED;
return esc_html__( 'Yes', 'woocommerce-paypal-payments' );
}
return esc_html__( 'No', 'woocommerce-paypal-payments' );
} }
/** /**
* It returns whether vaulting is enabled or not. * It returns whether vaulting is enabled or not.
* *
* @param Bearer $bearer The bearer. * @param Bearer $bearer The bearer.
* @return string * @return bool
*/ */
private function vault_enabled( $bearer ) { private function vault_enabled( Bearer $bearer ): bool {
try { try {
$token = $bearer->bearer(); $token = $bearer->bearer();
return $token->vaulting_available() return $token->vaulting_available();
? esc_html__( 'Yes', 'woocommerce-paypal-payments' )
: esc_html__( 'No', 'woocommerce-paypal-payments' );
} catch ( RuntimeException $exception ) { } catch ( RuntimeException $exception ) {
return esc_html__( 'No', 'woocommerce-paypal-payments' ); return false;
} }
} }
/**
* Checks if reference transactions are enabled in account.
*
* @param BillingAgreementsEndpoint $billing_agreements_endpoint The endpoint.
*/
private function reference_transaction_enabled( BillingAgreementsEndpoint $billing_agreements_endpoint ): bool {
try {
return $billing_agreements_endpoint->reference_transaction_enabled();
} catch ( RuntimeException $exception ) {
return false;
}
}
/**
* Converts the bool value to "yes" icon or dash.
*
* @param bool $value The value.
* @return string
*/
private function bool_to_html( bool $value ): string {
return $value
? '<mark class="yes"><span class="dashicons dashicons-yes"></span></mark>'
: '<mark class="no">&ndash;</mark>';
}
} }

View file

@ -32,11 +32,13 @@ return array(
$factory = $container->get( 'api.factory.webhook' ); $factory = $container->get( 'api.factory.webhook' );
$endpoint = $container->get( 'api.endpoint.webhook' ); $endpoint = $container->get( 'api.endpoint.webhook' );
$rest_endpoint = $container->get( 'webhook.endpoint.controller' ); $rest_endpoint = $container->get( 'webhook.endpoint.controller' );
$last_webhook_storage = $container->get( 'webhook.last-webhook-storage' );
$logger = $container->get( 'woocommerce.logger.woocommerce' ); $logger = $container->get( 'woocommerce.logger.woocommerce' );
return new WebhookRegistrar( return new WebhookRegistrar(
$factory, $factory,
$endpoint, $endpoint,
$rest_endpoint, $rest_endpoint,
$last_webhook_storage,
$logger $logger
); );
}, },
@ -48,6 +50,7 @@ return array(
$verify_request = ! defined( 'PAYPAL_WEBHOOK_REQUEST_VERIFICATION' ) || PAYPAL_WEBHOOK_REQUEST_VERIFICATION; $verify_request = ! defined( 'PAYPAL_WEBHOOK_REQUEST_VERIFICATION' ) || PAYPAL_WEBHOOK_REQUEST_VERIFICATION;
$webhook_event_factory = $container->get( 'api.factory.webhook-event' ); $webhook_event_factory = $container->get( 'api.factory.webhook-event' );
$simulation = $container->get( 'webhook.status.simulation' ); $simulation = $container->get( 'webhook.status.simulation' );
$last_webhook_storage = $container->get( 'webhook.last-webhook-storage' );
return new IncomingWebhookEndpoint( return new IncomingWebhookEndpoint(
$webhook_endpoint, $webhook_endpoint,
@ -56,6 +59,7 @@ return array(
$verify_request, $verify_request,
$webhook_event_factory, $webhook_event_factory,
$simulation, $simulation,
$last_webhook_storage,
... $handler ... $handler
); );
}, },
@ -183,6 +187,13 @@ return array(
); );
}, },
'webhook.last-webhook-storage' => static function ( ContainerInterface $container ): WebhookInfoStorage {
return new WebhookInfoStorage( $container->get( 'webhook.last-webhook-storage.key' ) );
},
'webhook.last-webhook-storage.key' => static function ( ContainerInterface $container ): string {
return 'ppcp-last-webhook';
},
'webhook.module-url' => static function ( ContainerInterface $container ): string { 'webhook.module-url' => static function ( ContainerInterface $container ): string {
return plugins_url( return plugins_url(
'/modules/ppcp-webhooks/', '/modules/ppcp-webhooks/',

View file

@ -76,6 +76,13 @@ class IncomingWebhookEndpoint {
*/ */
private $simulation; private $simulation;
/**
* The last webhook info storage.
*
* @var WebhookInfoStorage
*/
private $last_webhook_storage;
/** /**
* IncomingWebhookEndpoint constructor. * IncomingWebhookEndpoint constructor.
* *
@ -85,6 +92,7 @@ class IncomingWebhookEndpoint {
* @param bool $verify_request Whether requests need to be verified or not. * @param bool $verify_request Whether requests need to be verified or not.
* @param WebhookEventFactory $webhook_event_factory The webhook event factory. * @param WebhookEventFactory $webhook_event_factory The webhook event factory.
* @param WebhookSimulation $simulation The simulation handler. * @param WebhookSimulation $simulation The simulation handler.
* @param WebhookInfoStorage $last_webhook_storage The last webhook info storage.
* @param RequestHandler ...$handlers The handlers, which process a request in the end. * @param RequestHandler ...$handlers The handlers, which process a request in the end.
*/ */
public function __construct( public function __construct(
@ -94,6 +102,7 @@ class IncomingWebhookEndpoint {
bool $verify_request, bool $verify_request,
WebhookEventFactory $webhook_event_factory, WebhookEventFactory $webhook_event_factory,
WebhookSimulation $simulation, WebhookSimulation $simulation,
WebhookInfoStorage $last_webhook_storage,
RequestHandler ...$handlers RequestHandler ...$handlers
) { ) {
@ -103,6 +112,7 @@ class IncomingWebhookEndpoint {
$this->logger = $logger; $this->logger = $logger;
$this->verify_request = $verify_request; $this->verify_request = $verify_request;
$this->webhook_event_factory = $webhook_event_factory; $this->webhook_event_factory = $webhook_event_factory;
$this->last_webhook_storage = $last_webhook_storage;
$this->simulation = $simulation; $this->simulation = $simulation;
} }
@ -176,6 +186,8 @@ class IncomingWebhookEndpoint {
public function handle_request( \WP_REST_Request $request ): \WP_REST_Response { public function handle_request( \WP_REST_Request $request ): \WP_REST_Response {
$event = $this->event_from_request( $request ); $event = $this->event_from_request( $request );
$this->last_webhook_storage->save( $event );
if ( $this->simulation->is_simulation_event( $event ) ) { if ( $this->simulation->is_simulation_event( $event ) ) {
$this->logger->info( 'Received simulated webhook.' ); $this->logger->info( 'Received simulated webhook.' );
$this->simulation->receive( $event ); $this->simulation->receive( $event );

View file

@ -0,0 +1,76 @@
<?php
/**
* Stores the info about webhook events.
*
* @package WooCommerce\PayPalCommerce\Webhooks
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\Webhooks;
use WooCommerce\PayPalCommerce\ApiClient\Entity\WebhookEvent;
/**
* Class WebhookInfoStorage
*/
class WebhookInfoStorage {
/**
* The WP option key.
*
* @var string
*/
private $key;
/**
* WebhookInfoStorage constructor.
*
* @param string $key The WP option key.
*/
public function __construct(
string $key
) {
$this->key = $key;
}
/**
* Saves the info about webhook event.
*
* @param WebhookEvent $webhook_event The webhook event to save.
*/
public function save( WebhookEvent $webhook_event ): void {
$data = array(
'id' => $webhook_event->id(),
'received_time' => time(),
);
update_option( $this->key, $data );
}
/**
* Returns the stored data or null.
*/
public function get_data(): ?array {
$data = get_option( $this->key );
if ( ! $data || ! is_array( $data ) ) {
return null;
}
return $data;
}
/**
* Checks if there is any stored data.
*/
public function is_empty(): bool {
$data = get_option( $this->key );
return ! $data || ! is_array( $data );
}
/**
* Removes the stored data.
*/
public function clear(): void {
delete_option( $this->key );
}
}

View file

@ -44,6 +44,13 @@ class WebhookRegistrar {
*/ */
private $rest_endpoint; private $rest_endpoint;
/**
* The last webhook info storage.
*
* @var WebhookInfoStorage
*/
private $last_webhook_storage;
/** /**
* The logger. * The logger.
* *
@ -57,19 +64,22 @@ class WebhookRegistrar {
* @param WebhookFactory $webhook_factory The Webhook factory. * @param WebhookFactory $webhook_factory The Webhook factory.
* @param WebhookEndpoint $endpoint The Webhook endpoint. * @param WebhookEndpoint $endpoint The Webhook endpoint.
* @param IncomingWebhookEndpoint $rest_endpoint The WordPress Rest API endpoint. * @param IncomingWebhookEndpoint $rest_endpoint The WordPress Rest API endpoint.
* @param WebhookInfoStorage $last_webhook_storage The last webhook info storage.
* @param LoggerInterface $logger The logger. * @param LoggerInterface $logger The logger.
*/ */
public function __construct( public function __construct(
WebhookFactory $webhook_factory, WebhookFactory $webhook_factory,
WebhookEndpoint $endpoint, WebhookEndpoint $endpoint,
IncomingWebhookEndpoint $rest_endpoint, IncomingWebhookEndpoint $rest_endpoint,
WebhookInfoStorage $last_webhook_storage,
LoggerInterface $logger LoggerInterface $logger
) { ) {
$this->webhook_factory = $webhook_factory; $this->webhook_factory = $webhook_factory;
$this->endpoint = $endpoint; $this->endpoint = $endpoint;
$this->rest_endpoint = $rest_endpoint; $this->rest_endpoint = $rest_endpoint;
$this->logger = $logger; $this->last_webhook_storage = $last_webhook_storage;
$this->logger = $logger;
} }
/** /**
@ -92,6 +102,7 @@ class WebhookRegistrar {
self::KEY, self::KEY,
$created->to_array() $created->to_array()
); );
$this->last_webhook_storage->clear();
$this->logger->info( 'Webhooks subscribed.' ); $this->logger->info( 'Webhooks subscribed.' );
return true; return true;
} catch ( RuntimeException $error ) { } catch ( RuntimeException $error ) {
@ -120,6 +131,7 @@ class WebhookRegistrar {
if ( $success ) { if ( $success ) {
delete_option( self::KEY ); delete_option( self::KEY );
$this->last_webhook_storage->clear();
$this->logger->info( 'Webhooks deleted.' ); $this->logger->info( 'Webhooks deleted.' );
} }
return $success; return $success;

View file

@ -12,20 +12,23 @@ class RendererTest extends TestCase
{ {
$items = [ $items = [
[ [
'label' => 'Foo', 'label' => 'Translated Foo',
'exported_label' => 'Foo',
'description' => 'Bar', 'description' => 'Bar',
'value' => 'Baz' 'value' => 'Baz'
], ],
]; ];
when('esc_attr')->returnArg(); when('esc_attr')->returnArg();
when('wp_kses_post')->returnArg();
when('wc_help_tip')->returnArg(); when('wc_help_tip')->returnArg();
$testee = new Renderer(); $testee = new Renderer();
$result = $testee->render('Some title here', $items); $result = $testee->render('Some title here', $items);
self::assertStringContainsString('<h2>Some title here</h2>', $result); self::assertStringContainsString('<h2>Some title here</h2>', $result);
self::assertStringContainsString('<td data-export-label="Foo">Foo</td>', $result); self::assertStringContainsString('data-export-label="Foo"', $result);
self::assertStringContainsString('Translated Foo', $result);
self::assertStringContainsString('<td class="help">Bar</td>', $result); self::assertStringContainsString('<td class="help">Bar</td>', $result);
self::assertStringContainsString('<td>Baz</td>', $result); self::assertStringContainsString('<td>Baz</td>', $result);
} }