Add webhook simulation

This commit is contained in:
Alex P 2021-09-22 17:13:38 +03:00
parent 0268524261
commit ea2f728cd8
14 changed files with 896 additions and 20 deletions

View file

@ -35,6 +35,7 @@ use WooCommerce\PayPalCommerce\ApiClient\Factory\PaymentTokenFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\PurchaseUnitFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\SellerStatusFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\ShippingFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookEventFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory;
use WooCommerce\PayPalCommerce\ApiClient\Helper\Cache;
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
@ -114,6 +115,7 @@ return array(
$container->get( 'api.host' ),
$container->get( 'api.bearer' ),
$container->get( 'api.factory.webhook' ),
$container->get( 'api.factory.webhook-event' ),
$container->get( 'woocommerce.logger.woocommerce' )
);
},
@ -214,6 +216,9 @@ return array(
'api.factory.webhook' => static function ( $container ): WebhookFactory {
return new WebhookFactory();
},
'api.factory.webhook-event' => static function ( $container ): WebhookEventFactory {
return new WebhookEventFactory();
},
'api.factory.capture' => static function ( $container ): CaptureFactory {
$amount_factory = $container->get( 'api.factory.amount' );

View file

@ -11,8 +11,10 @@ namespace WooCommerce\PayPalCommerce\ApiClient\Endpoint;
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
use WooCommerce\PayPalCommerce\ApiClient\Entity\Webhook;
use WooCommerce\PayPalCommerce\ApiClient\Entity\WebhookEvent;
use WooCommerce\PayPalCommerce\ApiClient\Exception\PayPalApiException;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookEventFactory;
use WooCommerce\PayPalCommerce\ApiClient\Factory\WebhookFactory;
use Psr\Log\LoggerInterface;
@ -44,6 +46,13 @@ class WebhookEndpoint {
*/
private $webhook_factory;
/**
* The webhook event factory.
*
* @var WebhookEventFactory
*/
private $webhook_event_factory;
/**
* The logger.
*
@ -54,22 +63,25 @@ class WebhookEndpoint {
/**
* WebhookEndpoint constructor.
*
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param WebhookFactory $webhook_factory The webhook factory.
* @param LoggerInterface $logger The logger.
* @param string $host The host.
* @param Bearer $bearer The bearer.
* @param WebhookFactory $webhook_factory The webhook factory.
* @param WebhookEventFactory $webhook_event_factory The webhook event factory.
* @param LoggerInterface $logger The logger.
*/
public function __construct(
string $host,
Bearer $bearer,
WebhookFactory $webhook_factory,
WebhookEventFactory $webhook_event_factory,
LoggerInterface $logger
) {
$this->host = $host;
$this->bearer = $bearer;
$this->webhook_factory = $webhook_factory;
$this->logger = $logger;
$this->host = $host;
$this->bearer = $bearer;
$this->webhook_factory = $webhook_factory;
$this->webhook_event_factory = $webhook_event_factory;
$this->logger = $logger;
}
/**
@ -189,6 +201,51 @@ class WebhookEndpoint {
return wp_remote_retrieve_response_code( $response ) === 204;
}
/**
* Request a simulated webhook to be sent.
*
* @param Webhook $hook The webhook subscription to use.
* @param string $event_type The event type, such as CHECKOUT.ORDER.APPROVED.
*
* @return WebhookEvent
* @throws RuntimeException If the request fails.
* @throws PayPalApiException If the request fails.
*/
public function simulate( Webhook $hook, string $event_type ): WebhookEvent {
$bearer = $this->bearer->bearer();
$url = trailingslashit( $this->host ) . 'v1/notifications/simulate-event';
$args = array(
'method' => 'POST',
'headers' => array(
'Authorization' => 'Bearer ' . $bearer->token(),
'Content-Type' => 'application/json',
),
'body' => wp_json_encode(
array(
'webhook_id' => $hook->id(),
'event_type' => $event_type,
)
),
);
$response = $this->request( $url, $args );
if ( is_wp_error( $response ) ) {
throw new RuntimeException(
__( 'Not able to simulate webhook.', 'woocommerce-paypal-payments' )
);
}
$json = json_decode( $response['body'] );
$status_code = (int) wp_remote_retrieve_response_code( $response );
if ( 202 !== $status_code ) {
throw new PayPalApiException(
$json,
$status_code
);
}
return $this->webhook_event_factory->from_paypal_response( $json );
}
/**
* Verifies if a webhook event is legitimate.
*

View file

@ -0,0 +1,170 @@
<?php
/**
* The Webhook event notification object.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Entity
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Entity;
use DateTime;
use stdClass;
/**
* Class WebhookEvent
*/
class WebhookEvent {
/**
* The ID of the event notification.
*
* @var string
*/
private $id;
/**
* The date and time when the event notification was created.
*
* @var DateTime|null
*/
private $create_time;
/**
* The name of the resource related to the webhook notification event, such as 'checkout-order'.
*
* @var string
*/
private $resource_type;
/**
* The event version in the webhook notification, such as '1.0'.
*
* @var string
*/
private $event_version;
/**
* The event that triggered the webhook event notification, such as 'CHECKOUT.ORDER.APPROVED'.
*
* @var string
*/
private $event_type;
/**
* A summary description for the event notification.
*
* @var string
*/
private $summary;
/**
* The resource version in the webhook notification, such as '1.0'.
*
* @var string
*/
private $resource_version;
/**
* The resource that triggered the webhook event notification.
*
* @var stdClass
*/
private $resource;
/**
* WebhookEvent constructor.
*
* @param string $id The ID of the event notification.
* @param DateTime|null $create_time The date and time when the event notification was created.
* @param string $resource_type The name of the resource related to the webhook notification event, such as 'checkout-order'.
* @param string $event_version The event version in the webhook notification, such as '1.0'.
* @param string $event_type The event that triggered the webhook event notification, such as 'CHECKOUT.ORDER.APPROVED'.
* @param string $summary A summary description for the event notification.
* @param string $resource_version The resource version in the webhook notification, such as '1.0'.
* @param stdClass $resource The resource that triggered the webhook event notification.
*/
public function __construct( string $id, ?DateTime $create_time, string $resource_type, string $event_version, string $event_type, string $summary, string $resource_version, stdClass $resource ) {
$this->id = $id;
$this->create_time = $create_time;
$this->resource_type = $resource_type;
$this->event_version = $event_version;
$this->event_type = $event_type;
$this->summary = $summary;
$this->resource_version = $resource_version;
$this->resource = $resource;
}
/**
* The ID of the event notification.
*
* @return string
*/
public function id(): string {
return $this->id;
}
/**
* The date and time when the event notification was created.
*
* @return DateTime|null
*/
public function create_time(): ?DateTime {
return $this->create_time;
}
/**
* The name of the resource related to the webhook notification event, such as 'checkout-order'.
*
* @return string
*/
public function resource_type(): string {
return $this->resource_type;
}
/**
* The event version in the webhook notification, such as '1.0'.
*
* @return string
*/
public function event_version(): string {
return $this->event_version;
}
/**
* The event that triggered the webhook event notification, such as 'CHECKOUT.ORDER.APPROVED'.
*
* @return string
*/
public function event_type(): string {
return $this->event_type;
}
/**
* A summary description for the event notification.
*
* @return string
*/
public function summary(): string {
return $this->summary;
}
/**
* The resource version in the webhook notification, such as '1.0'.
*
* @return string
*/
public function resource_version(): string {
return $this->resource_version;
}
/**
* The resource that triggered the webhook event notification.
*
* @return stdClass
*/
public function resource(): stdClass {
return $this->resource;
}
}

View file

@ -0,0 +1,74 @@
<?php
/**
* Creates WebhookEvent.
*
* @package WooCommerce\PayPalCommerce\ApiClient\Factory
*/
declare(strict_types=1);
namespace WooCommerce\PayPalCommerce\ApiClient\Factory;
use DateTime;
use stdClass;
use WooCommerce\PayPalCommerce\ApiClient\Entity\WebhookEvent;
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
/**
* Class WebhookEventFactory
*/
class WebhookEventFactory {
/**
* Returns a webhook from a given data array.
*
* @param array $data The data array.
*
* @return WebhookEvent
*/
public function from_array( array $data ): WebhookEvent {
return $this->from_paypal_response( (object) $data );
}
/**
* Returns a Webhook based of a PayPal JSON response.
*
* @param stdClass $data The JSON object.
*
* @return WebhookEvent
* @throws RuntimeException When JSON object is malformed.
*/
public function from_paypal_response( stdClass $data ): WebhookEvent {
if ( ! isset( $data->id ) ) {
throw new RuntimeException(
__( 'ID for webhook event not found.', 'woocommerce-paypal-payments' )
);
}
if ( ! isset( $data->event_type ) ) {
throw new RuntimeException(
__( 'Event type for webhook event not found.', 'woocommerce-paypal-payments' )
);
}
$create_time = ( isset( $data->create_time ) ) ?
DateTime::createFromFormat( 'Y-m-d\TH:i:sO', $data->create_time )
: null;
// Sometimes the time may be in weird format 2018-12-19T22:20:32.000Z (at least in simulation),
// we do not care much about time, so just ignore on failure.
if ( false === $create_time ) {
$create_time = null;
}
return new WebhookEvent(
(string) $data->id,
$create_time,
(string) $data->resource_type ?? '',
(string) $data->event_version ?? '',
(string) $data->event_type,
(string) $data->summary ?? '',
(string) $data->resource_version ?? '',
(object) $data->resource ?? ( new stdClass() )
);
}
}