mirror of
https://github.com/woocommerce/woocommerce-paypal-payments.git
synced 2025-09-04 08:47:23 +08:00
Merge pull request #259 from woocommerce/webhooks-status-page
Webhooks status page
This commit is contained in:
commit
efb5ab3d14
23 changed files with 5522 additions and 29 deletions
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -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' ),
|
||||
|
|
|
@ -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.
|
||||
*
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
/**
|
||||
* PageMatcherTrait.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\WcGateway\Settings
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Settings;
|
||||
|
||||
use WooCommerce\PayPalCommerce\AdminNotices\Entity\Message;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Helper\DccApplies;
|
||||
use WooCommerce\PayPalCommerce\Button\Helper\MessagesApply;
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\CreditCardGateway;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Gateway\PayPalGateway;
|
||||
use Woocommerce\PayPalCommerce\WcGateway\Helper\DccProductStatus;
|
||||
use Woocommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Status\WebhooksStatusPage;
|
||||
|
||||
/**
|
||||
* Class PageMatcherTrait.
|
||||
*/
|
||||
trait PageMatcherTrait {
|
||||
|
||||
/**
|
||||
* Checks whether the field config matches the current page (can be rendered here).
|
||||
*
|
||||
* @param array $field_config The field config (from wcgateway.settings.fields).
|
||||
* @param string $current_page_id ID of the current PPCP gateway settings page.
|
||||
* @return bool
|
||||
*/
|
||||
protected function field_matches_page( array $field_config, string $current_page_id ): bool {
|
||||
$allowed_gateways = $field_config['gateway'];
|
||||
if ( ! is_array( $allowed_gateways ) ) {
|
||||
$allowed_gateways = array( $allowed_gateways );
|
||||
}
|
||||
|
||||
$gateway_page_id_map = array(
|
||||
PayPalGateway::ID => '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 );
|
||||
}
|
||||
}
|
|
@ -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 '<ul class="subsubsub">';
|
||||
|
|
|
@ -23,6 +23,8 @@ use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar;
|
|||
*/
|
||||
class SettingsListener {
|
||||
|
||||
use PageMatcherTrait;
|
||||
|
||||
const NONCE = 'ppcp-settings';
|
||||
|
||||
private const CREDENTIALS_ADDED = 'credentials_added';
|
||||
|
@ -360,16 +362,7 @@ class SettingsListener {
|
|||
if ( ! in_array( $this->state->current_state(), $config['screens'], true ) ) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
'dcc' === $config['gateway']
|
||||
&& CreditCardGateway::ID !== $this->page_id
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
'paypal' === $config['gateway']
|
||||
&& PayPalGateway::ID !== $this->page_id
|
||||
) {
|
||||
if ( ! $this->field_matches_page( $config, $this->page_id ) ) {
|
||||
continue;
|
||||
}
|
||||
switch ( $config['type'] ) {
|
||||
|
|
|
@ -24,6 +24,8 @@ use Woocommerce\PayPalCommerce\WcGateway\Helper\SettingsStatus;
|
|||
*/
|
||||
class SettingsRenderer {
|
||||
|
||||
use PageMatcherTrait;
|
||||
|
||||
/**
|
||||
* The Settings status helper.
|
||||
*
|
||||
|
@ -309,6 +311,60 @@ class SettingsRenderer {
|
|||
return $html;
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the table row.
|
||||
*
|
||||
* @param array $data Values of the row cells.
|
||||
* @param string $tag HTML tag ('td', 'th').
|
||||
* @return string
|
||||
*/
|
||||
public function render_table_row( array $data, string $tag = 'td' ): string {
|
||||
$cells = array_map(
|
||||
function ( $value ) use ( $tag ): string {
|
||||
return "<$tag>" . (string) $value . "</$tag>";
|
||||
},
|
||||
$data
|
||||
);
|
||||
return '<tr>' . implode( $cells ) . '</tr>';
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the table field.
|
||||
*
|
||||
* @param string $field The current field HTML.
|
||||
* @param string $key The key.
|
||||
* @param array $config The configuration of the field.
|
||||
* @param array $value The current value.
|
||||
*
|
||||
* @return string HTML.
|
||||
*/
|
||||
public function render_table( $field, $key, $config, $value ): string {
|
||||
if ( 'ppcp-table' !== $config['type'] ) {
|
||||
return $field;
|
||||
}
|
||||
|
||||
$data = $value['data'];
|
||||
if ( empty( $data ) ) {
|
||||
$empty_placeholder = $value['empty_placeholder'] ?? ( $config['empty_placeholder'] ?? null );
|
||||
if ( $empty_placeholder ) {
|
||||
return $empty_placeholder;
|
||||
}
|
||||
}
|
||||
|
||||
$header_row_html = $this->render_table_row( $value['headers'], 'th' );
|
||||
$data_rows_html = implode(
|
||||
array_map(
|
||||
array( $this, 'render_table_row' ),
|
||||
$data
|
||||
)
|
||||
);
|
||||
|
||||
return "<table>
|
||||
$header_row_html
|
||||
$data_rows_html
|
||||
</table>";
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the settings.
|
||||
*/
|
||||
|
@ -325,10 +381,7 @@ class SettingsRenderer {
|
|||
if ( ! in_array( $this->state->current_state(), $config['screens'], true ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( $is_dcc && ! in_array( $config['gateway'], array( 'all', 'dcc' ), true ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( ! $is_dcc && ! in_array( $config['gateway'], array( 'all', 'paypal' ), true ) ) {
|
||||
if ( ! $this->field_matches_page( $config, $this->page_id ) ) {
|
||||
continue;
|
||||
}
|
||||
if (
|
||||
|
@ -349,7 +402,7 @@ class SettingsRenderer {
|
|||
) {
|
||||
continue;
|
||||
}
|
||||
$value = $this->settings->has( $field ) ? $this->settings->get( $field ) : null;
|
||||
$value = $this->settings->has( $field ) ? $this->settings->get( $field ) : ( isset( $config['value'] ) ? $config['value']() : null );
|
||||
$key = 'ppcp[' . $field . ']';
|
||||
$id = 'ppcp-' . $field;
|
||||
$config['id'] = $id;
|
||||
|
|
|
@ -214,6 +214,7 @@ class WcGatewayModule implements ModuleInterface {
|
|||
$field = $renderer->render_password( $field, $key, $args, $value );
|
||||
$field = $renderer->render_text_input( $field, $key, $args, $value );
|
||||
$field = $renderer->render_heading( $field, $key, $args, $value );
|
||||
$field = $renderer->render_table( $field, $key, $args, $value );
|
||||
return $field;
|
||||
},
|
||||
10,
|
||||
|
|
2
modules/ppcp-webhooks/.gitignore
vendored
Normal file
2
modules/ppcp-webhooks/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
node_modules
|
||||
assets
|
|
@ -9,4 +9,40 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks;
|
||||
|
||||
return array();
|
||||
use WooCommerce\PayPalCommerce\Onboarding\State;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Status\WebhooksStatusPage;
|
||||
|
||||
return array(
|
||||
'wcgateway.settings.fields' => static function ( $container, array $fields ): array {
|
||||
$status_page_fields = array(
|
||||
'webhooks_list' => array(
|
||||
'title' => __( 'Subscribed webhooks', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-table',
|
||||
'screens' => array(
|
||||
State::STATE_PROGRESSIVE,
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => WebhooksStatusPage::ID,
|
||||
'classes' => array( 'ppcp-webhooks-table' ),
|
||||
'value' => function () use ( $container ) : array {
|
||||
return $container->get( 'webhook.status.registered-webhooks-data' );
|
||||
},
|
||||
),
|
||||
'webhooks_resubscribe' => array(
|
||||
'title' => __( 'Resubscribe webhooks', 'woocommerce-paypal-payments' ),
|
||||
'type' => 'ppcp-text',
|
||||
'text' => '<button type="button" class="button ppcp-webhooks-resubscribe">' . esc_html__( 'Resubscribe', 'woocommerce-paypal-payments' ) . '</button>',
|
||||
'screens' => array(
|
||||
State::STATE_PROGRESSIVE,
|
||||
State::STATE_ONBOARDED,
|
||||
),
|
||||
'requirements' => array(),
|
||||
'gateway' => WebhooksStatusPage::ID,
|
||||
'description' => __( 'Click to remove the current webhook subscription and subscribe again, for example, if the website domain or URL structure changed.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
);
|
||||
|
||||
return array_merge( $fields, $status_page_fields );
|
||||
},
|
||||
);
|
||||
|
|
23
modules/ppcp-webhooks/package.json
Normal file
23
modules/ppcp-webhooks/package.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"name": "ppcp-webhooks",
|
||||
"version": "1.0.0",
|
||||
"license": "GPL-3.0-or-later",
|
||||
"main": "resources/js/status-page.js",
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.9.0",
|
||||
"@babel/preset-env": "^7.9.5",
|
||||
"babel-loader": "^8.1.0",
|
||||
"cross-env": "^5.0.1",
|
||||
"file-loader": "^4.2.0",
|
||||
"node-sass": "^4.13.0",
|
||||
"sass-loader": "^8.0.0",
|
||||
"webpack": "^4.42.1",
|
||||
"webpack-cli": "^3.1.2",
|
||||
"babel-plugin-transform-object-rest-spread": "^6.26.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env BABEL_ENV=default NODE_ENV=production webpack",
|
||||
"watch": "cross-env BABEL_ENV=default NODE_ENV=production webpack --watch",
|
||||
"dev": "cross-env BABEL_ENV=default webpack --watch"
|
||||
}
|
||||
}
|
23
modules/ppcp-webhooks/resources/css/status-page.scss
Normal file
23
modules/ppcp-webhooks/resources/css/status-page.scss
Normal file
|
@ -0,0 +1,23 @@
|
|||
.ppcp-webhooks-table {
|
||||
.error {
|
||||
color: red;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
|
||||
th {
|
||||
border-bottom: 1px solid black;
|
||||
}
|
||||
|
||||
td, th {
|
||||
padding: 10px 15px;
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
vertical-align: top;
|
||||
width: 450px;
|
||||
}
|
||||
}
|
||||
}
|
42
modules/ppcp-webhooks/resources/js/status-page.js
Normal file
42
modules/ppcp-webhooks/resources/js/status-page.js
Normal file
|
@ -0,0 +1,42 @@
|
|||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
() => {
|
||||
const resubscribeBtn = jQuery(PayPalCommerceGatewayWebhooksStatus.resubscribe.button);
|
||||
|
||||
resubscribeBtn.click(async () => {
|
||||
resubscribeBtn.prop('disabled', true);
|
||||
|
||||
const response = await fetch(
|
||||
PayPalCommerceGatewayWebhooksStatus.resubscribe.endpoint,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(
|
||||
{
|
||||
nonce: PayPalCommerceGatewayWebhooksStatus.resubscribe.nonce,
|
||||
}
|
||||
)
|
||||
}
|
||||
);
|
||||
|
||||
const reportError = error => {
|
||||
const msg = PayPalCommerceGatewayWebhooksStatus.resubscribe.failureMessage + ' ' + error;
|
||||
alert(msg);
|
||||
}
|
||||
|
||||
if (!response.ok) {
|
||||
try {
|
||||
const result = await response.json();
|
||||
reportError(result.data);
|
||||
} catch (exc) {
|
||||
console.error(exc);
|
||||
reportError(response.status);
|
||||
}
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
);
|
|
@ -9,6 +9,11 @@ declare(strict_types=1);
|
|||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks;
|
||||
|
||||
use Exception;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Endpoint\WebhookEndpoint;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Entity\Webhook;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Assets\WebhooksStatusPageAssets;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\CheckoutOrderApproved;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\CheckoutOrderCompleted;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Handler\PaymentCaptureCompleted;
|
||||
|
@ -18,7 +23,7 @@ use Psr\Container\ContainerInterface;
|
|||
|
||||
return array(
|
||||
|
||||
'webhook.registrar' => function( $container ) : WebhookRegistrar {
|
||||
'webhook.registrar' => function( $container ) : WebhookRegistrar {
|
||||
$factory = $container->get( 'api.factory.webhook' );
|
||||
$endpoint = $container->get( 'api.endpoint.webhook' );
|
||||
$rest_endpoint = $container->get( 'webhook.endpoint.controller' );
|
||||
|
@ -30,7 +35,7 @@ return array(
|
|||
$logger
|
||||
);
|
||||
},
|
||||
'webhook.endpoint.controller' => function( $container ) : IncomingWebhookEndpoint {
|
||||
'webhook.endpoint.controller' => function( $container ) : IncomingWebhookEndpoint {
|
||||
$webhook_endpoint = $container->get( 'api.endpoint.webhook' );
|
||||
$webhook_factory = $container->get( 'api.factory.webhook' );
|
||||
$handler = $container->get( 'webhook.endpoint.handler' );
|
||||
|
@ -45,7 +50,7 @@ return array(
|
|||
... $handler
|
||||
);
|
||||
},
|
||||
'webhook.endpoint.handler' => function( $container ) : array {
|
||||
'webhook.endpoint.handler' => function( $container ) : array {
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
$prefix = $container->get( 'api.prefix' );
|
||||
$order_endpoint = $container->get( 'api.endpoint.order' );
|
||||
|
@ -57,4 +62,71 @@ return array(
|
|||
new PaymentCaptureCompleted( $logger, $prefix ),
|
||||
);
|
||||
},
|
||||
|
||||
'webhook.status.registered-webhooks' => function( $container ) : array {
|
||||
$endpoint = $container->get( 'api.endpoint.webhook' );
|
||||
assert( $endpoint instanceof WebhookEndpoint );
|
||||
|
||||
return $endpoint->list();
|
||||
},
|
||||
|
||||
'webhook.status.registered-webhooks-data' => function( $container ) : array {
|
||||
$empty_placeholder = __( 'No webhooks found.', 'woocommerce-paypal-payments' );
|
||||
|
||||
$webhooks = array();
|
||||
try {
|
||||
$webhooks = $container->get( 'webhook.status.registered-webhooks' );
|
||||
} catch ( Exception $exception ) {
|
||||
$empty_placeholder = sprintf(
|
||||
'<span class="error">%s</span>',
|
||||
__( 'Failed to load webhooks.', 'woocommerce-paypal-payments' )
|
||||
);
|
||||
}
|
||||
|
||||
return array(
|
||||
'headers' => array(
|
||||
__( 'URL', 'woocommerce-paypal-payments' ),
|
||||
__( 'Tracked events', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
'data' => array_map(
|
||||
function ( Webhook $webhook ): array {
|
||||
return array(
|
||||
esc_html( $webhook->url() ),
|
||||
implode(
|
||||
',<br/>',
|
||||
array_map(
|
||||
'esc_html',
|
||||
$webhook->humanfriendly_event_names()
|
||||
)
|
||||
),
|
||||
);
|
||||
},
|
||||
$webhooks
|
||||
),
|
||||
'empty_placeholder' => $empty_placeholder,
|
||||
);
|
||||
},
|
||||
|
||||
'webhook.status.assets' => function( $container ) : WebhooksStatusPageAssets {
|
||||
return new WebhooksStatusPageAssets(
|
||||
$container->get( 'webhook.module-url' )
|
||||
);
|
||||
},
|
||||
|
||||
'webhook.endpoint.resubscribe' => static function ( $container ) : ResubscribeEndpoint {
|
||||
$registrar = $container->get( 'webhook.registrar' );
|
||||
$request_data = $container->get( 'button.request-data' );
|
||||
|
||||
return new ResubscribeEndpoint(
|
||||
$registrar,
|
||||
$request_data
|
||||
);
|
||||
},
|
||||
|
||||
'webhook.module-url' => static function ( $container ): string {
|
||||
return plugins_url(
|
||||
'/modules/ppcp-webhooks/',
|
||||
dirname( __FILE__, 3 ) . '/woocommerce-paypal-payments.php'
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
<?php
|
||||
/**
|
||||
* The endpoint for resubscribing webhooks.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Endpoint
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Endpoint;
|
||||
|
||||
use Exception;
|
||||
use WooCommerce\PayPalCommerce\Button\Endpoint\RequestData;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar;
|
||||
|
||||
/**
|
||||
* Class ResubscribeEndpoint
|
||||
*/
|
||||
class ResubscribeEndpoint {
|
||||
|
||||
const ENDPOINT = 'ppc-webhooks-resubscribe';
|
||||
|
||||
/**
|
||||
* The webhooks registrar.
|
||||
*
|
||||
* @var WebhookRegistrar
|
||||
*/
|
||||
private $registrar;
|
||||
|
||||
/**
|
||||
* The Request Data helper object.
|
||||
*
|
||||
* @var RequestData
|
||||
*/
|
||||
private $request_data;
|
||||
|
||||
/**
|
||||
* ResubscribeEndpoint constructor.
|
||||
*
|
||||
* @param WebhookRegistrar $registrar The webhooks registrar.
|
||||
* @param RequestData $request_data The Request Data helper object.
|
||||
*/
|
||||
public function __construct( WebhookRegistrar $registrar, RequestData $request_data ) {
|
||||
$this->registrar = $registrar;
|
||||
$this->request_data = $request_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the nonce for the endpoint.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function nonce(): string {
|
||||
return self::ENDPOINT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the incoming request.
|
||||
*/
|
||||
public function handle_request() {
|
||||
try {
|
||||
// Validate nonce.
|
||||
$this->request_data->read_request( $this->nonce() );
|
||||
|
||||
$this->registrar->unregister();
|
||||
|
||||
if ( ! $this->registrar->register() ) {
|
||||
wp_send_json_error( 'Webhook subscription failed.', 500 );
|
||||
return false;
|
||||
}
|
||||
|
||||
wp_send_json_success();
|
||||
return true;
|
||||
} catch ( Exception $error ) {
|
||||
wp_send_json_error( $error->getMessage(), 403 );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
<?php
|
||||
/**
|
||||
* Register and configure assets for webhooks status page.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Status\Assets
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\WcGateway\Assets;
|
||||
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Authentication\Bearer;
|
||||
use WooCommerce\PayPalCommerce\ApiClient\Exception\RuntimeException;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint;
|
||||
|
||||
/**
|
||||
* Class WebhooksStatusPageAssets
|
||||
*/
|
||||
class WebhooksStatusPageAssets {
|
||||
|
||||
/**
|
||||
* The URL to the module.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
private $module_url;
|
||||
|
||||
/**
|
||||
* WebhooksStatusPageAssets constructor.
|
||||
*
|
||||
* @param string $module_url The URL to the module.
|
||||
*/
|
||||
public function __construct(
|
||||
string $module_url
|
||||
) {
|
||||
$this->module_url = untrailingslashit( $module_url );
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the scripts and styles.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register(): void {
|
||||
wp_register_style(
|
||||
'ppcp-webhooks-status-page-style',
|
||||
$this->module_url . '/assets/css/status-page.css',
|
||||
array(),
|
||||
1
|
||||
);
|
||||
|
||||
wp_register_script(
|
||||
'ppcp-webhooks-status-page',
|
||||
$this->module_url . '/assets/js/status-page.js',
|
||||
array(),
|
||||
1,
|
||||
true
|
||||
);
|
||||
|
||||
wp_localize_script(
|
||||
'ppcp-webhooks-status-page',
|
||||
'PayPalCommerceGatewayWebhooksStatus',
|
||||
$this->get_script_data()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the data for the script.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function get_script_data() {
|
||||
return array(
|
||||
'resubscribe' => array(
|
||||
'endpoint' => home_url( \WC_AJAX::get_endpoint( ResubscribeEndpoint::ENDPOINT ) ),
|
||||
'nonce' => wp_create_nonce( ResubscribeEndpoint::nonce() ),
|
||||
'button' => '.ppcp-webhooks-resubscribe',
|
||||
'failureMessage' => __( 'Operation failed. Check WooCommerce logs for more details.', 'woocommerce-paypal-payments' ),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueues the necessary scripts.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enqueue(): void {
|
||||
wp_enqueue_style( 'ppcp-webhooks-status-page-style' );
|
||||
wp_enqueue_script( 'ppcp-webhooks-status-page' );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
/**
|
||||
* Status page.
|
||||
*
|
||||
* @package WooCommerce\PayPalCommerce\Webhooks\Status
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace WooCommerce\PayPalCommerce\Webhooks\Status;
|
||||
|
||||
/**
|
||||
* Class WebhooksStatusPage
|
||||
*/
|
||||
class WebhooksStatusPage {
|
||||
|
||||
const ID = 'ppcp-webhooks-status-page';
|
||||
}
|
|
@ -11,8 +11,13 @@ namespace WooCommerce\PayPalCommerce\Webhooks;
|
|||
|
||||
use Dhii\Container\ServiceProvider;
|
||||
use Dhii\Modular\Module\ModuleInterface;
|
||||
use Exception;
|
||||
use Interop\Container\ServiceProviderInterface;
|
||||
use Psr\Container\ContainerInterface;
|
||||
use Psr\Log\LoggerInterface;
|
||||
use WooCommerce\PayPalCommerce\WcGateway\Assets\WebhooksStatusPageAssets;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Endpoint\ResubscribeEndpoint;
|
||||
use WooCommerce\PayPalCommerce\Webhooks\Status\WebhooksStatusPage;
|
||||
|
||||
/**
|
||||
* Class WebhookModule
|
||||
|
@ -37,6 +42,9 @@ class WebhookModule implements ModuleInterface {
|
|||
* @param ContainerInterface|null $container The Container.
|
||||
*/
|
||||
public function run( ContainerInterface $container ): void {
|
||||
$logger = $container->get( 'woocommerce.logger.woocommerce' );
|
||||
assert( $logger instanceof LoggerInterface );
|
||||
|
||||
add_action(
|
||||
'rest_api_init',
|
||||
static function () use ( $container ) {
|
||||
|
@ -75,6 +83,50 @@ class WebhookModule implements ModuleInterface {
|
|||
$registrar->unregister();
|
||||
}
|
||||
);
|
||||
|
||||
add_action(
|
||||
'wc_ajax_' . ResubscribeEndpoint::ENDPOINT,
|
||||
static function () use ( $container ) {
|
||||
$endpoint = $container->get( 'webhook.endpoint.resubscribe' );
|
||||
assert( $endpoint instanceof ResubscribeEndpoint );
|
||||
|
||||
$endpoint->handle_request();
|
||||
}
|
||||
);
|
||||
|
||||
$page_id = $container->get( 'wcgateway.current-ppcp-settings-page-id' );
|
||||
if ( WebhooksStatusPage::ID === $page_id ) {
|
||||
$GLOBALS['hide_save_button'] = true;
|
||||
$asset_loader = $container->get( 'webhook.status.assets' );
|
||||
assert( $asset_loader instanceof WebhooksStatusPageAssets );
|
||||
add_action(
|
||||
'init',
|
||||
array( $asset_loader, 'register' )
|
||||
);
|
||||
add_action(
|
||||
'admin_enqueue_scripts',
|
||||
array( $asset_loader, 'enqueue' )
|
||||
);
|
||||
|
||||
try {
|
||||
$webhooks = $container->get( 'webhook.status.registered-webhooks' );
|
||||
|
||||
if ( empty( $webhooks ) ) {
|
||||
$registrar = $container->get( 'webhook.registrar' );
|
||||
assert( $registrar instanceof WebhookRegistrar );
|
||||
|
||||
// Looks like we cannot call rest_url too early.
|
||||
add_action(
|
||||
'init',
|
||||
function () use ( $registrar ) {
|
||||
$registrar->register();
|
||||
}
|
||||
);
|
||||
}
|
||||
} catch ( Exception $exception ) {
|
||||
$logger->error( 'Failed to load webhooks list: ' . $exception->getMessage() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -92,10 +92,10 @@ class WebhookRegistrar {
|
|||
self::KEY,
|
||||
$created->to_array()
|
||||
);
|
||||
$this->logger->info( 'Webhooks registered.' );
|
||||
$this->logger->info( 'Webhooks subscribed.' );
|
||||
return true;
|
||||
} catch ( RuntimeException $error ) {
|
||||
$this->logger->error( 'Failed to register webhooks: ' . $error->getMessage() );
|
||||
$this->logger->error( 'Failed to subscribe webhooks: ' . $error->getMessage() );
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
36
modules/ppcp-webhooks/webpack.config.js
Normal file
36
modules/ppcp-webhooks/webpack.config.js
Normal file
|
@ -0,0 +1,36 @@
|
|||
const path = require('path');
|
||||
const isProduction = process.env.NODE_ENV === 'production';
|
||||
|
||||
module.exports = {
|
||||
devtool: 'sourcemap',
|
||||
mode: isProduction ? 'production' : 'development',
|
||||
target: 'web',
|
||||
entry: {
|
||||
'status-page': path.resolve('./resources/js/status-page.js'),
|
||||
'status-page-style': path.resolve('./resources/css/status-page.scss'),
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(__dirname, 'assets/'),
|
||||
filename: 'js/[name].js',
|
||||
},
|
||||
module: {
|
||||
rules: [{
|
||||
test: /\.js?$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'babel-loader',
|
||||
},
|
||||
{
|
||||
test: /\.scss$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'file-loader',
|
||||
options: {
|
||||
name: 'css/[name].css',
|
||||
}
|
||||
},
|
||||
{loader:'sass-loader'}
|
||||
]
|
||||
}]
|
||||
}
|
||||
};
|
4830
modules/ppcp-webhooks/yarn.lock
Normal file
4830
modules/ppcp-webhooks/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue