630 lines
19 KiB
PHP
630 lines
19 KiB
PHP
<?php
|
|
/**
|
|
* REST API License Controller.
|
|
*
|
|
* @package PopupMaker
|
|
* @copyright Copyright (c) 2024, Code Atlantic LLC
|
|
*/
|
|
|
|
namespace PopupMaker\RestAPI;
|
|
|
|
use WP_REST_Controller;
|
|
use WP_REST_Server;
|
|
use WP_REST_Request;
|
|
use WP_REST_Response;
|
|
use WP_Error;
|
|
|
|
defined( 'ABSPATH' ) || exit;
|
|
|
|
/**
|
|
* License REST API Controller.
|
|
*
|
|
* Handles license management endpoints for Pro upgrade workflow.
|
|
*
|
|
* @since 1.21.0
|
|
*/
|
|
class License extends WP_REST_Controller {
|
|
|
|
/**
|
|
* Endpoint namespace.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $namespace = 'popup-maker/v2';
|
|
|
|
/**
|
|
* Route base.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $rest_base = 'license';
|
|
|
|
|
|
/**
|
|
* Register the routes for the license endpoints.
|
|
*
|
|
* @return void
|
|
*/
|
|
public function register_routes() {
|
|
// GET /license - Get license information.
|
|
register_rest_route(
|
|
$this->namespace,
|
|
'/' . $this->rest_base,
|
|
[
|
|
[
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => [ $this, 'get_license' ],
|
|
'permission_callback' => [ $this, 'get_license_permissions_check' ],
|
|
'args' => [],
|
|
],
|
|
'schema' => [ $this, 'get_license_schema' ],
|
|
]
|
|
);
|
|
|
|
// POST /license/activate - Activate license key.
|
|
register_rest_route(
|
|
$this->namespace,
|
|
'/' . $this->rest_base . '/activate',
|
|
[
|
|
[
|
|
'methods' => WP_REST_Server::CREATABLE,
|
|
'callback' => [ $this, 'activate_license' ],
|
|
'permission_callback' => [ $this, 'license_action_permissions_check' ],
|
|
'args' => $this->get_activate_license_args(),
|
|
],
|
|
]
|
|
);
|
|
|
|
// POST /license/activate-pro - Activate license and install Pro.
|
|
register_rest_route(
|
|
$this->namespace,
|
|
'/' . $this->rest_base . '/activate-pro',
|
|
[
|
|
[
|
|
'methods' => WP_REST_Server::CREATABLE,
|
|
'callback' => [ $this, 'activate_license_pro' ],
|
|
'permission_callback' => [ $this, 'license_action_permissions_check' ],
|
|
'args' => $this->get_activate_license_args(),
|
|
],
|
|
]
|
|
);
|
|
|
|
// GET /license/connect-info - Get connection info for popup upgrade flow.
|
|
register_rest_route(
|
|
$this->namespace,
|
|
'/' . $this->rest_base . '/connect-info',
|
|
[
|
|
[
|
|
'methods' => WP_REST_Server::READABLE,
|
|
'callback' => [ $this, 'get_connect_info' ],
|
|
'permission_callback' => [ $this, 'license_action_permissions_check' ],
|
|
'args' => $this->get_connect_info_args(),
|
|
],
|
|
]
|
|
);
|
|
|
|
// POST /license/activate-plugin - Activate Pro plugin if installed.
|
|
register_rest_route(
|
|
$this->namespace,
|
|
'/' . $this->rest_base . '/activate-plugin',
|
|
[
|
|
[
|
|
'methods' => WP_REST_Server::CREATABLE,
|
|
'callback' => [ $this, 'activate_plugin' ],
|
|
'permission_callback' => [ $this, 'license_action_permissions_check' ],
|
|
'args' => [],
|
|
],
|
|
]
|
|
);
|
|
|
|
// POST /license/deactivate - Deactivate license.
|
|
register_rest_route(
|
|
$this->namespace,
|
|
'/' . $this->rest_base . '/deactivate',
|
|
[
|
|
[
|
|
'methods' => WP_REST_Server::CREATABLE,
|
|
'callback' => [ $this, 'deactivate_license' ],
|
|
'permission_callback' => [ $this, 'license_action_permissions_check' ],
|
|
'args' => [],
|
|
],
|
|
]
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Get license information.
|
|
*
|
|
* @param WP_REST_Request $request Full data about the request.
|
|
* @return WP_REST_Response|WP_Error Response object or WP_Error on failure.
|
|
*/
|
|
public function get_license( $request ) {
|
|
$license_service = \PopupMaker\plugin( 'license' );
|
|
|
|
$response_data = [
|
|
'license_key' => $license_service->get_license_key(),
|
|
'status' => $license_service->get_license_status(),
|
|
'status_data' => $license_service->get_license_status_data(),
|
|
'is_active' => $license_service->is_license_active(),
|
|
'is_pro_installed' => \PopupMaker\plugin()->is_pro_installed(),
|
|
'is_pro_active' => \PopupMaker\plugin()->is_pro_active(),
|
|
'has_extensions' => \PopupMaker\plugin()->has_extensions(),
|
|
'has_pro_plus_addons' => \PopupMaker\plugin()->has_pro_plus_addons(),
|
|
'can_upgrade' => false,
|
|
'connect_info' => null,
|
|
];
|
|
|
|
// Add upgrade information if license is valid but Pro isn't installed.
|
|
if ( $license_service->is_license_active() && ! \PopupMaker\plugin()->is_pro_installed() ) {
|
|
$response_data['can_upgrade'] = true;
|
|
$response_data['connect_info'] = $license_service->generate_connect_info();
|
|
}
|
|
|
|
return new WP_REST_Response( $response_data, 200 );
|
|
}
|
|
|
|
/**
|
|
* Activate license key.
|
|
*
|
|
* @param WP_REST_Request $request Full data about the request.
|
|
* @return WP_REST_Response|WP_Error Response object or WP_Error on failure.
|
|
*/
|
|
public function activate_license( $request ) {
|
|
$license_key = $request->get_param( 'license_key' );
|
|
$license_service = \PopupMaker\plugin( 'license' );
|
|
|
|
try {
|
|
$activated = $license_service->maybe_activate_license( $license_key );
|
|
|
|
if ( ! $activated ) {
|
|
$status_data = $license_service->get_license_status_data();
|
|
$error_message = $license_service->get_license_error_message( $status_data );
|
|
|
|
return new WP_Error(
|
|
'license_activation_failed',
|
|
$error_message ?: __( 'License activation failed.', 'popup-maker' ),
|
|
[ 'status' => 400 ]
|
|
);
|
|
}
|
|
|
|
$response_data = [
|
|
'success' => true,
|
|
'message' => __( 'License activated successfully.', 'popup-maker' ),
|
|
'license_key' => $license_service->get_license_key(),
|
|
'status' => $license_service->get_license_status(),
|
|
'status_data' => $license_service->get_license_status_data(),
|
|
'is_active' => $license_service->is_license_active(),
|
|
'is_pro_installed' => \PopupMaker\plugin()->is_pro_installed(),
|
|
'is_pro_active' => \PopupMaker\plugin()->is_pro_active(),
|
|
];
|
|
|
|
return new WP_REST_Response( $response_data, 200 );
|
|
} catch ( \Exception $e ) {
|
|
return new WP_Error(
|
|
'license_activation_error',
|
|
$e->getMessage(),
|
|
[ 'status' => 500 ]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Activate license key and provide Pro installation info.
|
|
*
|
|
* @param WP_REST_Request $request Full data about the request.
|
|
* @return WP_REST_Response|WP_Error Response object or WP_Error on failure.
|
|
*/
|
|
public function activate_license_pro( $request ) {
|
|
$license_key = $request->get_param( 'license_key' );
|
|
$license_service = \PopupMaker\plugin( 'license' );
|
|
|
|
try {
|
|
$activated = $license_service->maybe_activate_license( $license_key );
|
|
|
|
if ( ! $activated ) {
|
|
$status_data = $license_service->get_license_status_data();
|
|
$error_message = $license_service->get_license_error_message( $status_data );
|
|
|
|
return new WP_Error(
|
|
'license_activation_failed',
|
|
$error_message ?: __( 'License activation failed.', 'popup-maker' ),
|
|
[ 'status' => 400 ]
|
|
);
|
|
}
|
|
|
|
$response_data = [
|
|
'success' => true,
|
|
'message' => __( 'License activated successfully.', 'popup-maker' ),
|
|
'license_key' => $license_service->get_license_key(),
|
|
'status' => $license_service->get_license_status(),
|
|
'status_data' => $license_service->get_license_status_data(),
|
|
'is_active' => $license_service->is_license_active(),
|
|
'is_pro_installed' => \PopupMaker\plugin()->is_pro_installed(),
|
|
'is_pro_active' => \PopupMaker\plugin()->is_pro_active(),
|
|
'can_upgrade' => false,
|
|
'connect_info' => null,
|
|
];
|
|
|
|
// Add upgrade information if Pro isn't installed.
|
|
if ( ! \PopupMaker\plugin()->is_pro_installed() ) {
|
|
$response_data['can_upgrade'] = true;
|
|
$response_data['connect_info'] = $license_service->generate_connect_info();
|
|
$response_data['message'] = __( 'License activated successfully. Ready for Pro upgrade.', 'popup-maker' );
|
|
}
|
|
|
|
return new WP_REST_Response( $response_data, 200 );
|
|
} catch ( \Exception $e ) {
|
|
return new WP_Error(
|
|
'license_activation_error',
|
|
$e->getMessage(),
|
|
[ 'status' => 500 ]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deactivate license.
|
|
*
|
|
* @param WP_REST_Request $request Full data about the request.
|
|
* @return WP_REST_Response|WP_Error Response object or WP_Error on failure.
|
|
*/
|
|
public function deactivate_license( $request ) {
|
|
$license_service = \PopupMaker\plugin( 'license' );
|
|
|
|
try {
|
|
$deactivated = $license_service->deactivate_license();
|
|
|
|
if ( ! $deactivated ) {
|
|
return new WP_Error(
|
|
'license_deactivation_failed',
|
|
__( 'License deactivation failed.', 'popup-maker' ),
|
|
[ 'status' => 400 ]
|
|
);
|
|
}
|
|
|
|
$response_data = [
|
|
'success' => true,
|
|
'message' => __( 'License deactivated successfully.', 'popup-maker' ),
|
|
'license_key' => $license_service->get_license_key(),
|
|
'status' => $license_service->get_license_status(),
|
|
'status_data' => $license_service->get_license_status_data(),
|
|
'is_active' => $license_service->is_license_active(),
|
|
'is_pro_installed' => \PopupMaker\plugin()->is_pro_installed(),
|
|
'is_pro_active' => \PopupMaker\plugin()->is_pro_active(),
|
|
];
|
|
|
|
return new WP_REST_Response( $response_data, 200 );
|
|
} catch ( \Exception $e ) {
|
|
return new WP_Error(
|
|
'license_deactivation_error',
|
|
$e->getMessage(),
|
|
[ 'status' => 500 ]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check if a given request has access to get license information.
|
|
*
|
|
* @param WP_REST_Request $request Full data about the request.
|
|
* @return true|WP_Error True if the request has read access, WP_Error object otherwise.
|
|
*/
|
|
public function get_license_permissions_check( $request ) {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return new WP_Error(
|
|
'rest_forbidden',
|
|
__( 'Sorry, you are not allowed to view license information.', 'popup-maker' ),
|
|
[ 'status' => rest_authorization_required_code() ]
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Check if a given request has access to perform license actions.
|
|
*
|
|
* @param WP_REST_Request $request Full data about the request.
|
|
* @return true|WP_Error True if the request has access, WP_Error object otherwise.
|
|
*/
|
|
public function license_action_permissions_check( $request ) {
|
|
if ( ! current_user_can( 'manage_options' ) ) {
|
|
return new WP_Error(
|
|
'rest_forbidden',
|
|
__( 'Sorry, you are not allowed to manage licenses.', 'popup-maker' ),
|
|
[ 'status' => rest_authorization_required_code() ]
|
|
);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Get the arguments for license activation endpoints.
|
|
*
|
|
* @return array<string,array<string,mixed>>
|
|
*/
|
|
public function get_activate_license_args() {
|
|
return [
|
|
'license_key' => [
|
|
'description' => __( 'License key to activate.', 'popup-maker' ),
|
|
'type' => 'string',
|
|
'required' => true,
|
|
'sanitize_callback' => 'sanitize_text_field',
|
|
'validate_callback' => function ( $param ) {
|
|
if ( empty( $param ) || ! is_string( $param ) ) {
|
|
return new WP_Error(
|
|
'invalid_license_key',
|
|
__( 'License key is required and must be a valid string.', 'popup-maker' ),
|
|
[ 'status' => 400 ]
|
|
);
|
|
}
|
|
return true;
|
|
},
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get the schema for license endpoints.
|
|
*
|
|
* @return array<string,mixed>
|
|
*/
|
|
public function get_license_schema() {
|
|
return [
|
|
'$schema' => 'http://json-schema.org/draft-04/schema#',
|
|
'title' => 'license',
|
|
'type' => 'object',
|
|
'properties' => [
|
|
'license_key' => [
|
|
'description' => __( 'The license key.', 'popup-maker' ),
|
|
'type' => 'string',
|
|
'readonly' => true,
|
|
],
|
|
'status' => [
|
|
'description' => __( 'The license status.', 'popup-maker' ),
|
|
'type' => 'string',
|
|
'enum' => [ 'empty', 'inactive', 'expired', 'error', 'valid' ],
|
|
'readonly' => true,
|
|
],
|
|
'status_data' => [
|
|
'description' => __( 'Detailed license status data.', 'popup-maker' ),
|
|
'type' => [ 'object', 'null' ],
|
|
'readonly' => true,
|
|
],
|
|
'is_active' => [
|
|
'description' => __( 'Whether the license is active.', 'popup-maker' ),
|
|
'type' => 'boolean',
|
|
'readonly' => true,
|
|
],
|
|
'is_pro_installed' => [
|
|
'description' => __( 'Whether Pro is installed.', 'popup-maker' ),
|
|
'type' => 'boolean',
|
|
'readonly' => true,
|
|
],
|
|
'is_pro_active' => [
|
|
'description' => __( 'Whether Pro is active.', 'popup-maker' ),
|
|
'type' => 'boolean',
|
|
'readonly' => true,
|
|
],
|
|
'can_upgrade' => [
|
|
'description' => __( 'Whether Pro upgrade is available.', 'popup-maker' ),
|
|
'type' => 'boolean',
|
|
'readonly' => true,
|
|
],
|
|
'connect_info' => [
|
|
'description' => __( 'Connection information for Pro upgrade.', 'popup-maker' ),
|
|
'type' => [ 'object', 'null' ],
|
|
'readonly' => true,
|
|
],
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Get connection info for upgrade flow.
|
|
*
|
|
* @param WP_REST_Request $request Full details about the request.
|
|
* @return WP_REST_Response|WP_Error Response object or WP_Error on failure.
|
|
*/
|
|
public function get_connect_info( $request ) {
|
|
$license_key = sanitize_text_field( $request->get_param( 'license_key' ) );
|
|
$context = $request->get_param( 'context' );
|
|
|
|
// Parse context if provided as JSON string.
|
|
if ( is_string( $context ) ) {
|
|
$context = json_decode( $context, true );
|
|
if ( null === $context && JSON_ERROR_NONE !== json_last_error() ) {
|
|
return new WP_Error(
|
|
'invalid_context_json',
|
|
__( 'Invalid JSON provided for context parameter.', 'popup-maker' ),
|
|
[ 'status' => 400 ]
|
|
);
|
|
}
|
|
}
|
|
|
|
if ( ! is_array( $context ) ) {
|
|
$context = [];
|
|
}
|
|
|
|
// CRITICAL FIX: Use stored/activated license key instead of form field
|
|
// This ensures we send the same license key that shows as "valid" locally
|
|
$license_service = \PopupMaker\plugin( 'license' );
|
|
$stored_license_key = $license_service->get_license_key();
|
|
|
|
// Prefer stored license key over form field, use form field only as fallback
|
|
$final_license_key = ! empty( $stored_license_key ) ? $stored_license_key : $license_key;
|
|
|
|
try {
|
|
// Use the Connect service to generate proper connection info.
|
|
$connect_service = \PopupMaker\plugin( 'connect' );
|
|
$connect_info = $connect_service->get_connect_info( $final_license_key );
|
|
|
|
// Parse the URL to extract individual parameters for frontend use.
|
|
$parsed_url = wp_parse_url( $connect_info['url'] );
|
|
$query_params = [];
|
|
|
|
if ( isset( $parsed_url['query'] ) ) {
|
|
parse_str( $parsed_url['query'], $query_params );
|
|
}
|
|
|
|
// Return individual parameters that the frontend can use.
|
|
$response_data = [
|
|
'success' => true,
|
|
'data' => array_merge( $query_params, [
|
|
'product' => $context['product'] ?? 'popup-maker-pro',
|
|
'source' => $context['source'] ?? 'rest-api',
|
|
'campaign' => $context['campaign'] ?? 'upgrade-flow',
|
|
'base_url' => $parsed_url['scheme'] . '://' . $parsed_url['host'] . ( $parsed_url['path'] ?? '' ),
|
|
'full_url' => $connect_info['url'],
|
|
'back_url' => $connect_info['back_url'],
|
|
]),
|
|
];
|
|
|
|
return new WP_REST_Response( $response_data, 200 );
|
|
} catch ( \Exception $e ) {
|
|
return new WP_Error(
|
|
'connect_info_error',
|
|
$e->getMessage(),
|
|
[ 'status' => 500 ]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get arguments for connect-info endpoint.
|
|
*
|
|
* @return array<string,array<string,mixed>>
|
|
*/
|
|
public function get_connect_info_args() {
|
|
return [
|
|
'license_key' => [
|
|
'description' => __( 'License key for connection.', 'popup-maker' ),
|
|
'type' => 'string',
|
|
'required' => false,
|
|
'validate_callback' => function ( $param ) {
|
|
return is_string( $param ) && strlen( $param ) <= 100;
|
|
},
|
|
'sanitize_callback' => 'sanitize_text_field',
|
|
],
|
|
'context' => [
|
|
'description' => __( 'Additional context information.', 'popup-maker' ),
|
|
'type' => [ 'string', 'object' ],
|
|
'required' => false,
|
|
'validate_callback' => function ( $param ) {
|
|
if ( is_string( $param ) ) {
|
|
$decoded = json_decode( $param, true );
|
|
return json_last_error() === JSON_ERROR_NONE;
|
|
}
|
|
return is_array( $param );
|
|
},
|
|
],
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Activate Pro plugin if installed but not active.
|
|
*
|
|
* @param WP_REST_Request $request Full details about the request.
|
|
* @return WP_REST_Response|WP_Error Response object or WP_Error on failure.
|
|
*/
|
|
public function activate_plugin( $request ) {
|
|
// Check if Pro plugin is installed.
|
|
if ( ! \PopupMaker\plugin()->is_pro_installed() ) {
|
|
return new WP_Error(
|
|
'pro_not_installed',
|
|
__( 'Pro plugin is not installed.', 'popup-maker' ),
|
|
[ 'status' => 404 ]
|
|
);
|
|
}
|
|
|
|
// Check if Pro plugin is already active.
|
|
if ( \PopupMaker\plugin()->is_pro_active() ) {
|
|
return new WP_Error(
|
|
'pro_already_active',
|
|
__( 'Pro plugin is already active.', 'popup-maker' ),
|
|
[ 'status' => 400 ]
|
|
);
|
|
}
|
|
|
|
// Activate the Pro plugin.
|
|
if ( ! function_exists( 'activate_plugin' ) ) {
|
|
require_once ABSPATH . 'wp-admin/includes/plugin.php';
|
|
}
|
|
|
|
$plugin_path = 'popup-maker-pro/popup-maker-pro.php';
|
|
$result = activate_plugin( $plugin_path, '', false, true );
|
|
|
|
if ( is_wp_error( $result ) ) {
|
|
return new WP_Error(
|
|
'activation_failed',
|
|
sprintf(
|
|
/* translators: %s - Error message */
|
|
__( 'Something went wrong, the Pro plugin could not be activated: %s', 'popup-maker' ),
|
|
$result->get_error_message()
|
|
),
|
|
[ 'status' => 500 ]
|
|
);
|
|
}
|
|
|
|
// Return success response.
|
|
return new WP_REST_Response( [
|
|
'success' => true,
|
|
'message' => __( 'Pro plugin activated successfully.', 'popup-maker' ),
|
|
'is_pro_installed' => \PopupMaker\plugin()->is_pro_installed(),
|
|
'is_pro_active' => \PopupMaker\plugin()->is_pro_active(),
|
|
], 200 );
|
|
}
|
|
|
|
/**
|
|
* Handle webhook request from upgrade server.
|
|
*
|
|
* @param WP_REST_Request $request Full details about the request.
|
|
* @return WP_REST_Response|WP_Error Response object or WP_Error on failure.
|
|
*/
|
|
public function handle_webhook( $request ) {
|
|
try {
|
|
// Use Connect service to handle the webhook processing.
|
|
$connect_service = \PopupMaker\plugin( 'connect' );
|
|
|
|
// Verify the webhook request is valid and secure.
|
|
$verification = $connect_service->verify_webhook_request( $request );
|
|
|
|
if ( ! $verification['valid'] ) {
|
|
return new WP_Error(
|
|
'webhook_verification_failed',
|
|
$verification['error'] ?: __( 'Webhook verification failed.', 'popup-maker' ),
|
|
[ 'status' => 403 ]
|
|
);
|
|
}
|
|
|
|
// Process the webhook - this will handle the Pro installation.
|
|
$connect_service->process_webhook();
|
|
|
|
// If we get here, the webhook was processed successfully.
|
|
return new WP_REST_Response( [
|
|
'success' => true,
|
|
'message' => __( 'Webhook processed successfully.', 'popup-maker' ),
|
|
], 200 );
|
|
} catch ( \Exception $e ) {
|
|
return new WP_Error(
|
|
'webhook_processing_error',
|
|
$e->getMessage(),
|
|
[ 'status' => 500 ]
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check permissions for webhook endpoint.
|
|
*
|
|
* @param WP_REST_Request $request Full details about the request.
|
|
* @return true|WP_Error True if allowed, WP_Error otherwise.
|
|
*/
|
|
public function webhook_permissions_check( $request ) {
|
|
// Webhook requests come from external upgrade server with special authentication.
|
|
// The Connect service handles its own authentication via tokens and signatures.
|
|
return true;
|
|
}
|
|
}
|