<?php
/**
* REST controller for authenticating a PayPal merchant.
*
* @package WooCommerce\PayPalCommerce\Settings\Endpoint
*/
declare (strict_types=1);
namespace WooCommerce\PayPalCommerce\Settings\Endpoint;
use Exception;
use WooCommerce\PayPalCommerce\Vendor\Psr\Log\LoggerInterface;
use WP_REST_Request;
use WP_REST_Response;
use WP_REST_Server;
use WooCommerce\WooCommerce\Logging\Logger\NullLogger;
use WooCommerce\PayPalCommerce\Settings\Service\AuthenticationManager;
use WooCommerce\PayPalCommerce\Settings\Service\SettingsDataManager;
/**
* REST controller for authenticating and connecting to a PayPal merchant account.
*
* This endpoint is responsible for verifying credentials and establishing
* a connection, regardless of whether they are provided via:
* 1. Direct login (clientId + secret)
* 2. UI-driven login (sharedId + authCode)
*
* It handles the actual authentication process after the login URL has been used.
*/
class AuthenticationRestEndpoint extends \WooCommerce\PayPalCommerce\Settings\Endpoint\RestEndpoint
{
/**
* The base path for this REST controller.
*
* @var string
*/
protected $rest_base = 'authenticate';
/**
* Authentication manager service.
*
* @var AuthenticationManager
*/
private AuthenticationManager $authentication_manager;
/**
* Settings data manager service.
*
* @var SettingsDataManager
*/
private SettingsDataManager $data_manager;
private LoggerInterface $logger;
/**
* Defines the JSON response format (when connection was successful).
*
* @var array
*/
private array $response_map = array('merchant_id' => array('js_name' => 'merchantId'), 'merchant_email' => array('js_name' => 'email'));
public function __construct(AuthenticationManager $authentication_manager, SettingsDataManager $data_manager, ?LoggerInterface $logger = null)
{
$this->authentication_manager = $authentication_manager;
$this->data_manager = $data_manager;
$this->logger = $logger ?? new NullLogger();
}
/**
* Configure REST API routes.
*/
public function register_routes(): void
{
/**
* POST /wp-json/wc/v3/wc_paypal/authenticate/direct
* {
* clientId
* clientSecret
* useSandbox
* }
*/
register_rest_route(static::NAMESPACE, '/' . $this->rest_base . '/direct', array('methods' => WP_REST_Server::EDITABLE, 'callback' => array($this, 'connect_direct'), 'permission_callback' => array($this, 'check_permission'), 'args' => array('clientId' => array('required' => \true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field', 'minLength' => 80, 'maxLength' => 80), 'clientSecret' => array('required' => \true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'), 'useSandbox' => array('required' => \false, 'type' => 'boolean', 'default' => \false, 'sanitize_callback' => array($this, 'to_boolean')))));
/**
* POST /wp-json/wc/v3/wc_paypal/authenticate/oauth
* {
* sharedId
* authCode
* useSandbox
* }
*/
register_rest_route(static::NAMESPACE, '/' . $this->rest_base . '/oauth', array('methods' => WP_REST_Server::EDITABLE, 'callback' => array($this, 'connect_oauth'), 'permission_callback' => array($this, 'check_permission'), 'args' => array('sharedId' => array('required' => \true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'), 'authCode' => array('required' => \true, 'type' => 'string', 'sanitize_callback' => 'sanitize_text_field'), 'useSandbox' => array('default' => 0, 'type' => 'boolean', 'sanitize_callback' => array($this, 'to_boolean')))));
/**
* POST /wp-json/wc/v3/wc_paypal/authenticate/disconnect
*/
register_rest_route(static::NAMESPACE, '/' . $this->rest_base . '/disconnect', array('methods' => WP_REST_Server::EDITABLE, 'callback' => array($this, 'disconnect'), 'permission_callback' => array($this, 'check_permission')));
}
/**
* Direct login: Retrieves merchantId and email using clientId and clientSecret.
*
* This is the "Manual Login" logic, when a merchant already knows their
* API credentials.
*
* @param WP_REST_Request $request Full data about the request.
*/
public function connect_direct(WP_REST_Request $request): WP_REST_Response
{
$client_id = $request->get_param('clientId');
$client_secret = $request->get_param('clientSecret');
$use_sandbox = $request->get_param('useSandbox');
try {
$this->authentication_manager->validate_id_and_secret($client_id, $client_secret);
} catch (Exception $exception) {
return $this->return_error($exception->getMessage());
}
try {
$this->authentication_manager->authenticate_via_direct_api($use_sandbox, $client_id, $client_secret);
} catch (Exception $exception) {
$this->logger->error('Direct API authentication failed: ' . $exception->getMessage());
return $this->return_error(__('Could not connect to PayPal. Please verify your credentials and try again.', 'woocommerce-paypal-payments'));
}
$account = $this->authentication_manager->get_account_details();
$response = $this->sanitize_for_javascript($account, $this->response_map);
return $this->return_success($response);
}
/**
* OAuth login: Retrieves clientId and clientSecret using a sharedId and authCode.
*
* This is the final step in the UI-driven login via the OAuth popup, which
* is triggered by the LoginLinkRestEndpoint URL.
*
* @param WP_REST_Request $request Full data about the request.
*/
public function connect_oauth(WP_REST_Request $request): WP_REST_Response
{
$shared_id = $request->get_param('sharedId');
$auth_code = $request->get_param('authCode');
$use_sandbox = $request->get_param('useSandbox');
$this->authentication_manager->remember_oauth_connection_details($shared_id, $auth_code, $use_sandbox);
return $this->return_success(\true);
}
/**
* Disconnect the merchant and clear the authentication details.
*
* @param WP_REST_Request $request Full data about the request.
*
* @return WP_REST_Response
*/
public function disconnect(WP_REST_Request $request): WP_REST_Response
{
$reset_settings = $request->get_param('reset');
$this->authentication_manager->disconnect();
if ($reset_settings) {
$this->data_manager->reset_all_settings();
}
return $this->return_success('OK');
}
}