diff --git a/modules/ppcp-api-client/src/Endpoint/class-webhookendpoint.php b/modules/ppcp-api-client/src/Endpoint/class-webhookendpoint.php index 7f08587d9..7e5f02315 100644 --- a/modules/ppcp-api-client/src/Endpoint/class-webhookendpoint.php +++ b/modules/ppcp-api-client/src/Endpoint/class-webhookendpoint.php @@ -118,6 +118,46 @@ class WebhookEndpoint { return $hook; } + /** + * Loads the webhooks list for the current auth token. + * + * @return Webhook[] + * @throws RuntimeException If the request fails. + * @throws PayPalApiException If the request fails. + */ + public function list(): array { + $bearer = $this->bearer->bearer(); + $url = trailingslashit( $this->host ) . 'v1/notifications/webhooks'; + $args = array( + 'method' => 'GET', + 'headers' => array( + 'Authorization' => 'Bearer ' . $bearer->token(), + 'Content-Type' => 'application/json', + ), + ); + $response = $this->request( $url, $args ); + + if ( is_wp_error( $response ) ) { + throw new RuntimeException( + __( 'Not able to load webhooks list.', 'woocommerce-paypal-payments' ) + ); + } + + $json = json_decode( $response['body'] ); + $status_code = (int) wp_remote_retrieve_response_code( $response ); + if ( 200 !== $status_code ) { + throw new PayPalApiException( + $json, + $status_code + ); + } + + return array_map( + array( $this->webhook_factory, 'from_paypal_response' ), + $json->webhooks + ); + } + /** * Deletes a webhook. * diff --git a/modules/ppcp-api-client/src/Entity/class-webhook.php b/modules/ppcp-api-client/src/Entity/class-webhook.php index 71ff8b5fe..b2e8ab2dd 100644 --- a/modules/ppcp-api-client/src/Entity/class-webhook.php +++ b/modules/ppcp-api-client/src/Entity/class-webhook.php @@ -9,6 +9,8 @@ declare(strict_types=1); namespace WooCommerce\PayPalCommerce\ApiClient\Entity; +use stdClass; + /** * Class Webhook */ @@ -71,13 +73,38 @@ class Webhook { /** * Returns the event types. * - * @return array + * @return stdClass[] */ public function event_types(): array { return $this->event_types; } + /** + * Returns the human-friendly names of the event types. + * + * @return string[] + */ + public function humanfriendly_event_names(): array { + + return array_map( + function ( $event ): string { + return Webhook::get_humanfriendly_event_name( $event->name ); + }, + $this->event_types + ); + } + + /** + * Converts event names to more human-friendly form. + * + * @param string $name The event name like 'CHECKOUT.ORDER.APPROVED'. + * @return string + */ + public static function get_humanfriendly_event_name( string $name ): string { + return strtolower( str_replace( '.', ' ', $name ) ); + } + /** * Returns the object as array. * diff --git a/modules/ppcp-wc-gateway/services.php b/modules/ppcp-wc-gateway/services.php index c977e588e..ab228be00 100644 --- a/modules/ppcp-wc-gateway/services.php +++ b/modules/ppcp-wc-gateway/services.php @@ -38,6 +38,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\SectionsRenderer; use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsListener; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; +use WooCommerce\PayPalCommerce\Webhooks\Status\WebhooksStatusPage; return array( 'wcgateway.paypal-gateway' => static function ( $container ): PayPalGateway { @@ -118,7 +119,7 @@ return array( } $section = isset( $_GET['section'] ) ? sanitize_text_field( wp_unslash( $_GET['section'] ) ) : ''; - return in_array( $section, array( PayPalGateway::ID, CreditCardGateway::ID ), true ); + return in_array( $section, array( PayPalGateway::ID, CreditCardGateway::ID, WebhooksStatusPage::ID ), true ); }, 'wcgateway.current-ppcp-settings-page-id' => static function ( $container ): string { @@ -700,7 +701,7 @@ return array( State::STATE_ONBOARDED, ), 'requirements' => array(), - 'gateway' => 'all', + 'gateway' => array( 'paypal', 'dcc' ), ), 'logging_enabled' => array( 'title' => __( 'Logging', 'woocommerce-paypal-payments' ), diff --git a/modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php b/modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php index da7597abb..4ca2f947b 100644 --- a/modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php +++ b/modules/ppcp-wc-gateway/src/Gateway/class-paypalgateway.php @@ -19,6 +19,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Processor\RefundProcessor; use WooCommerce\PayPalCommerce\WcGateway\Settings\SectionsRenderer; use WooCommerce\PayPalCommerce\WcGateway\Settings\SettingsRenderer; use Psr\Container\ContainerInterface; +use WooCommerce\PayPalCommerce\Webhooks\Status\WebhooksStatusPage; /** * Class PayPalGateway @@ -224,7 +225,7 @@ class PayPalGateway extends \WC_Payment_Gateway { ), ); - $should_show_enabled_checkbox = ! $this->is_credit_card_tab() && ( $this->config->has( 'merchant_email' ) && $this->config->get( 'merchant_email' ) ); + $should_show_enabled_checkbox = $this->is_paypal_tab() && ( $this->config->has( 'merchant_email' ) && $this->config->get( 'merchant_email' ) ); if ( ! $should_show_enabled_checkbox ) { unset( $this->form_fields['enabled'] ); } @@ -308,6 +309,9 @@ class PayPalGateway extends \WC_Payment_Gateway { if ( $this->is_credit_card_tab() ) { return __( 'PayPal Card Processing', 'woocommerce-paypal-payments' ); } + if ( $this->is_webhooks_tab() ) { + return __( 'Webhooks Status', 'woocommerce-paypal-payments' ); + } if ( $this->is_paypal_tab() ) { return __( 'PayPal Checkout', 'woocommerce-paypal-payments' ); } @@ -326,6 +330,12 @@ class PayPalGateway extends \WC_Payment_Gateway { 'woocommerce-paypal-payments' ); } + if ( $this->is_webhooks_tab() ) { + return __( + 'Status of the webhooks subscription.', + 'woocommerce-paypal-payments' + ); + } return __( 'Accept PayPal, Pay Later and alternative payment types.', @@ -346,6 +356,16 @@ class PayPalGateway extends \WC_Payment_Gateway { } + /** + * Whether we are on the Webhooks Status tab. + * + * @return bool + */ + private function is_webhooks_tab() : bool { + return is_admin() + && WebhooksStatusPage::ID === $this->page_id; + } + /** * Whether we are on the PayPal settings tab. * diff --git a/modules/ppcp-wc-gateway/src/Settings/class-pagematchertrait.php b/modules/ppcp-wc-gateway/src/Settings/class-pagematchertrait.php new file mode 100644 index 000000000..f47388c85 --- /dev/null +++ b/modules/ppcp-wc-gateway/src/Settings/class-pagematchertrait.php @@ -0,0 +1,49 @@ + 'paypal', + CreditCardGateway::ID => 'dcc', // TODO: consider using just the gateway ID for PayPal and DCC too. + WebhooksStatusPage::ID => WebhooksStatusPage::ID, + ); + return array_key_exists( $current_page_id, $gateway_page_id_map ) + && in_array( $gateway_page_id_map[ $current_page_id ], $allowed_gateways, true ); + } +} diff --git a/modules/ppcp-wc-gateway/src/Settings/class-sectionsrenderer.php b/modules/ppcp-wc-gateway/src/Settings/class-sectionsrenderer.php index ea9e693f5..2bddabe1b 100644 --- a/modules/ppcp-wc-gateway/src/Settings/class-sectionsrenderer.php +++ b/modules/ppcp-wc-gateway/src/Settings/class-sectionsrenderer.php @@ -11,6 +11,7 @@ namespace WooCommerce\PayPalCommerce\WcGateway\Settings; use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway; use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway; +use WooCommerce\PayPalCommerce\Webhooks\Status\WebhooksStatusPage; /** * Class SectionsRenderer @@ -53,8 +54,9 @@ class SectionsRenderer { } $sections = array( - PayPalGateway::ID => __( 'PayPal Checkout', 'woocommerce-paypal-payments' ), - CreditCardGateway::ID => __( 'PayPal Card Processing', 'woocommerce-paypal-payments' ), + PayPalGateway::ID => __( 'PayPal Checkout', 'woocommerce-paypal-payments' ), + CreditCardGateway::ID => __( 'PayPal Card Processing', 'woocommerce-paypal-payments' ), + WebhooksStatusPage::ID => __( 'Webhooks Status', 'woocommerce-paypal-payments' ), ); echo '