Get and resubscribe webhooks

This commit is contained in:
inpsyde-maticluznar 2024-12-18 07:00:47 +01:00
parent 484ecc17d7
commit 08f5b4fba5
No known key found for this signature in database
GPG key ID: D005973F231309F6
13 changed files with 113 additions and 167 deletions

View file

@ -15,6 +15,7 @@ const ButtonSettingsBlock = ( { title, description, ...props } ) => (
</Header>
<Action>
<Button
isBusy={ props.actionProps?.isBusy }
variant={ props.actionProps?.buttonType }
onClick={
props.actionProps?.callback

View file

@ -8,8 +8,21 @@ import {
ButtonSettingsBlock,
} from '../../../../ReusableComponents/SettingsBlocks';
import SettingsBlock from '../../../../ReusableComponents/SettingsBlocks/SettingsBlock';
import { CommonHooks } from '../../../../../data';
import { useState } from '@wordpress/element';
const Troubleshooting = ( { updateFormValue, settings } ) => {
const { webhooks, registerWebhooks, simulateWebhooks } =
CommonHooks.useWebhooks();
const [ resubscribing, setResubscribing ] = useState( false );
const resubscribeWebhooks = async () => {
setResubscribing( true );
await registerWebhooks();
setResubscribing( false );
};
return (
<AccordionSettingsBlock
className="ppcp-r-settings-block--troubleshooting"
@ -61,7 +74,7 @@ const Troubleshooting = ( { updateFormValue, settings } ) => {
.
</Description>
</Header>
<HooksTable data={ hooksExampleData() } />
<HooksTable data={ webhooks } />
</>
),
] }
@ -78,11 +91,8 @@ const Troubleshooting = ( { updateFormValue, settings } ) => {
) }
actionProps={ {
buttonType: 'secondary',
callback: () =>
console.log(
'Resubscribe webhooks',
'woocommerce-paypal-payments'
),
isBusy: resubscribing,
callback: () => resubscribeWebhooks(),
value: __(
'Resubscribe webhooks',
'woocommerce-paypal-payments'
@ -97,11 +107,7 @@ const Troubleshooting = ( { updateFormValue, settings } ) => {
) }
actionProps={ {
buttonType: 'secondary',
callback: () =>
console.log(
'Simulate webhooks',
'woocommerce-paypal-payments'
),
callback: () => simulateWebhooks(),
value: __(
'Simulate webhooks',
'woocommerce-paypal-payments'
@ -112,32 +118,6 @@ const Troubleshooting = ( { updateFormValue, settings } ) => {
);
};
const hooksExampleData = () => {
return {
url: 'https://www.rt3.tech/wordpress/paypal-ux-testin/index.php?rest_route=/paypal/v1/incoming',
hooks: [
'billing plan pricing-change activated',
'billing plan updated',
'billing subscription cancelled',
'catalog product updated',
'checkout order approved',
'checkout order completed',
'checkout payment-approval reversed',
'payment authorization voided',
'payment capture completed',
'payment capture denied',
'payment capture pending',
'payment capture refunded',
'payment capture reversed',
'payment order cancelled',
'payment sale completed',
'payment sale refunded',
'vault payment-token created',
'vault payment-token deleted',
],
};
};
const HooksTable = ( { data } ) => {
return (
<table className="ppcp-r-table">
@ -156,15 +136,11 @@ const HooksTable = ( { data } ) => {
</thead>
<tbody>
<tr>
<td className="ppcp-r-table__hooks-url">{ data?.url }</td>
<td className="ppcp-r-table__hooks-events">
{ data.hooks.map( ( hook, index ) => (
<span key={ hook }>
{ hook }{ ' ' }
{ index !== data.hooks.length - 1 && ',' }
</span>
) ) }
</td>
<td className="ppcp-r-table__hooks-url">{ data?.[ 0 ] }</td>
<td
className="ppcp-r-table__hooks-events"
dangerouslySetInnerHTML={ { __html: data?.[ 1 ] } }
></td>
</tr>
</tbody>
</table>

View file

@ -23,4 +23,5 @@ export default {
DO_SANDBOX_LOGIN: 'COMMON:DO_SANDBOX_LOGIN',
DO_PRODUCTION_LOGIN: 'COMMON:DO_PRODUCTION_LOGIN',
DO_REFRESH_MERCHANT: 'COMMON:DO_REFRESH_MERCHANT',
DO_WEBHOOKS_DATA: 'COMMON:DO_WEBHOOKS_DATA',
};

View file

@ -134,6 +134,11 @@ export const setClientSecret = ( clientSecret ) => ( {
payload: { clientSecret },
} );
export const setWebhooks = ( webhooks ) => ( {
type: ACTION_TYPES.SET_PERSISTENT,
payload: { webhooks },
} );
/**
* Side effect. Saves the persistent details to the WP database.
*

View file

@ -53,3 +53,5 @@ export const REST_MANUAL_CONNECTION_PATH = '/wc/v3/wc_paypal/connect_manual';
* @type {string}
*/
export const REST_CONNECTION_URL_PATH = '/wc/v3/wc_paypal/login_link';
export const REST_WEBHOOKS = '/wc/v3/wc_paypal/webhook_settings';
export const REST_WEBHOOKS_SIMULATE = '/wc/v3/wc_paypal/webhooks_simulate';

View file

@ -9,6 +9,8 @@
import { useDispatch, useSelect } from '@wordpress/data';
import { useCallback } from '@wordpress/element';
import { REST_WEBHOOKS, REST_WEBHOOKS_SIMULATE } from './constants';
import apiFetch from '@wordpress/api-fetch';
import { STORE_NAME } from './constants';
@ -31,6 +33,7 @@ const useHooks = () => {
setManualConnectionMode,
setClientId,
setClientSecret,
setWebhooks,
connectToSandbox,
connectToProduction,
connectViaIdAndSecret,
@ -44,7 +47,7 @@ const useHooks = () => {
const clientSecret = usePersistent( 'clientSecret' );
const isSandboxMode = usePersistent( 'useSandbox' );
const isManualConnectionMode = usePersistent( 'useManualConnection' );
const webhooks = usePersistent( 'webhooks' );
const merchant = useSelect(
( select ) => select( STORE_NAME ).merchant(),
[]
@ -77,11 +80,32 @@ const useHooks = () => {
setClientSecret: ( value ) => {
return savePersistent( setClientSecret, value );
},
setWebhooks: async () => {
const response = await apiFetch( {
method: 'GET',
path: REST_WEBHOOKS,
} );
setWebhooks( response?.data?.webhooks );
},
registerWebhooks: async () => {
const response = await apiFetch( {
method: 'POST',
path: REST_WEBHOOKS,
} );
setWebhooks( response?.data?.webhooks );
},
simulateWebhooks: async () => {
const response = await apiFetch( {
path: REST_WEBHOOKS_SIMULATE,
} );
console.log( response );
},
connectToSandbox,
connectToProduction,
connectViaIdAndSecret,
merchant,
wooSettings,
webhooks,
};
};
@ -125,6 +149,10 @@ export const useWooSettings = () => {
return wooSettings;
};
export const useWebhooks = () => {
const { webhooks, setWebhooks, registerWebhooks } = useHooks();
return { webhooks, setWebhooks, registerWebhooks };
};
export const useMerchantInfo = () => {
const { merchant } = useHooks();
const { refreshMerchantData } = useDispatch( STORE_NAME );

View file

@ -35,6 +35,7 @@ const defaultPersistent = Object.freeze( {
useManualConnection: false,
clientId: '',
clientSecret: '',
webhooks: 0,
} );
// Reducer logic.

View file

@ -12,7 +12,7 @@ import { dispatch } from '@wordpress/data';
import { __ } from '@wordpress/i18n';
import { apiFetch } from '@wordpress/data-controls';
import { STORE_NAME, REST_HYDRATE_PATH } from './constants';
import { STORE_NAME, REST_HYDRATE_PATH, REST_WEBHOOKS } from './constants';
export const resolvers = {
/**
@ -21,7 +21,9 @@ export const resolvers = {
*persistentData() {
try {
const result = yield apiFetch( { path: REST_HYDRATE_PATH } );
const webhooks = yield apiFetch( { path: REST_WEBHOOKS } );
result.data = { ...result.data, ...webhooks.data };
yield dispatch( STORE_NAME ).hydrate( result );
yield dispatch( STORE_NAME ).setIsReady( true );
} catch ( e ) {

View file

@ -33,3 +33,7 @@ export const merchant = ( state ) => {
export const wooSettings = ( state ) => {
return getState( state ).wooSettings || EMPTY_OBJ;
};
export const webhooks = ( state ) => {
return getState( state ).webhooks || EMPTY_OBJ;
};

View file

@ -18,6 +18,7 @@ use WooCommerce\PayPalCommerce\Settings\Endpoint\ConnectManualRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\LoginLinkRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\OnboardingRestEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\SwitchSettingsUiEndpoint;
use WooCommerce\PayPalCommerce\Settings\Endpoint\WebhookSettingsEndpoint;
use WooCommerce\PayPalCommerce\Settings\Service\ConnectionUrlGenerator;
use WooCommerce\PayPalCommerce\Settings\Service\OnboardingUrlManager;
use WooCommerce\PayPalCommerce\Vendor\Psr\Container\ContainerInterface;
@ -83,6 +84,13 @@ return array(
$container->get( 'settings.service.connection-url-generators' ),
);
},
'settings.rest.webhooks' => static function ( ContainerInterface $container ) : WebhookSettingsEndpoint {
return new WebhookSettingsEndpoint(
$container->get('webhook.status.registered-webhooks-data'),
$container->get( 'webhook.registrar'),
$container->get('webhook.status.simulation')
);
},
'settings.casual-selling.supported-countries' => static function ( ContainerInterface $container ) : array {
return array(
'AR',

View file

@ -58,6 +58,9 @@ class CommonRestEndpoint extends RestEndpoint {
'js_name' => 'clientSecret',
'sanitize' => 'sanitize_text_field',
),
'webhooks' => array(
'js_name' => 'webhooks'
)
);
/**

View file

@ -9,88 +9,24 @@ declare( strict_types = 1 );
namespace WooCommerce\PayPalCommerce\Settings\Endpoint;
use WP_REST_Server;
use WooCommerce\PayPalCommerce\Webhooks\Status\WebhookSimulation;
use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar;
use WP_REST_Response;
use WP_REST_Request;
use WooCommerce\PayPalCommerce\Settings\Data\OnboardingProfile;
use WP_REST_Server;
/**
* REST controller for the onboarding module.
*
* This API acts as the intermediary between the "external world" and our
* internal data model.
*/
class WebhookSettingsEndpoint extends RestEndpoint {
/**
* The base path for this REST controller.
*
* @var string
*/
protected $rest_base = 'webhook-settings';
protected $rest_base = 'webhook_settings';
protected string $rest_simulate_base = 'webhooks_simulate';
/**
* The settings instance.
*
* @var OnboardingProfile
*/
protected OnboardingProfile $profile;
private array $webhooksData;
private WebhookRegistrar $webhookRegistrar;
private WebhookSimulation $webhookSimulation;
/**
* Field mapping for request to profile transformation.
*
* @var array
*/
private array $field_map = array(
'completed' => array(
'js_name' => 'completed',
'sanitize' => 'to_boolean',
),
'step' => array(
'js_name' => 'step',
'sanitize' => 'to_number',
),
'is_casual_seller' => array(
'js_name' => 'isCasualSeller',
'sanitize' => 'to_boolean',
),
'are_optional_payment_methods_enabled' => array(
'js_name' => 'areOptionalPaymentMethodsEnabled',
'sanitize' => 'to_boolean',
),
'products' => array(
'js_name' => 'products',
),
);
/**
* Map the internal flags to JS names.
*
* @var array
*/
private array $flag_map = array(
'can_use_casual_selling' => array(
'js_name' => 'canUseCasualSelling',
),
'can_use_vaulting' => array(
'js_name' => 'canUseVaulting',
),
'can_use_card_payments' => array(
'js_name' => 'canUseCardPayments',
),
'can_use_subscriptions' => array(
'js_name' => 'canUseSubscriptions',
),
);
/**
* Constructor.
*
* @param OnboardingProfile $profile The settings instance.
*/
public function __construct( OnboardingProfile $profile ) {
$this->profile = $profile;
$this->field_map['products']['sanitize'] = fn( $list ) => array_map( 'sanitize_text_field', $list );
public function __construct(array $webhooksData, WebhookRegistrar $webhookRegistrar, WebhookSimulation $webhookSimulation)
{
$this->webhooksData = $webhooksData;
$this->webhookRegistrar = $webhookRegistrar;
$this->webhookSimulation = $webhookSimulation;
}
/**
@ -103,65 +39,43 @@ class WebhookSettingsEndpoint extends RestEndpoint {
array(
array(
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'get_details' ),
'callback' => array( $this, 'get_webhooks' ),
'permission_callback' => array( $this, 'check_permission' ),
),
array(
'methods' => WP_REST_Server::CREATABLE,
'callback' => array($this, 'register_webhooks'),
'permission_callback' => array($this, 'check_permission')
)
)
);
register_rest_route(
$this->namespace,
'/' . $this->rest_base,
'/' . $this->rest_simulate_base,
array(
array(
'methods' => WP_REST_Server::EDITABLE,
'callback' => array( $this, 'update_details' ),
'methods' => WP_REST_Server::READABLE,
'callback' => array( $this, 'simulate_webhooks' ),
'permission_callback' => array( $this, 'check_permission' ),
),
)
)
);
}
/**
* Returns all details of the current onboarding wizard progress.
*
* @return WP_REST_Response The current state of the onboarding wizard.
*/
public function get_details() : WP_REST_Response {
$js_data = $this->sanitize_for_javascript(
$this->profile->to_array(),
$this->field_map
);
$js_flags = $this->sanitize_for_javascript(
$this->profile->get_flags(),
$this->flag_map
);
return $this->return_success(
$js_data,
array(
'flags' => $js_flags,
)
);
public function get_webhooks(): WP_REST_Response{
return $this->return_success( ["webhooks" => $this->webhooksData['data'][0]] );
}
/**
* Updates onboarding details based on the request.
*
* @param WP_REST_Request $request Full data about the request.
*
* @return WP_REST_Response The updated state of the onboarding wizard.
*/
public function update_details( WP_REST_Request $request ) : WP_REST_Response {
$wp_data = $this->sanitize_for_wordpress(
$request->get_params(),
$this->field_map
);
public function register_webhooks(): WP_REST_Response{
if ( ! $this->webhookRegistrar->register() ) {
return $this->return_error('Webhook subscription failed.');
}
$this->profile->from_array( $wp_data );
$this->profile->save();
return $this->return_success(["webhooks" => $this->webhooksData['data'][0]]);
}
return $this->get_details();
public function simulate_webhooks(): WP_REST_Response{
$this->return_success(['success' => true]);
}
}

View file

@ -181,6 +181,7 @@ class SettingsModule implements ServiceModule, ExecutableModule {
$container->get( 'settings.rest.common' ),
$container->get( 'settings.rest.connect_manual' ),
$container->get( 'settings.rest.login_link' ),
$container->get('settings.rest.webhooks')
);
foreach ( $endpoints as $endpoint ) {