\WP_REST_Server::READABLE, 'callback' => array( $this, 'validate_key' ), 'args' => array(), 'permission_callback' => function () { return true; }, ) ); register_rest_route( $namespace, '/delete_test_key/', array( 'methods' => \WP_REST_Server::DELETABLE, 'callback' => array( $this, 'delete_test' ), 'args' => array(), 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ) ); register_rest_route( $namespace, '/save_test_key/', array( 'methods' => \WP_REST_Server::CREATABLE, 'callback' => array( $this, 'save_test' ), 'args' => array( 'test_key' => array( 'description' => 'Test API key.', 'type' => 'string', 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ), ), 'permission_callback' => function () { return current_user_can( 'manage_options' ); }, ) ); register_rest_route( $namespace, '/activate/', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'activate_key' ), 'args' => array( 'license_key' => array( 'description' => 'License key.', 'type' => 'string', 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ), 'instance_name' => array( 'description' => 'Instance name for the activation.', 'type' => 'string', 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ), ), 'permission_callback' => function () { return true; }, ) ); register_rest_route( $namespace, '/deactivate/', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'deactivate_key' ), 'args' => array( 'license_key' => array( 'description' => 'License key.', 'type' => 'string', 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ), 'instance_id' => array( 'description' => 'Instance ID of the existing activation.', 'type' => 'string', 'required' => true, 'sanitize_callback' => 'sanitize_text_field', ), ), 'permission_callback' => function () { return true; }, ) ); register_rest_route( $namespace, '/stores/', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'get_stores' ), 'args' => array(), 'permission_callback' => function () { return true; }, ) ); register_rest_route( $namespace, '/products/', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'get_products' ), 'args' => array(), 'permission_callback' => function () { return true; }, ) ); register_rest_route( $namespace, '/update/', array( 'methods' => \WP_REST_Server::READABLE, 'callback' => array( $this, 'get_update' ), 'args' => array(), 'permission_callback' => function () { return true; }, ) ); } /** * Delete Test API key. * * @param \WP_REST_Request $request Full data about the request. * @return \WP_Error|\WP_REST_Response */ public function delete_test() { $deleted = delete_option( 'lsq_api_key_test' ); return new \WP_REST_Response( array( 'success' => $deleted, ), $deleted ? 200 : 400 ); } /** * Save Test API key with Lemon Squeezy API. * * @param \WP_REST_Request $request Full data about the request. * @return \WP_Error|\WP_REST_Response */ public function save_test( $request ) { $test_key = $request->get_param( 'test_key' ); $response = wp_remote_get( LSQ_API_URL . '/v1/users/me', array( 'headers' => array( 'Authorization' => 'Bearer ' . $test_key, 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', 'Cache-Control' => 'no-cache', ), ) ); $is_valid = false; if ( ! is_wp_error( $response ) ) { if ( 200 === wp_remote_retrieve_response_code( $response ) ) { $is_valid = true; $body = json_decode( $response['body'] ); $user = $body->data; update_option( 'lsq_api_key_test', $test_key ); } else { $error_message = wp_remote_retrieve_response_message( $response ); } } else { $error_message = $response->get_error_message(); } return new \WP_REST_Response( array( 'success' => $is_valid, 'user' => $user, 'error' => $error_message, ), $is_valid ? 200 : 400 ); } /** * Validate API key with Lemon Squeezy API. * * @param \WP_REST_Request $request Full data about the request. * @return \WP_Error|\WP_REST_Response */ public function validate_key() { // Check LS API connection. $api_key = get_option( 'lsq_api_key' ); $is_valid = false; $user = null; $error_message = ''; if ( ! isset( $api_key ) || empty( $api_key ) ) { return new \WP_REST_Response( array( 'success' => $is_valid, 'user' => $user, 'error' => __( 'Unauthorized request', 'lemon-squeezy' ), ), 401 ); } $response = wp_remote_get( LSQ_API_URL . '/v1/users/me', array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', 'Cache-Control' => 'no-cache', ), ) ); if ( ! is_wp_error( $response ) ) { if ( 200 === wp_remote_retrieve_response_code( $response ) ) { $is_valid = true; $body = json_decode( $response['body'] ); $user = $body->data; } else { $error_message = wp_remote_retrieve_response_message( $response ); } } else { $error_message = $response->get_error_message(); } return new \WP_REST_Response( array( 'success' => $is_valid, 'user' => $user, 'error' => $error_message, ), $is_valid ? 200 : 400 ); } /** * Activate license key through Lemon Squeezy API. * * @param WP_REST_Request $request Full data about the request. * @return \WP_REST_Response|\WP_Error */ public function activate_key( $request ) { $license_key = $request->get_param( 'license_key' ); $instance_name = $request->get_param( 'instance_name' ); $is_valid = false; $error_message = ''; $api_key = get_option( 'lsq_api_key' ); if ( empty( $api_key ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Unauthorized request', 'lemon-squeezy' ), ), 401 ); } $response = wp_remote_post( LSQ_API_URL . "/v1/licenses/activate?license_key={$license_key}&instance_name={$instance_name}", array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', 'Cache-Control' => 'no-cache', ), ) ); $body = json_decode( $response['body'], true ); if ( ! is_wp_error( $response ) ) { if ( 200 === wp_remote_retrieve_response_code( $response ) ) { $is_valid = true; } else { $error_message = isset( $body['error'] ) ? $body['error'] : wp_remote_retrieve_response_message( $response ); } } else { $error_message = $response->get_error_message(); } return new \WP_REST_Response( array( 'success' => $is_valid, 'error' => $error_message, 'data' => $body, ), $is_valid ? 200 : 400 ); } /** * Deactivate license key through Lemon Squeezy API. * * @param WP_REST_Request $request Full data about the request. * @return \WP_REST_Response|\WP_Error */ public function deactivate_key( $request ) { $license_key = $request->get_param( 'license_key' ); $instance_name = $request->get_param( 'instance_id' ); $is_valid = false; $error_message = null; $api_key = get_option( 'lsq_api_key' ); if ( empty( $api_key ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Unauthorized request', 'lemon-squeezy' ), ), 401 ); } $response = wp_remote_post( LSQ_API_URL . "/v1/licenses/deactivate?license_key={$license_key}&instance_id={$instance_name}", array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', 'Cache-Control' => 'no-cache', ), ) ); if ( ! is_wp_error( $response ) ) { $body = json_decode( $response['body'], true ); if ( 200 === wp_remote_retrieve_response_code( $response ) ) { $is_valid = $body['deactivated']; } else { $error_message = isset( $body['error'] ) ? $body['error'] : wp_remote_retrieve_response_message( $response ); } } else { $error_message = $response->get_error_message(); } return new \WP_REST_Response( array( 'success' => $is_valid, 'error' => $error_message, ), $is_valid ? 200 : 400 ); } /** * Get products from the Lemon Squeezy API. * * @param \WP_REST_Request $request Full data about the request. * @return \WP_Error|\WP_REST_Response */ public function get_stores() { // Check LS API connection. $api_key = get_option( 'lsq_api_key' ); $error_message = ''; if ( ! isset( $api_key ) || empty( $api_key ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Unauthorized request', 'lemon-squeezy' ), 'error_code' => 'unauthorized', ), 401 ); } // Get stores. $response = wp_remote_get( LSQ_API_URL . '/v1/stores/', array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', 'Cache-Control' => 'no-cache', ), ) ); if ( ! is_wp_error( $response ) ) { if ( 200 === wp_remote_retrieve_response_code( $response ) ) { $store_data = json_decode( $response['body'] ); $stores = array(); // Build product list. if ( isset( $store_data ) && ! empty( $store_data ) ) { foreach ( $store_data->data as $store ) { $stores[] = array( 'label' => $store->attributes->name, 'value' => $store->id, ); } } return new \WP_REST_Response( array( 'success' => true, 'stores' => $stores, ), 200 ); } else { $error_message = wp_remote_retrieve_response_message( $response ); } } else { $error_message = $response->get_error_message(); } return new \WP_REST_Response( array( 'success' => false, 'error' => $error_message, 'error_code' => 'api_error', ), 400 ); } /** * Get products from the Lemon Squeezy API. * * @param \WP_REST_Request $request Full data about the request. * @return \WP_Error|\WP_REST_Response */ public function get_products( $request ) { // Check LS API connection. $api_key = get_option( 'lsq_api_key' ); $error_message = ''; if ( ! isset( $api_key ) || empty( $api_key ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Unauthorized request', 'lemon-squeezy' ), 'error_code' => 'unauthorized', ), 401 ); } $store_id = filter_var( $request->get_param( 'store_id' ), FILTER_SANITIZE_FULL_SPECIAL_CHARS ); $page_size = rawurlencode( 'page[size]' ); $response = wp_remote_get( LSQ_API_URL . "/v1/stores/{$store_id}/products?" . $page_size . '=100', array( 'headers' => array( 'Authorization' => 'Bearer ' . $api_key, 'Accept' => 'application/vnd.api+json', 'Content-Type' => 'application/vnd.api+json', 'Cache-Control' => 'no-cache', ), ) ); if ( ! is_wp_error( $response ) ) { if ( 200 === wp_remote_retrieve_response_code( $response ) ) { $product_data = json_decode( $response['body'] ); $products = array(); foreach ( $product_data->data as $product ) { if ( 'published' !== $product->attributes->status ) { continue; } $products[] = array( 'label' => $product->attributes->name, 'value' => $product->attributes->buy_now_url, ); } return new \WP_REST_Response( array( 'success' => true, 'products' => $products, ), 200 ); } else { $error_message = wp_remote_retrieve_response_message( $response ); } } else { $error_message = $response->get_error_message(); } return new \WP_REST_Response( array( 'success' => false, 'error' => $error_message, 'error_code' => 'api_error', ), 400 ); } /** * Validate and return a software update from the Lemon Squeezy API. * * @param \WP_REST_Request $request Full data about the request. * @return \WP_Error|\WP_REST_Response */ public function get_update( $request ) { $api_key = get_option( 'lsq_api_key' ); if ( empty( $api_key ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Unauthorized request', 'lemon-squeezy' ), 'error_code' => 'unauthorized', ), 401 ); } $license_key = filter_var( $request->get_param( 'license_key' ), FILTER_SANITIZE_FULL_SPECIAL_CHARS ); if ( empty( $license_key ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Missing license_key', 'lemon-squeezy' ), 'error_code' => 'missing_license_key', ), 401 ); } $lsq_updater = new LSQ_Updater(); $license = $lsq_updater->get_license( $license_key ); if ( empty( $license->valid ) || empty( $license->license_key->id ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Invalid license_key', 'lemon-squeezy' ), 'error_code' => 'invalid_license_key', ), 401 ); } $license_key_obj = $lsq_updater->get_license_key( $license->license_key->id ); if ( empty( $license_key_obj ) || empty( $license_key_obj->data->relationships->{'order-item'}->data ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Error fetching license_key', 'lemon-squeezy' ), 'error_code' => 'error_fetching_license_key', ), 400 ); } $order_item = $lsq_updater->relation_from_license_key( $license_key_obj, 'order-item' ); if ( ! $order_item ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Invalid order item', 'lemon-squeezy' ), 'error_code' => 'invalid_order_item', ), 400 ); } $files = $lsq_updater->get_files( $order_item->attributes->variant_id ); if ( empty( $files->data ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Missing files', 'lemon-squeezy' ), 'error_code' => 'missing_files', ), 400 ); } $sorted_files = $lsq_updater->sort_files_by_version( $files->data ); $latest_file = array_pop( $sorted_files ); if ( empty( $latest_file->attributes->version ) ) { return new \WP_REST_Response( array( 'success' => false, 'error' => __( 'Missing file version', 'lemon-squeezy' ), 'error_code' => 'missing_file_version', ), 400 ); } $store = $lsq_updater->relation_from_license_key( $license_key_obj, 'store' ); $product = $lsq_updater->relation_from_license_key( $license_key_obj, 'product' ); return new \WP_REST_Response( array( 'success' => true, 'error' => '', 'error_code' => '', 'update' => array( 'version' => $latest_file->attributes->version, 'tested' => null, 'requires' => null, 'author' => $store ? $store->attributes->name : null, 'author_profile' => $store ? $store->attributes->url : null, 'download_link' => $latest_file->attributes->download_url, 'trunk' => $latest_file->attributes->download_url, 'requires_php' => null, 'last_updated' => null, 'sections' => array( 'description' => $product ? $product->attributes->description : null, 'changelog' => $latest_file->attributes->version, ), ), ), 200 ); } }