diff --git a/modules/ppcp-settings/services.php b/modules/ppcp-settings/services.php index d2dd9df97..edb8b37d6 100644 --- a/modules/ppcp-settings/services.php +++ b/modules/ppcp-settings/services.php @@ -49,6 +49,7 @@ use WooCommerce\PayPalCommerce\WcGateway\Settings\Settings; use WooCommerce\PayPalCommerce\PayLaterConfigurator\Endpoint\SaveConfig; use WooCommerce\PayPalCommerce\WcGateway\Helper\Environment; use WooCommerce\PayPalCommerce\WcGateway\Helper\ConnectionState; +use WooCommerce\PayPalCommerce\Settings\Service\InternalRestService; return array( 'settings.url' => static function ( ContainerInterface $container ) : string { @@ -172,7 +173,10 @@ return array( return new OnboardingRestEndpoint( $container->get( 'settings.data.onboarding' ) ); }, 'settings.rest.common' => static function ( ContainerInterface $container ) : CommonRestEndpoint { - return new CommonRestEndpoint( $container->get( 'settings.data.general' ) ); + return new CommonRestEndpoint( + $container->get( 'settings.data.general' ), + $container->get( 'settings.service.rest-service' ) + ); }, 'settings.rest.payment' => static function ( ContainerInterface $container ) : PaymentRestEndpoint { return new PaymentRestEndpoint( @@ -313,10 +317,13 @@ return array( $container->get( 'api.env.endpoint.login-seller' ), $container->get( 'api.repository.partner-referrals-data' ), $container->get( 'settings.connection-state' ), - $container->get( 'api.endpoint.partners' ), + $container->get( 'settings.service.rest-service' ), $container->get( 'woocommerce.logger.woocommerce' ) ); }, + 'settings.service.rest-service' => static function ( ContainerInterface $container ) : InternalRestService { + return new InternalRestService(); + }, 'settings.service.sanitizer' => static function ( ContainerInterface $container ) : DataSanitizer { return new DataSanitizer(); }, diff --git a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php index bbd29c744..82aa06b29 100644 --- a/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php +++ b/modules/ppcp-settings/src/Endpoint/CommonRestEndpoint.php @@ -13,6 +13,7 @@ use WP_REST_Server; use WP_REST_Response; use WP_REST_Request; use WooCommerce\PayPalCommerce\Settings\Data\GeneralSettings; +use WooCommerce\PayPalCommerce\Settings\Service\InternalRestService; /** * REST controller for "common" settings, which are used and modified by @@ -22,6 +23,11 @@ use WooCommerce\PayPalCommerce\Settings\Data\GeneralSettings; * internal data model. */ class CommonRestEndpoint extends RestEndpoint { + /** + * Full REST path to the merchant-details endpoint, relative to the namespace. + */ + protected const SELLER_ACCOUNT_PATH = 'common/seller-account'; + /** * The base path for this REST controller. * @@ -36,6 +42,13 @@ class CommonRestEndpoint extends RestEndpoint { */ protected GeneralSettings $settings; + /** + * Internal REST handler, used to authenticate internal requests. + * + * @var InternalRestService + */ + protected InternalRestService $rest_service; + /** * Field mapping for request to profile transformation. * @@ -104,10 +117,27 @@ class CommonRestEndpoint extends RestEndpoint { /** * Constructor. * - * @param GeneralSettings $settings The settings instance. + * @param GeneralSettings $settings The settings instance. + * @param InternalRestService $rest_service Internal REST handler, for authentication. */ - public function __construct( GeneralSettings $settings ) { - $this->settings = $settings; + public function __construct( GeneralSettings $settings, InternalRestService $rest_service ) { + $this->settings = $settings; + $this->rest_service = $rest_service; + } + + /** + * Returns the path to the "Get Seller Account Details" REST route. + * This is an internal route which is consumed by the plugin itself during onboarding. + * + * @param bool $full_route Whether to return the full endpoint path or just the route name. + * @return string The full path to the REST endpoint. + */ + public static function seller_account_route( bool $full_route = false ) : string { + if ( $full_route ) { + return '/' . static::NAMESPACE . '/' . self::SELLER_ACCOUNT_PATH; + } + + return self::SELLER_ACCOUNT_PATH; } /** @@ -155,6 +185,24 @@ class CommonRestEndpoint extends RestEndpoint { 'permission_callback' => array( $this, 'check_permission' ), ) ); + + /** + * GET /wp-json/wc/v3/wc_paypal/common/seller-account + */ + register_rest_route( + static::NAMESPACE, + self::seller_account_route(), + array( + 'methods' => WP_REST_Server::READABLE, + 'callback' => array( $this, 'get_seller_account_info' ), + 'permission_callback' => function ( WP_REST_Request $request ) { + $token = $request->get_header( 'X-Internal-Token' ); + $endpoint = self::seller_account_route(); + + return $this->rest_service->verify_token( $token, $endpoint ); + }, + ) + ); } /** @@ -205,6 +253,17 @@ class CommonRestEndpoint extends RestEndpoint { return $this->return_success( $js_data, $extra_data ); } + /** + * Requests details from the PayPal API. + * + * Used during onboarding to enrich the merchant details in the DB. + * + * @return WP_REST_Response Seller details, provided by PayPal's API. + */ + public function get_seller_account_info() : WP_REST_Response { + return $this->return_success( array( 'country' => 'XY' ) ); + } + /** * Appends the "merchant" attribute to the extra_data collection, which * contains details about the merchant's PayPal account, like the merchant ID. diff --git a/modules/ppcp-settings/src/Service/AuthenticationManager.php b/modules/ppcp-settings/src/Service/AuthenticationManager.php index 1637c73a6..b48a7554a 100644 --- a/modules/ppcp-settings/src/Service/AuthenticationManager.php +++ b/modules/ppcp-settings/src/Service/AuthenticationManager.php @@ -27,6 +27,7 @@ use WooCommerce\PayPalCommerce\Settings\DTO\MerchantConnectionDTO; use WooCommerce\PayPalCommerce\Webhooks\WebhookRegistrar; use WooCommerce\PayPalCommerce\Settings\Enum\SellerTypeEnum; use WooCommerce\PayPalCommerce\WcGateway\Helper\ConnectionState; +use WooCommerce\PayPalCommerce\Settings\Endpoint\CommonRestEndpoint; /** * Class that manages the connection to PayPal. @@ -75,22 +76,22 @@ class AuthenticationManager { private ConnectionState $connection_state; /** - * Partners endpoint. + * Internal REST service, to consume own REST handlers in a separate request. * - * @var PartnersEndpoint + * @var InternalRestService */ - private PartnersEndpoint $partners_endpoint; + private InternalRestService $rest_service; /** * Constructor. * - * @param GeneralSettings $common_settings Data model that stores the connection details. - * @param EnvironmentConfig $connection_host API host for direct authentication. - * @param EnvironmentConfig $login_endpoint API handler to fetch merchant credentials. - * @param PartnerReferralsData $referrals_data Partner referrals data. - * @param ConnectionState $connection_state Connection state manager. - * @param PartnersEndpoint $partners_endpoint Partners endpoint. - * @param ?LoggerInterface $logger Logging instance. + * @param GeneralSettings $common_settings Data model that stores the connection details. + * @param EnvironmentConfig $connection_host API host for direct authentication. + * @param EnvironmentConfig $login_endpoint API handler to fetch merchant credentials. + * @param PartnerReferralsData $referrals_data Partner referrals data. + * @param ConnectionState $connection_state Connection state manager. + * @param InternalRestService $rest_service Allows calling internal REST endpoints. + * @param ?LoggerInterface $logger Logging instance. */ public function __construct( GeneralSettings $common_settings, @@ -98,16 +99,16 @@ class AuthenticationManager { EnvironmentConfig $login_endpoint, PartnerReferralsData $referrals_data, ConnectionState $connection_state, - PartnersEndpoint $partners_endpoint, + InternalRestService $rest_service, ?LoggerInterface $logger = null ) { - $this->common_settings = $common_settings; - $this->connection_host = $connection_host; - $this->login_endpoint = $login_endpoint; - $this->referrals_data = $referrals_data; - $this->connection_state = $connection_state; - $this->partners_endpoint = $partners_endpoint; - $this->logger = $logger ?: new NullLogger(); + $this->common_settings = $common_settings; + $this->connection_host = $connection_host; + $this->login_endpoint = $login_endpoint; + $this->referrals_data = $referrals_data; + $this->connection_state = $connection_state; + $this->rest_service = $rest_service; + $this->logger = $logger ?: new NullLogger(); } /** @@ -281,17 +282,10 @@ class AuthenticationManager { */ $connection = $this->common_settings->get_merchant_data(); - try { - $seller_status = $this->partners_endpoint->seller_status(); - } catch ( PayPalApiException $exception ) { - $seller_status = null; - } - - $connection->is_sandbox = $use_sandbox; - $connection->client_id = $credentials['client_id']; - $connection->client_secret = $credentials['client_secret']; - $connection->merchant_id = $credentials['merchant_id']; - $connection->merchant_country = ! is_null( $seller_status ) ? $seller_status->country() : ''; + $connection->is_sandbox = $use_sandbox; + $connection->client_id = $credentials['client_id']; + $connection->client_secret = $credentials['client_secret']; + $connection->merchant_id = $credentials['merchant_id']; $this->update_connection_details( $connection ); } @@ -451,14 +445,14 @@ class AuthenticationManager { } try { - // TODO: this call only reliably works in the _next_ request, because in the current request the PartnersEndpoint instance might be initialized with an empty merchant_id. - $seller_status = $this->partners_endpoint->seller_status(); + $endpoint = CommonRestEndpoint::seller_account_route( true ); + $details = $this->rest_service->get_data( $endpoint ); // Request the merchant details via a PayPal API request. $connection = $this->common_settings->get_merchant_data(); // Enrich the connection details with additional details. - $connection->merchant_country = $seller_status->country(); + $connection->merchant_country = $details['country']; // Persist the changes. $this->common_settings->set_merchant_data( $connection ); diff --git a/modules/ppcp-settings/src/Service/InternalRestService.php b/modules/ppcp-settings/src/Service/InternalRestService.php new file mode 100644 index 000000000..a83e5fdcf --- /dev/null +++ b/modules/ppcp-settings/src/Service/InternalRestService.php @@ -0,0 +1,57 @@ +generate_token( $endpoint ); + $rest_url = rest_url( $endpoint ); + + $response = wp_remote_get( + $rest_url, + array( + 'headers' => array( + 'X-Internal-Token' => $token, + 'Content-Type' => 'application/json', + ), + ) + ); + + if ( is_wp_error( $response ) ) { + return array(); + } + + $body = wp_remote_retrieve_body( $response ); + + try { + $json = json_decode( $body, true, 512, JSON_THROW_ON_ERROR ); + } catch ( Throwable $exception ) { + return array(); + } + + if ( ! $json || empty( $json['success'] ) ) { + return array(); + } + + return $json['data']; + } + + public function verify_token( string $token, string $endpoint ) : bool { + $expected_token = $this->generate_token( $endpoint ); + + return $expected_token === $token; + } + + private function generate_token( string $token_id ) : string { + return base64_encode( $token_id ); + } +}