$duration, 'path' => '/', 'secure' => isset($_SERVER['HTTPS']), 'httponly' => true, 'samesite' => 'Lax' ]); if (session_status() === PHP_SESSION_NONE) { session_start(); } // 2. RECOVERY LOGIC (Check cookie before WP loads) if (!isset($_SESSION['user']) && isset($_COOKIE['telvero_remember'])) { $decoded = json_decode(base64_decode($_COOKIE['telvero_remember']), true); if ($decoded && $decoded['expires'] > time()) { $_SESSION['user'] = $decoded['user']; $_SESSION['full_name'] = $decoded['full_name']; } } // 3. CAPTURE DATA FOR WP PROTECTION $cap_user = $_SESSION['user'] ?? null; $cap_name = $_SESSION['full_name'] ?? null; // 4. LOAD WORDPRESS $wp_load = __DIR__ . '/wp-load.php'; if (!file_exists($wp_load)) { $wp_load = dirname(__DIR__) . '/wp-load.php'; } if (file_exists($wp_load)) { require_once $wp_load; } // 5. RESTORE DATA if ($cap_user && !isset($_SESSION['user'])) { $_SESSION['user'] = $cap_user; $_SESSION['full_name'] = $cap_name; } // 6. REST OF THE BOOTSTRAP require __DIR__ . '/vendor/autoload.php'; if (file_exists(__DIR__ . '/.env')) { $dotenv = Dotenv\Dotenv::createImmutable(__DIR__); $dotenv->load(); } use Automattic\WooCommerce\Client; header('Content-Type: application/json'); $db = new mysqli($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASS'], $_ENV['DB_NAME']); if ($db->connect_error) { die(json_encode(['error' => 'Database connectie mislukt'])); } $action = $_GET['action'] ?? ''; // 6. LOGIN ACTION if ($action === 'login') { $input = json_decode(file_get_contents('php://input'), true); $username = $input['username'] ?? ''; $stmt = $db->prepare("SELECT password, full_name FROM sales_users WHERE username = ?"); $stmt->bind_param("s", $username); $stmt->execute(); $res = $stmt->get_result()->fetch_assoc(); if ($res && password_verify($input['password'], $res['password'])) { $_SESSION['user'] = $username; $_SESSION['full_name'] = $res['full_name']; // Recovery cookie payload $cookie_payload = base64_encode(json_encode([ 'user' => $username, 'full_name' => $res['full_name'], 'expires' => $midnight_timestamp ])); // Use the explicit $midnight_timestamp variable setcookie('telvero_remember', $cookie_payload, $midnight_timestamp, '/', '', isset($_SERVER['HTTPS']), true); echo json_encode(['success' => true, 'user' => $res['full_name']]); } else { http_response_code(401); echo json_encode(['error' => 'Login mislukt']); } exit; } // 7. SECURITY GATE (The ONLY one needed) if (!isset($_SESSION['user'])) { http_response_code(403); echo json_encode(['error' => 'Not authenticated']); exit; } // 8. LOGOUT if ($action === 'logout') { session_destroy(); setcookie('telvero_remember', '', time() - 3600, '/'); echo json_encode(['success' => true]); exit; } $woocommerce = new Client($_ENV['WC_URL'], $_ENV['WC_KEY'], $_ENV['WC_SECRET'], ['version' => 'wc/v3', 'verify_ssl' => false]); function ss_get_upsellwp_recommended_product_ids($trigger_product_id) { if (!function_exists('get_option')) { return []; } global $wpdb; $trigger_product_id = (int) $trigger_product_id; if ($trigger_product_id <= 0) return []; // 1) Vind "campaign posts" waar in meta_value ergens dit product_id voorkomt. // We weten niet exact welke meta_key UpsellWP gebruikt, dus zoeken we breed op meta_key LIKE '%upsellwp%'. // Daarnaast zoeken we op bekende patronen in serialized/json/plain. $like_json = '%' . $wpdb->esc_like('"' . $trigger_product_id . '"') . '%'; $like_plain = '%' . $wpdb->esc_like((string) $trigger_product_id) . '%'; $like_serial = '%' . $wpdb->esc_like('i:' . $trigger_product_id . ';') . '%'; $campaign_post_ids = $wpdb->get_col( $wpdb->prepare( " SELECT DISTINCT post_id FROM {$wpdb->postmeta} WHERE meta_key LIKE %s AND ( meta_value LIKE %s OR meta_value LIKE %s OR meta_value LIKE %s ) ", '%upsellwp%', $like_json, $like_serial, $like_plain ) ); if (empty($campaign_post_ids)) { return []; } // 2) Verzamel uit die campaigns alle product IDs die genoemd worden (offers / bundles / buy-more-save-more tiers). $recommended = []; foreach ($campaign_post_ids as $cid) { $cid = (int) $cid; $rows = $wpdb->get_results( $wpdb->prepare( " SELECT meta_key, meta_value FROM {$wpdb->postmeta} WHERE post_id = %d ", $cid ) ); foreach ($rows as $r) { $key = (string) $r->meta_key; // Kleine bias naar keys die logisch product/offers bevatten // maar alsnog niet te streng, want add-ons gebruiken soms andere keys. if (!preg_match('/product|products|offer|offers|bundle|bmsm|tier|rule|condition|trigger|cart|recommend/i', $key) && stripos($key, 'upsellwp') === false ) { continue; } $val = (string) $r->meta_value; // Extract alle integers uit json/serialized/plain if (preg_match_all('/\b\d+\b/', $val, $m)) { foreach ($m[0] as $id) { $id = (int) $id; if ($id > 0) $recommended[] = $id; } } } } // 3) Opschonen: unieke IDs, trigger zelf eruit, en alleen echte WC producten (best effort). $recommended = array_values(array_unique(array_filter($recommended))); $recommended = array_values(array_diff($recommended, [$trigger_product_id])); // Filter op bestaande producten (scheelt false positives zoals campaign IDs) $recommended = array_values(array_filter($recommended, function($id){ return get_post_type($id) === 'product' || get_post_type($id) === 'product_variation'; })); return $recommended; } /** * Build a map: product_id => related product ids from CUW (UpsellWP) campaigns. * Uses {$wpdb->prefix}cuw_campaigns table (as in your screenshot). */ function ss_cuw_build_product_map() { if (!function_exists('get_post_type')) return []; global $wpdb; $table = $wpdb->prefix . 'cuw_campaigns'; // Safety: table might not exist $exists = $wpdb->get_var( $wpdb->prepare("SHOW TABLES LIKE %s", $table) ); if ($exists !== $table) return []; $rows = $wpdb->get_results(" SELECT id, type, filters, data FROM {$table} WHERE enabled = 1 ORDER BY priority DESC, id DESC "); if (empty($rows)) return []; $map = []; foreach ($rows as $row) { $filters = json_decode((string) $row->filters, true); if (!is_array($filters)) continue; // Extract product lists from filters: // Example (your screenshot): // {"relation":"or","176...":{"type":"products","method":"in_list","values":["1831"]}} $filterProductLists = []; foreach ($filters as $k => $v) { if ($k === 'relation') continue; if (!is_array($v)) continue; $type = $v['type'] ?? ''; $method = $v['method'] ?? ''; $values = $v['values'] ?? []; if ($type !== 'products') continue; if (!in_array($method, ['in_list', 'in', 'include', 'equals'], true)) continue; if (!is_array($values)) continue; $ids = array_values(array_unique(array_map('intval', $values))); $ids = array_values(array_filter($ids)); if (!empty($ids)) { $filterProductLists[] = $ids; } } if (empty($filterProductLists)) continue; // For each list: if a product is in that list, relate it to the other products in that list foreach ($filterProductLists as $list) { foreach ($list as $pid) { $related = array_values(array_diff($list, [$pid])); if (empty($related)) continue; // Keep only real Woo products $related = array_values(array_filter($related, function($id){ $pt = get_post_type($id); return $pt === 'product' || $pt === 'product_variation'; })); if (empty($related)) continue; if (!isset($map[$pid])) $map[$pid] = []; $map[$pid] = array_values(array_unique(array_merge($map[$pid], $related))); } } } return $map; } function ss_cuw_build_deals_map() { global $wpdb; $table = $wpdb->prefix . 'cuw_campaigns'; $exists = $wpdb->get_var( $wpdb->prepare("SHOW TABLES LIKE %s", $table) ); if ($exists !== $table) return []; $rows = $wpdb->get_results(" SELECT id, type, title, priority, filters, data FROM {$table} WHERE enabled = 1 ORDER BY priority DESC, id DESC "); if (empty($rows)) return []; $map = []; foreach ($rows as $row) { $type = (string) $row->type; if ($type !== 'buy_more_save_more') { continue; } $filters = json_decode((string) $row->filters, true); $data = json_decode((string) $row->data, true); if (!is_array($filters) || !is_array($data)) continue; // 1) Product IDs uit filters halen (zoals jouw screenshot: type=products, method=in_list, values=[...]) $product_ids = []; foreach ($filters as $k => $v) { if ($k === 'relation') continue; if (!is_array($v)) continue; $f_type = $v['type'] ?? ''; $f_method = $v['method'] ?? ''; $values = $v['values'] ?? []; if ($f_type !== 'products') continue; if (!in_array($f_method, ['in_list', 'in', 'include', 'equals'], true)) continue; if (!is_array($values)) continue; foreach ($values as $pid) { $pid = (int) $pid; if ($pid > 0) $product_ids[] = $pid; } } $product_ids = array_values(array_unique(array_filter($product_ids))); if (empty($product_ids)) continue; // 2) Deal details uit data halen $cta_text = $data['template']['cta_text'] ?? ''; $display_location = $data['display_location'] ?? ($data['template']['display_location'] ?? ''); $discounts = $data['discounts'] ?? []; if (!is_array($discounts) || empty($discounts)) continue; foreach ($discounts as $disc) { if (!is_array($disc)) continue; $qty = isset($disc['qty']) ? (int) $disc['qty'] : 0; $dType = isset($disc['type']) ? (string) $disc['type'] : ''; $value = isset($disc['value']) ? (float) $disc['value'] : 0.0; if ($qty <= 0 || $dType === '' || $value <= 0) continue; $deal = [ 'campaign_id' => (int) $row->id, 'campaign_type' => $type, 'title' => (string) $row->title, 'priority' => (int) $row->priority, 'qty' => $qty, 'discount_type' => $dType, // fixed_price 'value' => $value, // 10 (korting op 2e in jouw geval) 'cta_text' => (string) $cta_text, 'display_location' => (string) $display_location, ]; foreach ($product_ids as $pid) { if (!isset($map[$pid])) $map[$pid] = []; $map[$pid][] = $deal; } } } return $map; } // --- POSTCODE CHECK --- if ($action === 'postcode_check') { $postcode = str_replace(' ', '', $_GET['postcode']); $url = "https://postcode.tech/api/v1/postcode?postcode={$postcode}&number=" . $_GET['number']; $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_HTTPHEADER, ["Authorization: Bearer " . $_ENV['POSTCODE_TECH_KEY']]); echo curl_exec($ch); exit; } // --- GET PRODUCTS --- // --- GET PRODUCTS --- if ($action === 'get_products') { try { $products = $woocommerce->get('products', ['status' => 'publish', 'per_page' => 100]); $cuw_map = ss_cuw_build_product_map(); $enriched = []; foreach ($products as $product) { // variations (bestaand) $variation_details = ($product->type === 'variable') ? (array) $woocommerce->get("products/{$product->id}/variations", ['per_page' => 50]) : []; // wpupsell IDs uit meta_data halen (heuristisch, werkt bij veel plugins) $wpupsell_ids = []; if (!empty($product->meta_data) && is_array($product->meta_data)) { foreach ($product->meta_data as $md) { $key = isset($md->key) ? (string) $md->key : ''; if ($key === '' || !preg_match('/wp\s*upsell|wpupsell|upsell\s*campaign/i', $key)) { continue; } $val = $md->value ?? null; // value kan array/object/string zijn if (is_array($val)) { $flat = json_encode($val); if (preg_match_all('/\b\d+\b/', $flat, $m)) { foreach ($m[0] as $id) $wpupsell_ids[] = (int) $id; } } elseif (is_object($val)) { $flat = json_encode($val); if (preg_match_all('/\b\d+\b/', $flat, $m)) { foreach ($m[0] as $id) $wpupsell_ids[] = (int) $id; } } else { $flat = (string) $val; if (preg_match_all('/\b\d+\b/', $flat, $m)) { foreach ($m[0] as $id) $wpupsell_ids[] = (int) $id; } } } } // upsell + cross-sell + wpupsell bundelen $upsell_ids = !empty($product->upsell_ids) ? array_map('intval', (array) $product->upsell_ids) : []; $cross_sell_ids = !empty($product->cross_sell_ids) ? array_map('intval', (array) $product->cross_sell_ids) : []; $cuw_ids = $cuw_map[(int)$product->id] ?? []; $recommended_ids = array_values(array_unique(array_filter(array_merge( $upsell_ids, $cross_sell_ids, $cuw_ids )))); // product naar array + velden toevoegen $p = (array) $product; $p['variation_details'] = $variation_details; // expliciet meesturen voor frontend $p['cuw_ids'] = $cuw_ids; $p['recommended_ids'] = $recommended_ids; $enriched[] = $p; } echo json_encode($enriched); } catch (Exception $e) { echo json_encode([]); } exit; } // --- CREATE ORDER --- if ($action === 'create_order') { $input = json_decode(file_get_contents('php://input'), true); try { $email = $input['billing']['email']; $mediacode = $input['mediacode_internal'] ?? 'Geen'; $input['payment_method'] = 'cod'; $input['payment_method_title'] = 'Sales Panel Order'; $input['status'] = 'on-hold'; $existing = $woocommerce->get('customers', ['email' => $email]); $input['customer_id'] = !empty($existing) ? $existing[0]->id : 0; $shipping_incl = (float)($input['shipping_total'] ?? 0); if ($shipping_incl > 0) { $input['shipping_lines'] = [[ 'method_id' => 'flat_rate', 'method_title' => 'Verzendkosten', 'total' => number_format($shipping_incl / 1.21, 4, '.', '') ]]; } $input['customer_note'] = "Agent: {$_SESSION['user']} | Mediacode: $mediacode"; // --- ATTRIBUTION DATA (GEFIXTE SYNTAX) --- $input['meta_data'][] = ['key' => '_wc_order_attribution_source_type', 'value' => 'utm']; $input['meta_data'][] = ['key' => '_wc_order_attribution_utm_source', 'value' => 'SalesPanel']; $input['meta_data'][] = ['key' => '_wc_order_attribution_utm_campaign', 'value' => $mediacode]; $input['meta_data'][] = ['key' => 'Mediacode', 'value' => $mediacode]; $input['meta_data'][] = ['key' => 'Bron', 'value' => 'SalesPanel']; $order = $woocommerce->post('orders', $input); $action_type = 'order_created'; $log_stmt = $db->prepare("INSERT INTO sales_logs (username, action_type, order_id, amount, mediacode, customer_email, created_at) VALUES (?, ?, ?, ?, ?, ?, NOW())"); $log_stmt->bind_param("ssidss", $_SESSION['user'], $action_type, $order->id, $order->total, $mediacode, $email); $log_stmt->execute(); echo json_encode(['success' => true, 'order_id' => $order->id, 'total' => $order->total]); } catch (Exception $e) { http_response_code(422); echo json_encode(['error' => $e->getMessage()]); } exit; } if ($action === 'logout') { session_destroy(); echo json_encode(['success' => true]); exit; }