Skip to content

Instantly share code, notes, and snippets.

@fishuke
Created July 31, 2025 08:49
Show Gist options
  • Select an option

  • Save fishuke/47c312676afc936f6a0d52fb25eb83be to your computer and use it in GitHub Desktop.

Select an option

Save fishuke/47c312676afc936f6a0d52fb25eb83be to your computer and use it in GitHub Desktop.
Yith Waitlist Api
<?php
/**
* Plugin Name: YITH Waitlist API
* Description: Custom API to fetch YITH waitlist data via HTTP requests
* Version: 1.0.0
*/
// Prevent direct access
if (!defined('ABSPATH')) {
exit;
}
class YithWaitlistAPI {
private $base_url;
private $cookies = array();
private $api_key_option = 'yith_waitlist_api_key';
public function __construct() {
$this->base_url = home_url();
// Add REST API endpoints
add_action('rest_api_init', array($this, 'register_api_routes'));
// Add admin menu for testing
add_action('admin_menu', array($this, 'add_admin_menu'));
// Generate API key on activation
register_activation_hook(__FILE__, array($this, 'generate_api_key'));
// Add settings page
add_action('admin_init', array($this, 'register_settings'));
}
/**
* Generate API key on plugin activation
*/
public function generate_api_key() {
if (!get_option($this->api_key_option)) {
$api_key = $this->generate_secure_api_key();
update_option($this->api_key_option, $api_key);
}
}
/**
* Generate a secure API key
*/
private function generate_secure_api_key() {
return 'ywapi_' . bin2hex(random_bytes(32));
}
/**
* Register plugin settings
*/
public function register_settings() {
register_setting('yith_waitlist_api_settings', $this->api_key_option);
}
/**
* Register REST API routes
*/
public function register_api_routes() {
register_rest_route('yith-waitlist/v1', '/waitlist', array(
'methods' => 'GET',
'callback' => array($this, 'get_waitlist_data'),
'permission_callback' => array($this, 'check_permissions'),
'args' => array(
'search' => array(
'description' => 'Search term for products',
'type' => 'string',
'default' => ''
),
'orderby' => array(
'description' => 'Order by field',
'type' => 'string',
'default' => 'users'
),
'order' => array(
'description' => 'Order direction',
'type' => 'string',
'default' => 'desc'
),
'paged' => array(
'description' => 'Page number',
'type' => 'integer',
'default' => 1
)
)
));
register_rest_route('yith-waitlist/v1', '/waitlist/(?P<list_id>\d+)', array(
'methods' => 'GET',
'callback' => array($this, 'get_specific_waitlist'),
'permission_callback' => array($this, 'check_permissions'),
));
}
/**
* Check API permissions
*/
public function check_permissions($request) {
// Check for API key first
$api_key = $this->get_api_key_from_request($request);
if ($api_key && $this->validate_api_key($api_key)) {
return true;
}
// Fallback to WordPress user permissions
return current_user_can('manage_options');
}
/**
* Get API key from request
*/
private function get_api_key_from_request($request) {
// Check URL parameter
$api_key = $request->get_param('api_key');
if ($api_key) {
return $api_key;
}
// Check Authorization header
$headers = $request->get_headers();
if (isset($headers['authorization'][0])) {
$auth_header = $headers['authorization'][0];
if (preg_match('/Bearer\s+(.*)$/i', $auth_header, $matches)) {
return $matches[1];
}
}
// Check X-API-Key header
if (isset($headers['x_api_key'][0])) {
return $headers['x_api_key'][0];
}
return null;
}
/**
* Validate API key
*/
private function validate_api_key($provided_key) {
$stored_key = get_option($this->api_key_option);
return $stored_key && hash_equals($stored_key, $provided_key);
}
/**
* Get waitlist data via HTTP request
*/
public function get_waitlist_data($request) {
$search = $request->get_param('search');
$orderby = $request->get_param('orderby');
$order = $request->get_param('order');
$paged = $request->get_param('paged');
// Build the URL
$url = $this->base_url . '/wp-admin/admin.php';
$query_params = array(
'page' => 'yith_wcwtl_panel',
'tab' => 'waitlistdata',
'orderby' => $orderby,
'order' => $order,
'paged' => $paged
);
if (!empty($search)) {
$query_params['s'] = $search;
}
$url .= '?' . http_build_query($query_params);
// Make the request
$response = $this->make_authenticated_request($url);
if (is_wp_error($response)) {
return new WP_Error('request_failed', $response->get_error_message(), array('status' => 500));
}
// Parse the HTML response
$parsed_data = $this->parse_waitlist_html($response['body']);
return rest_ensure_response($parsed_data);
}
/**
* Get specific waitlist data
*/
public function get_specific_waitlist($request) {
$list_id = $request->get_param('list_id');
$url = $this->base_url . '/wp-admin/admin.php';
$query_params = array(
'page' => 'yith_wcwtl_panel',
'tab' => 'waitlistdata',
'list_id[]' => $list_id
);
$url .= '?' . http_build_query($query_params);
$response = $this->make_authenticated_request($url);
if (is_wp_error($response)) {
return new WP_Error('request_failed', $response->get_error_message(), array('status' => 500));
}
$parsed_data = $this->parse_waitlist_html($response['body']);
return rest_ensure_response($parsed_data);
}
/**
* Make authenticated HTTP request
*/
private function make_authenticated_request($url) {
// Get admin user for internal requests
$admin_user = $this->get_admin_user();
if (!$admin_user) {
return new WP_Error('no_admin_user', 'No admin user found for authentication');
}
// Set user context
wp_set_current_user($admin_user->ID);
// Get cookies for this admin user
$cookies = $this->get_admin_user_cookies($admin_user);
$args = array(
'timeout' => 30,
'cookies' => $cookies,
'headers' => array(
'User-Agent' => 'Mozilla/5.0 (WordPress YITH API)',
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language' => 'en-US,en;q=0.9',
'Cache-Control' => 'no-cache',
'DNT' => '1',
'Pragma' => 'no-cache',
'Sec-Fetch-Dest' => 'document',
'Sec-Fetch-Mode' => 'navigate',
'Sec-Fetch-Site' => 'same-origin',
'Upgrade-Insecure-Requests' => '1'
)
);
$response = wp_remote_get($url, $args);
if (is_wp_error($response)) {
return $response;
}
$status_code = wp_remote_retrieve_response_code($response);
if ($status_code !== 200) {
return new WP_Error('http_error', 'HTTP Error: ' . $status_code, array('status' => $status_code));
}
return array(
'body' => wp_remote_retrieve_body($response),
'headers' => wp_remote_retrieve_headers($response),
'status' => $status_code
);
}
/**
* Get an admin user for authentication
*/
private function get_admin_user() {
$admin_users = get_users(array(
'role' => 'administrator',
'number' => 1,
'orderby' => 'ID',
'order' => 'ASC'
));
return !empty($admin_users) ? $admin_users[0] : null;
}
/**
* Get authentication cookies for admin user
*/
private function get_admin_user_cookies($user) {
$cookies = array();
// Generate WordPress auth cookies
$expiration = time() + (14 * DAY_IN_SECONDS);
$auth_cookie = wp_generate_auth_cookie($user->ID, $expiration, 'logged_in');
$secure_cookie = wp_generate_auth_cookie($user->ID, $expiration, 'secure_auth');
$cookies[] = new WP_Http_Cookie(array(
'name' => LOGGED_IN_COOKIE,
'value' => $auth_cookie
));
if (is_ssl()) {
$cookies[] = new WP_Http_Cookie(array(
'name' => SECURE_AUTH_COOKIE,
'value' => $secure_cookie
));
}
return $cookies;
}
/**
* Parse HTML response to extract waitlist data
*/
private function parse_waitlist_html($html) {
$data = array(
'waitlists' => array(),
'total_items' => 0
);
// Use DOMDocument to parse HTML
$dom = new DOMDocument();
libxml_use_internal_errors(true); // Suppress HTML parsing warnings
@$dom->loadHTML('<?xml encoding="utf-8" ?>' . $html);
libxml_clear_errors();
$xpath = new DOMXPath($dom);
// Find waitlist table rows in tbody
$rows = $xpath->query('//tbody[@id="the-list"]//tr');
foreach ($rows as $row) {
$waitlist_item = array();
// Extract list ID from checkbox
$checkbox = $xpath->query('.//input[@type="checkbox"][@name="list_id[]"]', $row);
if ($checkbox->length > 0) {
$waitlist_item['list_id'] = $checkbox->item(0)->getAttribute('value');
}
// Extract product name and ID
$product_cell = $xpath->query('.//td[contains(@class, "column-product")]', $row);
if ($product_cell->length > 0) {
$product_link = $xpath->query('.//a[contains(@href, "post.php")]', $product_cell->item(0));
if ($product_link->length > 0) {
$waitlist_item['product'] = trim($product_link->item(0)->textContent);
// Extract product ID from edit URL
$edit_url = $product_link->item(0)->getAttribute('href');
if (preg_match('/post=(\d+)/', $edit_url, $matches)) {
$waitlist_item['product_id'] = intval($matches[1]);
}
}
}
// Extract variation
$variation_cell = $xpath->query('.//td[contains(@class, "column-variation")]', $row);
if ($variation_cell->length > 0) {
$waitlist_item['variation'] = trim($variation_cell->item(0)->textContent);
}
// Extract users count
$users_cell = $xpath->query('.//td[contains(@class, "column-users")]', $row);
if ($users_cell->length > 0) {
$users_link = $xpath->query('.//a', $users_cell->item(0));
if ($users_link->length > 0) {
$waitlist_item['users'] = intval(trim($users_link->item(0)->textContent));
}
}
// Only add if we have essential data
if (!empty($waitlist_item['product']) && isset($waitlist_item['users'])) {
$data['waitlists'][] = $waitlist_item;
}
}
// Extract total items count
$total_items_text = $xpath->query('//span[contains(@class, "displaying-num")]');
if ($total_items_text->length > 0) {
preg_match('/(\d+)/', $total_items_text->item(0)->textContent, $matches);
if (isset($matches[1])) {
$data['total_items'] = intval($matches[1]);
}
}
return $data;
}
/**
* Add admin menu for testing
*/
public function add_admin_menu() {
add_management_page(
'YITH Waitlist API',
'Waitlist API',
'manage_options',
'yith-waitlist-api',
array($this, 'admin_page')
);
}
/**
* Main admin page
*/
public function admin_page() {
$current_tab = $_GET['tab'] ?? 'settings';
?>
<div class="wrap">
<h1>YITH Waitlist API</h1>
<nav class="nav-tab-wrapper">
<a href="?page=yith-waitlist-api&tab=settings" class="nav-tab <?php echo $current_tab === 'settings' ? 'nav-tab-active' : ''; ?>">Settings</a>
<a href="?page=yith-waitlist-api&tab=test" class="nav-tab <?php echo $current_tab === 'test' ? 'nav-tab-active' : ''; ?>">Test API</a>
<a href="?page=yith-waitlist-api&tab=docs" class="nav-tab <?php echo $current_tab === 'docs' ? 'nav-tab-active' : ''; ?>">Documentation</a>
</nav>
<?php
switch ($current_tab) {
case 'settings':
$this->render_settings_tab();
break;
case 'test':
$this->render_test_tab();
break;
case 'docs':
$this->render_docs_tab();
break;
}
?>
</div>
<?php
}
/**
* Render settings tab
*/
private function render_settings_tab() {
$api_key = get_option($this->api_key_option);
if (isset($_POST['regenerate_key']) && wp_verify_nonce($_POST['_wpnonce'], 'regenerate_api_key')) {
$api_key = $this->generate_secure_api_key();
update_option($this->api_key_option, $api_key);
echo '<div class="notice notice-success"><p>API key regenerated successfully!</p></div>';
}
?>
<div class="card">
<h2>API Key Settings</h2>
<table class="form-table">
<tr>
<th scope="row">API Key</th>
<td>
<input type="text" value="<?php echo esc_attr($api_key); ?>" class="large-text code" readonly onclick="this.select()" />
<p class="description">Use this API key to authenticate external requests. Click to select and copy.</p>
</td>
</tr>
<tr>
<th scope="row">Regenerate Key</th>
<td>
<form method="post" style="display: inline;">
<?php wp_nonce_field('regenerate_api_key'); ?>
<input type="submit" name="regenerate_key" class="button button-secondary" value="Generate New API Key"
onclick="return confirm('This will invalidate the current API key. Are you sure?')" />
</form>
<p class="description">Generate a new API key. This will invalidate the current key.</p>
</td>
</tr>
</table>
</div>
<div class="card">
<h2>Security Notes</h2>
<ul>
<li>Keep your API key secure and don't share it publicly</li>
<li>The API key provides full admin access to your waitlist data</li>
<li>You can regenerate the key at any time if it's compromised</li>
<li>API requests are logged in your server access logs</li>
</ul>
</div>
<?php
}
/**
* Render test tab
*/
private function render_test_tab() {
?>
<div class="card">
<h2>Test API Call</h2>
<form method="post" action="">
<?php wp_nonce_field('test_api_call'); ?>
<table class="form-table">
<tr>
<th scope="row">Search Term</th>
<td><input type="text" name="search" value="<?php echo esc_attr($_POST['search'] ?? ''); ?>" class="regular-text" /></td>
</tr>
<tr>
<th scope="row">Order By</th>
<td>
<select name="orderby">
<option value="users" <?php selected($_POST['orderby'] ?? 'users', 'users'); ?>>Users</option>
<option value="date" <?php selected($_POST['orderby'] ?? 'users', 'date'); ?>>Date</option>
<option value="product" <?php selected($_POST['orderby'] ?? 'users', 'product'); ?>>Product</option>
</select>
</td>
</tr>
<tr>
<th scope="row">Order</th>
<td>
<select name="order">
<option value="desc" <?php selected($_POST['order'] ?? 'desc', 'desc'); ?>>Descending</option>
<option value="asc" <?php selected($_POST['order'] ?? 'desc', 'asc'); ?>>Ascending</option>
</select>
</td>
</tr>
</table>
<p class="submit">
<input type="submit" name="test_api" class="button-primary" value="Test API Call" />
</p>
</form>
<?php
if (isset($_POST['test_api']) && wp_verify_nonce($_POST['_wpnonce'], 'test_api_call')) {
$request = new WP_REST_Request('GET', '/yith-waitlist/v1/waitlist');
$request->set_param('search', $_POST['search'] ?? '');
$request->set_param('orderby', $_POST['orderby'] ?? 'users');
$request->set_param('order', $_POST['order'] ?? 'desc');
$response = $this->get_waitlist_data($request);
echo '<h3>API Response:</h3>';
echo '<pre style="background: #f1f1f1; padding: 15px; overflow: auto; max-height: 400px; border: 1px solid #ddd; border-radius: 4px;">';
echo esc_html(json_encode($response->get_data(), JSON_PRETTY_PRINT));
echo '</pre>';
}
?>
</div>
<?php
}
/**
* Render documentation tab
*/
private function render_docs_tab() {
$api_key = get_option($this->api_key_option);
$base_url = home_url('/wp-json/yith-waitlist/v1');
?>
<div class="card">
<h2>API Endpoints</h2>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th>Endpoint</th>
<th>Method</th>
<th>Description</th>
<th>Parameters</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>/waitlist</code></td>
<td>GET</td>
<td>Get all waitlists</td>
<td>search, orderby, order, paged</td>
</tr>
<tr>
<td><code>/waitlist/{id}</code></td>
<td>GET</td>
<td>Get specific waitlist</td>
<td>-</td>
</tr>
</tbody>
</table>
</div>
<div class="card">
<h2>Authentication Methods</h2>
<h3>Method 1: URL Parameter</h3>
<pre><code><?php echo esc_html($base_url); ?>/waitlist?api_key=<?php echo esc_html($api_key); ?></code></pre>
<h3>Method 2: Authorization Header</h3>
<pre><code>Authorization: Bearer <?php echo esc_html($api_key); ?></code></pre>
<h3>Method 3: X-API-Key Header</h3>
<pre><code>X-API-Key: <?php echo esc_html($api_key); ?></code></pre>
</div>
<div class="card">
<h2>Usage Examples</h2>
<h3>cURL</h3>
<pre><code># Using URL parameter
curl "<?php echo esc_html($base_url); ?>/waitlist?api_key=<?php echo esc_html($api_key); ?>"
# Using Authorization header
curl -H "Authorization: Bearer <?php echo esc_html($api_key); ?>" \
"<?php echo esc_html($base_url); ?>/waitlist"
# Search with parameters
curl -H "X-API-Key: <?php echo esc_html($api_key); ?>" \
"<?php echo esc_html($base_url); ?>/waitlist?search=STONE+BLOUSE&orderby=users&order=desc"</code></pre>
<h3>JavaScript/Fetch</h3>
<pre><code>// Using Authorization header
fetch('<?php echo esc_html($base_url); ?>/waitlist', {
headers: {
'Authorization': 'Bearer <?php echo esc_html($api_key); ?>'
}
})
.then(response => response.json())
.then(data => console.log(data));
// Using X-API-Key header
fetch('<?php echo esc_html($base_url); ?>/waitlist?search=STONE+BLOUSE', {
headers: {
'X-API-Key': '<?php echo esc_html($api_key); ?>'
}
})
.then(response => response.json())
.then(data => console.log(data.waitlists));</code></pre>
<h3>PHP</h3>
<pre><code>// Using WordPress HTTP API
$response = wp_remote_get('<?php echo esc_html($base_url); ?>/waitlist', array(
'headers' => array(
'X-API-Key' => '<?php echo esc_html($api_key); ?>'
)
));
$data = json_decode(wp_remote_retrieve_body($response), true);
print_r($data['waitlists']);</code></pre>
</div>
<div class="card">
<h2>Response Format</h2>
<pre><code>{
"waitlists": [
{
"id": "1674",
"product_name": "STONE BLOUSE BLAUW 277",
"users_count": 25,
"created_date": "2024-01-15",
"actions": [
{
"text": "View",
"url": "/wp-admin/admin.php?page=yith_wcwtl_panel&tab=waitlistdata&list_id=1674"
}
]
}
],
"pagination": [
{
"text": "1",
"url": "/wp-admin/admin.php?page=yith_wcwtl_panel&tab=waitlistdata&paged=1"
}
],
"total_items": 150
}</code></pre>
</div>
<style>
.nav-tab-wrapper {
margin-bottom: 20px;
}
.card {
margin-bottom: 20px;
}
.card h3 {
margin-top: 20px;
margin-bottom: 10px;
}
.card pre {
background: #f8f9fa;
border: 1px solid #e1e5e9;
border-radius: 6px;
padding: 16px;
overflow-x: auto;
font-size: 13px;
}
.card code {
background: #f1f3f4;
padding: 2px 4px;
border-radius: 3px;
font-family: 'Courier New', monospace;
}
.large-text.code {
font-family: 'Courier New', monospace;
font-size: 13px;
}
</style>
<?php
}
}
// Initialize the plugin
new YithWaitlistAPI();
/**
* Helper function to get waitlist data programmatically
*/
function get_yith_waitlist_data($args = array()) {
$api = new YithWaitlistAPI();
$request = new WP_REST_Request('GET', '/yith-waitlist/v1/waitlist');
foreach ($args as $key => $value) {
$request->set_param($key, $value);
}
$response = $api->get_waitlist_data($request);
if (is_wp_error($response)) {
return false;
}
return $response->get_data();
}
?>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment