woocommerce-paypal-payments/modules/ppcp-webhooks/src/class-incomingwebhookendpoint.php

261 lines
6.1 KiB
PHP
Raw Normal View History

2020-08-27 13:10:16 +03:00
<?php
/**
* Controls the endpoint for the incoming webhooks.
*
2020-09-11 14:11:10 +03:00
* @package WooCommerce\PayPalCommerce\Webhooks
2020-08-27 13:10:16 +03:00
*/
declare(strict_types=1);
2020-09-11 14:11:10 +03:00
namespace WooCommerce\PayPalCommerce\Webhooks;
2020-08-27 13:10:16 +03:00
2021-09-22 17:13:38 +03:00
use phpDocumentor\Reflection\Types\This;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\WebhookEndpoint;
2021-09-21 17:52:44 +03:00
use WooCommerce\PayPalCommerce\ApiClient\Entity\Webhook;
2021-09-22 17:13:38 +03:00
use WooCommerce\PayPalCommerce\ApiClient\Entity\WebhookEvent;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
2021-09-22 17:13:38 +03:00
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookEventFactory;
2020-09-11 14:11:10 +03:00
use WooCommerce\PayPalCommerce\Webhooks\Handler\RequestHandler;
2020-08-27 13:10:16 +03:00
use Psr\Log\LoggerInterface;
2021-09-22 17:13:38 +03:00
use WooCommerce\PayPalCommerce\Webhooks\Status\WebhookSimulation;
2020-08-27 13:10:16 +03:00
/**
* Class IncomingWebhookEndpoint
*/
class IncomingWebhookEndpoint {
2020-09-11 13:38:02 +03:00
const NAMESPACE = 'paypal/v1';
const ROUTE = 'incoming';
2020-08-27 13:10:16 +03:00
/**
* The Webhook endpoint.
*
* @var WebhookEndpoint
*/
private $webhook_endpoint;
/**
2021-09-21 17:52:44 +03:00
* Our registered webhook.
2020-08-27 13:10:16 +03:00
*
2021-09-21 17:52:44 +03:00
* @var Webhook|null
2020-08-27 13:10:16 +03:00
*/
2021-09-21 17:52:44 +03:00
private $webhook;
2020-08-27 13:10:16 +03:00
/**
* The Request handlers.
*
* @var RequestHandler[]
*/
private $handlers;
/**
* The logger.
*
* @var LoggerInterface
*/
private $logger;
/**
* Whether requests need to be verified.
*
* @var bool
*/
private $verify_request;
2021-09-22 17:13:38 +03:00
/**
* The webhook event factory.
*
* @var WebhookEventFactory
*/
private $webhook_event_factory;
/**
* The simulation handler.
*
* @var WebhookSimulation
*/
private $simulation;
2020-08-27 13:10:16 +03:00
/**
* IncomingWebhookEndpoint constructor.
*
2021-09-22 17:13:38 +03:00
* @param WebhookEndpoint $webhook_endpoint The webhook endpoint.
* @param Webhook|null $webhook Our registered webhook.
* @param LoggerInterface $logger The logger.
* @param bool $verify_request Whether requests need to be verified or not.
* @param WebhookEventFactory $webhook_event_factory The webhook event factory.
* @param WebhookSimulation $simulation The simulation handler.
* @param RequestHandler ...$handlers The handlers, which process a request in the end.
2020-08-27 13:10:16 +03:00
*/
public function __construct(
WebhookEndpoint $webhook_endpoint,
2021-09-21 17:52:44 +03:00
?Webhook $webhook,
2020-08-27 13:10:16 +03:00
LoggerInterface $logger,
bool $verify_request,
2021-09-22 17:13:38 +03:00
WebhookEventFactory $webhook_event_factory,
WebhookSimulation $simulation,
2020-08-27 13:10:16 +03:00
RequestHandler ...$handlers
) {
2021-09-22 17:13:38 +03:00
$this->webhook_endpoint = $webhook_endpoint;
$this->webhook = $webhook;
$this->handlers = $handlers;
$this->logger = $logger;
$this->verify_request = $verify_request;
$this->webhook_event_factory = $webhook_event_factory;
$this->simulation = $simulation;
2020-08-27 13:10:16 +03:00
}
/**
* Registers the endpoint.
*
* @return bool
*/
public function register(): bool {
return (bool) register_rest_route(
self::NAMESPACE,
self::ROUTE,
array(
'methods' => array(
'POST',
),
'callback' => array(
$this,
'handle_request',
),
'permission_callback' => array(
$this,
'verify_request',
),
)
);
}
/**
* Verifies the current request.
*
2021-09-22 17:13:38 +03:00
* @param \WP_REST_Request $request The request.
*
2020-08-27 13:10:16 +03:00
* @return bool
*/
2021-09-22 17:13:38 +03:00
public function verify_request( \WP_REST_Request $request ): bool {
2020-08-27 13:10:16 +03:00
if ( ! $this->verify_request ) {
return true;
}
2021-09-21 17:52:44 +03:00
if ( ! $this->webhook ) {
$this->logger->error( 'Failed to retrieve stored webhook data.' );
return false;
}
2020-08-27 13:10:16 +03:00
try {
2021-09-22 17:13:38 +03:00
$event = $this->event_from_request( $request );
if ( $this->simulation->is_simulation_event( $event ) ) {
return true;
}
2021-09-21 17:52:44 +03:00
$result = $this->webhook_endpoint->verify_current_request_for_webhook( $this->webhook );
2020-08-27 13:10:16 +03:00
if ( ! $result ) {
2021-09-21 17:52:44 +03:00
$this->logger->error( 'Webhook verification failed.' );
2020-08-27 13:10:16 +03:00
}
return $result;
} catch ( RuntimeException $exception ) {
2021-09-21 17:53:01 +03:00
$this->logger->error( 'Webhook verification failed: ' . $exception->getMessage() );
2020-08-27 13:10:16 +03:00
return false;
}
}
/**
* Handles the request.
*
* @param \WP_REST_Request $request The request.
*
* @return \WP_REST_Response
*/
public function handle_request( \WP_REST_Request $request ): \WP_REST_Response {
2021-09-22 17:13:38 +03:00
$event = $this->event_from_request( $request );
if ( $this->simulation->is_simulation_event( $event ) ) {
$this->logger->info( 'Received simulated webhook.' );
$this->simulation->receive( $event );
return rest_ensure_response(
array(
'success' => true,
)
);
}
2020-08-27 13:10:16 +03:00
foreach ( $this->handlers as $handler ) {
if ( $handler->responsible_for_request( $request ) ) {
$response = $handler->handle_request( $request );
$this->logger->log(
'info',
sprintf(
// translators: %s is the event type.
__( 'Webhook has been handled by %s', 'woocommerce-paypal-payments' ),
2020-08-27 13:10:16 +03:00
( $handler->event_types() ) ? current( $handler->event_types() ) : ''
),
array(
'request' => $request,
'response' => $response,
)
);
return $response;
}
}
$message = sprintf(
// translators: %s is the request type.
__( 'Could not find handler for request type %s', 'woocommerce-paypal-payments' ),
2020-08-27 13:10:16 +03:00
$request['event_type']
);
$this->logger->log(
'warning',
$message,
array(
'request' => $request,
)
);
$response = array(
'success' => false,
'message' => $message,
);
return rest_ensure_response( $response );
}
/**
* Returns the URL to the endpoint.
*
* @return string
*/
public function url(): string {
return rest_url( self::NAMESPACE . '/' . self::ROUTE );
}
/**
* Returns the event types, which are handled by the endpoint.
*
* @return array
*/
public function handled_event_types(): array {
$event_types = array();
foreach ( $this->handlers as $handler ) {
$event_types = array_merge( $event_types, $handler->event_types() );
}
return array_unique( $event_types );
}
2021-09-22 17:13:38 +03:00
/**
* Creates WebhookEvent from request data.
*
* @param \WP_REST_Request $request The request with event data.
*
* @return WebhookEvent
* @throws RuntimeException When failed to create.
*/
private function event_from_request( \WP_REST_Request $request ): WebhookEvent {
return $this->webhook_event_factory->from_array( $request->get_params() );
}
2020-08-27 13:10:16 +03:00
}