/**
* Plugin Name: PWC2SB Webhook + No-Key Watchdog
* Description: Palia Supabase feed + stock alerts. Installed 2026-09-05 via one-shot WPCode 90003. Canonical copy: E:/HTML outputs/palia-stage1/pwc2sb_webhook.php
* Version: 1.0.0
*/
if (!function_exists('pwc2sb_product_id')) {
/**
* PWC -> Supabase webhook: mirrors lmfwc license keys for the Palia product
* into Supabase `orders` (paliacheats.com buyer dashboard feed).
* Fires on order processing/completed; retry in 2 min if keys not attached yet.
* PLUS network-wide no-key watchdog: ANY paid order with 0 keys after a 2-min
* re-check emails PWC2SB_ALERT_EMAIL (pwc_orders_monitor.py, automated).
* Admin-only REST endpoint /wp-json/pwc2sb/v1/export for backfill + diagnostics:
* ?all=1&dry_run=1 preview backfill ?all=1 run backfill
* ?order_id=X export single order (no params = show log)
*/
if (!defined('ABSPATH')) exit;
if (!defined('PWC2SB_URL')) define('PWC2SB_URL', 'https://wspvdeqvxuhxgeezdjib.supabase.co');
if (!defined('PWC2SB_KEY')) define('PWC2SB_KEY', 'sb_secret_rYnrgyCufgh3-V9RqnNsWA_B-27G_jF');
if (!defined('PWC2SB_SITE')) define('PWC2SB_SITE', 'palia');
if (!defined('PWC2SB_SKU')) define('PWC2SB_SKU', 'fentry-palia');
// stock/no-key alerts go here (NOT admin_email - that stays for WP's own mail)
if (!defined('PWC2SB_ALERT_EMAIL')) define('PWC2SB_ALERT_EMAIL', 'lineage2crimson@gmail.com');
function pwc2sb_product_id() {
$p = get_page_by_path(PWC2SB_SKU, OBJECT, 'product');
return $p ? (int)$p->ID : 0;
}
function pwc2sb_license_table() {
global $wpdb;
foreach (['%fwc_licenses', '%licenses'] as $pat) {
$t = $wpdb->get_col($wpdb->prepare('SHOW TABLES LIKE %s', $pat));
if ($t) return $t[0];
}
return '';
}
function pwc2sb_log($msg) {
$log = get_option('pwc2sb_log', []);
array_unshift($log, ['t' => current_time('mysql'), 'm' => $msg]);
update_option('pwc2sb_log', array_slice($log, 0, 25), false);
}
/** @return array{0:array[],1:bool} rows + has-palia-item */
function pwc2sb_collect($order_id, &$info, $pidOverride = 0, $siteOverride = null) {
global $wpdb;
$order = wc_get_order($order_id);
if (!$order || !is_callable([$order, 'get_billing_email'])) { $info[] = "order $order_id not an order"; return [[], false]; }
$email = strtolower((string)$order->get_billing_email());
$pid = $pidOverride ? (int)$pidOverride : pwc2sb_product_id();
$name = '';
foreach ($order->get_items() as $item) {
if ((int)$item->get_product_id() === $pid) { $name = $item->get_name(); break; }
}
$tb = pwc2sb_license_table();
if (!$tb) { $info[] = 'license table not found'; return [[], false]; }
$lics = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM `{$tb}` WHERE order_id = %d AND product_id = %d", $order_id, $pid));
if ($name === '' && !$lics) { $info[] = "order $order_id: no palia item"; return [[], false]; }
if ($name === '') $name = get_the_title($pid); // migrated orders keep name only; product link lost
if ($name === '') { foreach ($order->get_items() as $item) { $name = $item->get_name(); break; } }
if ($email === '') { $info[] = "order $order_id: no billing email"; return [[], true]; }
$rows = []; $keyShapes = [];
foreach ($lics as $l) {
$key = isset($l->license_key) ? (string)$l->license_key : '';
if ($key === '') {
$info[] = "order $order_id: license id {$l->id} key empty - skipped";
continue;
}
// Defuse ciphertext (def... + long) -> decrypt via the plugin's own filter
if (strlen($key) > 100 && strpos($key, 'def') === 0) {
$dec = apply_filters('lmfwc_decrypt', $key);
if (!is_string($dec) || $dec === '' || $dec === $key) {
$info[] = "order $order_id: license id {$l->id} decrypt FAILED - skipped";
continue;
}
$keyShapes[] = 'decrypted len ' . strlen($dec) . ' [' . substr($dec, 0, 2) . '..' . substr($dec, -2) . ']';
$key = $dec;
}
$exp = null;
if (!empty($l->expires_at) && strpos($l->expires_at, '9999') === false) {
$exp = str_replace(' ', 'T', $l->expires_at);
}
$rows[] = [
'site' => $siteOverride ?: PWC2SB_SITE,
'pwc_order_id' => (int)$order_id,
'buyer_email' => $email,
'product_name' => $name,
'license_key' => $key,
'status' => 'active',
'expires_at' => $exp,
];
}
$info[] = "order $order_id (" . count($lics) . " licenses -> " . count($rows) . " rows)" . ($keyShapes ? ' | ' . implode(' ; ', $keyShapes) : '');
return [$rows, true];
}
/** @return array{0:int,1:int,2:string} pushed-count, http-code, message */
function pwc2sb_push(array $rows) {
if (!$rows) return [0, 0, 'nothing to push'];
$ok = 0; $code = 0; $err = [];
foreach (array_chunk($rows, 100) as $ch) {
$res = wp_remote_post(PWC2SB_URL . '/rest/v1/orders', [
'timeout' => 20,
'headers' => [
'apikey' => PWC2SB_KEY,
'Authorization' => 'Bearer ' . PWC2SB_KEY,
'Content-Type' => 'application/json',
'Prefer' => 'resolution=ignore-duplicates',
],
'body' => wp_json_encode($ch),
]);
if (is_wp_error($res)) { $err[] = $res->get_error_message(); continue; }
$code = (int)wp_remote_retrieve_response_code($res);
if ($code >= 200 && $code < 300) $ok += count($ch);
else $err[] = "HTTP $code: " . substr(wp_remote_retrieve_body($res), 0, 200);
}
return [$ok, $code, $err ? implode('; ', $err) : 'ok'];
}
function pwc2sb_handle_order($order_id, $isRetry = false) {
$info = [];
list($rows, $hasPalia) = pwc2sb_collect($order_id, $info);
if (!$hasPalia) return; // other games' orders: ignore silently
if (!$rows) {
wp_schedule_single_event(time() + 120, 'pwc2sb_retry', [(int)$order_id]);
if ($isRetry) {
wp_mail(PWC2SB_ALERT_EMAIL,
'[paliacheats] URGENT: paid palia order #' . $order_id . ' got NO key',
"Order #{$order_id} completed but lmfwc delivered NO license key - palia key stock is empty.\n" .
"The customer PAID but their email has no key.\n\n" .
"Fix: WP Admin > License Manager > Generators - generate keys for product 12241 (Fentry/palia),\n" .
"then edit order #{$order_id} and re-save status 'completed' so lmfwc delivers the key.");
pwc2sb_log("URGENT: order $order_id still 0 keys after retry - alert emailed");
} else {
pwc2sb_log("order $order_id: palia but 0 keys, retry scheduled - " . implode(' | ', $info));
}
return;
}
list($ok, $code, $msg) = pwc2sb_push($rows);
pwc2sb_log("order $order_id: pushed $ok rows (HTTP $code, $msg)");
// low-stock alert (unsold keys for palia), throttled to 1/24h; 0 = generator mode, no alert
global $wpdb;
if ($tb = pwc2sb_license_table()) {
$avail = (int)$wpdb->get_var($wpdb->prepare(
"SELECT COUNT(*) FROM `{$tb}` WHERE product_id = %d AND (order_id IS NULL OR order_id = 0)", pwc2sb_product_id()));
$last = (int)get_option('pwc2sb_lowstock_last', 0);
if ($avail > 0 && $avail < 5 && (time() - $last) > DAY_IN_SECONDS) {
wp_mail(PWC2SB_ALERT_EMAIL,
'[paliacheats] Palia key stock low: ' . $avail . ' left',
"Only {$avail} unsold Palia license keys remain on playwithcheats.com.\nRefill: WP Admin > License Manager > Licenses (import keys for product 12241).");
update_option('pwc2sb_lowstock_last', time());
pwc2sb_log("low stock alert sent ({$avail} left)");
}
}
}
add_action('woocommerce_order_status_processing', 'pwc2sb_handle_order', 200, 1);
add_action('woocommerce_order_status_completed', 'pwc2sb_handle_order', 200, 1);
add_action('pwc2sb_retry', function ($oid) { pwc2sb_handle_order($oid, true); }, 10, 1);
/**
* Network-wide no-key watchdog (automates pwc_orders_monitor.py's
* "PAID BUT NO KEY - SEND HIM KEY MANUALLY" flag for EVERY product).
* Any order in processing/completed with zero lmfwc keys attached:
* re-check after 2 min, then email PWC2SB_ALERT_EMAIL.
* Palia orders are skipped here - the palia handler above has its own alert.
*/
function pwc2sb_watchdog($order_id, $isRetry = false) {
$order = wc_get_order($order_id);
if (!$order || !is_callable([$order, 'get_billing_email'])) return;
if (!in_array($order->get_status(), ['processing', 'completed'], true)) return; // unpaid/refunded: key not owed
$pid = pwc2sb_product_id();
foreach ($order->get_items() as $it) {
if ((int)$it->get_product_id() === $pid) return; // palia: dedicated alert above
}
global $wpdb;
$tb = pwc2sb_license_table();
if (!$tb) return;
$n = (int)$wpdb->get_var($wpdb->prepare("SELECT COUNT(*) FROM `{$tb}` WHERE order_id = %d", $order_id));
if ($n > 0) return; // key(s) attached - all good
if (!$isRetry) {
wp_schedule_single_event(time() + 120, 'pwc2sb_wd_retry', [(int)$order_id]);
pwc2sb_log("watchdog: order $order_id paid but 0 keys - re-check in 2 min");
return;
}
$items = []; foreach ($order->get_items() as $it) $items[] = $it->get_name();
$email = $order->get_billing_email();
$total = $order->get_total() . ' ' . $order->get_currency();
wp_mail(PWC2SB_ALERT_EMAIL,
'[PWC] paid order #' . $order_id . ' has NO key - send manually',
"Order #{$order_id} ({$order->get_status()}) is PAID but has no license key attached.\n\n" .
"Customer: {$email}\n" .
"Product(s): " . implode('; ', $items) . "\n" .
"Total: {$total}\n\n" .
"NO KEY, SEND HIM KEY MANUALLY.\n" .
"Fix: WP Admin > License Manager > Licenses > assign a key to order #{$order_id} (or add stock for the product),\n" .
"then re-save the order as 'completed' so lmfwc emails the key and this stops re-alerting.");
pwc2sb_log("watchdog: URGENT order $order_id still 0 keys after retry - alert emailed");
}
add_action('woocommerce_order_status_processing', 'pwc2sb_watchdog', 210, 1);
add_action('woocommerce_order_status_completed', 'pwc2sb_watchdog', 210, 1);
add_action('pwc2sb_wd_retry', function ($oid) { pwc2sb_watchdog($oid, true); }, 10, 1);
add_action('rest_api_init', function () {
register_rest_route('pwc2sb/v1', '/export', [
'methods' => ['GET', 'POST'],
'permission_callback' => function () { return current_user_can('manage_woocommerce'); },
'callback' => function ($req) {
global $wpdb;
$tPid = (int)$req->get_param('test_product');
$tSite = $tPid ? 'test' : null;
$pid = $tPid ?: pwc2sb_product_id();
$tb = pwc2sb_license_table();
$out = ['site' => PWC2SB_SITE, 'product_id' => $pid, 'table' => $tb,
'columns' => $tb ? $wpdb->get_col("DESCRIBE `$tb`") : []];
$dry = !empty($req->get_param('dry_run'));
if ($req->get_param('debug')) {
if ($tb) {
$out['total_licenses'] = (int)$wpdb->get_var("SELECT COUNT(*) FROM `$tb`");
$out['top_products'] = $wpdb->get_results(
"SELECT product_id, COUNT(*) n, SUM(order_id>0) sold FROM `$tb` GROUP BY product_id ORDER BY n DESC LIMIT 15", ARRAY_A);
$r = $wpdb->get_row("SELECT * FROM `$tb` ORDER BY id DESC LIMIT 1");
if ($r) {
$r->license_key = (!empty($r->license_key)) ? 'non-empty len ' . strlen($r->license_key) : 'EMPTY';
$r->hash = (!empty($r->hash)) ? 'set' : '-';
$out['newest_license'] = $r;
}
// encryption + palia-variation diagnostics
$out['lmfwc_const'] = [
'LMFWC_SECRET_KEY' => defined('LMFWC_SECRET_KEY'),
'LMFWC_SECRET_IV' => defined('LMFWC_SECRET_IV'),
];
$raw = $wpdb->get_var("SELECT license_key FROM `$tb` WHERE order_id > 0 ORDER BY id DESC LIMIT 1");
if ($raw !== null && $raw !== '') {
$out['key_shape'] = [
'len' => strlen($raw),
'b64strict' => (base64_decode($raw, true) !== false),
'b64ish' => (bool)preg_match('/^[A-Za-z0-9+\/=]+$/', $raw),
'head' => substr($raw, 0, 4),
'tail' => substr($raw, -4),
];
}
$p = $pid ? wc_get_product($pid) : false;
if ($p && $p->is_type('variable') && ($kids = $p->get_children())) {
$out['variations'] = $kids;
$in = implode(',', array_map('intval', $kids));
$out['variation_licenses'] = $wpdb->get_results(
"SELECT product_id, COUNT(*) n, SUM(order_id>0) sold FROM `$tb` WHERE product_id IN ($in) GROUP BY product_id", ARRAY_A);
}
}
$out['lmfwc_options'] = $wpdb->get_col(
"SELECT option_name FROM {$wpdb->options} WHERE option_name LIKE '%lmfwc%' LIMIT 40");
$plugDir = ABSPATH . 'wp-content/plugins/license-manager-for-woocommerce';
if (is_dir($plugDir)) {
$cs = @file_get_contents($plugDir . '/includes/Crypto.php');
if ($cs !== false) $out['crypto_src'] = substr($cs, 0, 4000);
}
if ($oid2 = (int)$req->get_param('order_id')) {
$o = wc_get_order($oid2);
if ($o) {
$out['order'] = $oid2;
$out['order_status'] = $o->get_status();
$out['order_email_set'] = $o->get_billing_email() ? true : false;
$out['items'] = [];
foreach ($o->get_items() as $it) {
$out['items'][] = $it->get_product_id() . '/' . $it->get_variation_id() . ' ' . substr($it->get_name(), 0, 40);
}
}
}
return rest_ensure_response($out);
}
if ($req->get_param('all')) {
$ids = $tb ? $wpdb->get_col($wpdb->prepare(
"SELECT DISTINCT order_id FROM `{$tb}` WHERE product_id = %d AND order_id > 0 ORDER BY order_id DESC",
$pid)) : [];
$out['orders'] = count($ids);
if ($tb) {
$out['license_status_counts'] = $wpdb->get_results(
"SELECT status, COUNT(*) n FROM `$tb` WHERE product_id=" . intval($pid) . " GROUP BY status", ARRAY_A);
}
$all = []; $info = []; $out['sample'] = [];
foreach ($ids as $oid) {
list($rows, $hasPalia) = pwc2sb_collect((int)$oid, $info, $tPid, $tSite);
foreach ($rows as $r) {
$all[] = $r;
if (count($out['sample']) < 3) $out['sample'][] = array_diff_key($r, ['license_key' => 1]);
}
}
$out['rows'] = count($all);
$out['info'] = array_slice($info, 0, 10);
if (!$dry) {
list($ok, $code, $msg) = pwc2sb_push($all);
$out += ['pushed' => $ok, 'http' => $code, 'msg' => $msg];
pwc2sb_log("backfill: {$out['orders']} orders, pushed $ok rows ($msg)");
} else {
$out['dry_run'] = true;
}
} elseif ($oid = (int)$req->get_param('order_id')) {
$info = [];
list($rows, $hasPalia) = pwc2sb_collect($oid, $info, $tPid, $tSite);
$out['rows'] = count($rows);
$out['info'] = $info;
if ($req->get_param('debug')) {
$o = wc_get_order($oid);
$out['items'] = [];
if ($o) foreach ($o->get_items() as $it) {
$out['items'][] = $it->get_product_id() . '/' . $it->get_variation_id() . ' ' . substr($it->get_name(), 0, 40);
}
$out['order_status'] = $o ? $o->get_status() : '?';
$out['order_email_set'] = $o && method_exists($o, 'get_billing_email') ? ($o->get_billing_email() ? true : false) : 'n/a';
}
$out['sample'] = [];
foreach (array_slice($rows, 0, 3) as $r) $out['sample'][] = array_diff_key($r, ['license_key' => 1]);
if (!$dry && $rows) {
list($ok, $code, $msg) = pwc2sb_push($rows);
$out += ['pushed' => $ok, 'http' => $code, 'msg' => $msg];
}
} else {
$out['hint'] = 'params: all=1 [&dry_run=1] | order_id=X [&dry_run=1]';
$out['log'] = get_option('pwc2sb_log', []);
}
return rest_ensure_response($out);
},
]);
});
}
Escape the Backrooms - Play With Cheats - Best Undetected Cheats
has been added to your cart.
Checkout