|
|
@@ -0,0 +1,311 @@
|
|
|
+<?php
|
|
|
+
|
|
|
+declare(strict_types=1);
|
|
|
+
|
|
|
+namespace EKSRelay\Handlers;
|
|
|
+
|
|
|
+use EKSRelay\Clients\WooCommerceClient;
|
|
|
+use EKSRelay\Core\Auth;
|
|
|
+use EKSRelay\Core\Env;
|
|
|
+use EKSRelay\Core\HttpClient;
|
|
|
+use EKSRelay\Core\HttpException;
|
|
|
+use EKSRelay\Core\Logger;
|
|
|
+use EKSRelay\Core\Router;
|
|
|
+
|
|
|
+/**
|
|
|
+ * Handlers for all WooCommerce-backed tool endpoints.
|
|
|
+ */
|
|
|
+final class WooToolsHandler
|
|
|
+{
|
|
|
+ // =================================================================
|
|
|
+ // POST /tools/get_order_data
|
|
|
+ // =================================================================
|
|
|
+ public static function getOrderData(): void
|
|
|
+ {
|
|
|
+ Auth::requireBearer();
|
|
|
+ $body = Router::jsonBody();
|
|
|
+
|
|
|
+ $orderNumber = $body['orderNumber'] ?? ($body['order_number'] ?? null);
|
|
|
+ $email = $body['email'] ?? null;
|
|
|
+
|
|
|
+ if ($orderNumber !== null) {
|
|
|
+ $orderNumber = (string)$orderNumber;
|
|
|
+ }
|
|
|
+
|
|
|
+ $woo = new WooCommerceClient();
|
|
|
+
|
|
|
+ if ($orderNumber !== null && $orderNumber !== '') {
|
|
|
+ $order = $woo->getOrder(orderNumber: $orderNumber);
|
|
|
+ } elseif ($email !== null && $email !== '') {
|
|
|
+ $order = $woo->getOrder(email: $email);
|
|
|
+ } else {
|
|
|
+ throw new HttpException(400, 'MISSING_PARAMS', 'Provide orderNumber or email.');
|
|
|
+ }
|
|
|
+
|
|
|
+ // Return a curated subset useful for the LLM agent
|
|
|
+ Router::sendJson(200, [
|
|
|
+ 'ok' => true,
|
|
|
+ 'data' => self::formatOrder($order),
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // =================================================================
|
|
|
+ // POST /tools/get_product_data
|
|
|
+ // =================================================================
|
|
|
+ public static function getProductData(): void
|
|
|
+ {
|
|
|
+ Auth::requireBearer();
|
|
|
+ $body = Router::jsonBody();
|
|
|
+
|
|
|
+ $productId = isset($body['productId']) ? (int)$body['productId'] : (isset($body['product_id']) ? (int)$body['product_id'] : null);
|
|
|
+ $sku = $body['sku'] ?? null;
|
|
|
+ $search = $body['search'] ?? null;
|
|
|
+
|
|
|
+ $woo = new WooCommerceClient();
|
|
|
+
|
|
|
+ if ($productId !== null && $productId > 0) {
|
|
|
+ $product = $woo->getProduct(productId: $productId);
|
|
|
+ Router::sendJson(200, ['ok' => true, 'data' => self::formatProduct($product)]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($sku !== null && $sku !== '') {
|
|
|
+ $product = $woo->getProduct(sku: $sku);
|
|
|
+ Router::sendJson(200, ['ok' => true, 'data' => self::formatProduct($product)]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ($search !== null && $search !== '') {
|
|
|
+ $products = $woo->searchProducts($search);
|
|
|
+ $formatted = array_map(fn($p) => self::formatProduct($p), $products);
|
|
|
+ Router::sendJson(200, ['ok' => true, 'data' => $formatted]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ throw new HttpException(400, 'MISSING_PARAMS', 'Provide productId, sku, or search.');
|
|
|
+ }
|
|
|
+
|
|
|
+ // =================================================================
|
|
|
+ // POST /tools/get_shipping_data
|
|
|
+ // =================================================================
|
|
|
+ public static function getShippingData(): void
|
|
|
+ {
|
|
|
+ Auth::requireBearer();
|
|
|
+ $body = Router::jsonBody();
|
|
|
+
|
|
|
+ $woo = new WooCommerceClient();
|
|
|
+
|
|
|
+ // If zoneId provided — return methods for that specific zone
|
|
|
+ $zoneId = $body['zoneId'] ?? ($body['zone_id'] ?? null);
|
|
|
+
|
|
|
+ try {
|
|
|
+ if ($zoneId !== null) {
|
|
|
+ $methods = $woo->getShippingMethods((int)$zoneId);
|
|
|
+ Router::sendJson(200, ['ok' => true, 'data' => ['zone_id' => (int)$zoneId, 'methods' => $methods]]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Default: return list of zones (without methods to avoid 29+ API calls)
|
|
|
+ $zones = $woo->getShippingZones();
|
|
|
+ $formatted = array_map(fn($z) => [
|
|
|
+ 'id' => $z['id'] ?? 0,
|
|
|
+ 'name' => $z['name'] ?? '',
|
|
|
+ ], $zones);
|
|
|
+
|
|
|
+ Router::sendJson(200, [
|
|
|
+ 'ok' => true,
|
|
|
+ 'data' => $formatted,
|
|
|
+ 'hint' => 'Pass {"zoneId": <id>} to get shipping methods for a specific zone.',
|
|
|
+ ]);
|
|
|
+ } catch (HttpException $e) {
|
|
|
+ if ($e->httpCode === 502) {
|
|
|
+ Router::sendJson(200, [
|
|
|
+ 'ok' => false,
|
|
|
+ 'code' => 'NOT_IMPLEMENTED',
|
|
|
+ 'message' => 'Shipping data is not available via WooCommerce REST API. '
|
|
|
+ . 'Ensure shipping zones are configured and the consumer key has read access to shipping endpoints.',
|
|
|
+ ]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // =================================================================
|
|
|
+ // POST /tools/get_payment_methods
|
|
|
+ // =================================================================
|
|
|
+ public static function getPaymentMethods(): void
|
|
|
+ {
|
|
|
+ Auth::requireBearer();
|
|
|
+
|
|
|
+ $woo = new WooCommerceClient();
|
|
|
+
|
|
|
+ try {
|
|
|
+ $gateways = $woo->getPaymentGateways();
|
|
|
+ // Filter to enabled gateways only
|
|
|
+ $enabled = array_values(array_filter($gateways, fn($g) => ($g['enabled'] ?? false) === true));
|
|
|
+
|
|
|
+ $formatted = array_map(fn($g) => [
|
|
|
+ 'id' => $g['id'] ?? '',
|
|
|
+ 'title' => $g['title'] ?? '',
|
|
|
+ 'description' => $g['description'] ?? '',
|
|
|
+ 'enabled' => $g['enabled'] ?? false,
|
|
|
+ ], $enabled);
|
|
|
+
|
|
|
+ Router::sendJson(200, ['ok' => true, 'data' => $formatted]);
|
|
|
+ } catch (HttpException $e) {
|
|
|
+ if ($e->httpCode === 502) {
|
|
|
+ Router::sendJson(200, [
|
|
|
+ 'ok' => false,
|
|
|
+ 'code' => 'NOT_IMPLEMENTED',
|
|
|
+ 'message' => 'Payment gateways endpoint not accessible. '
|
|
|
+ . 'Ensure the WooCommerce consumer key has admin-level (read/write) permissions '
|
|
|
+ . 'to access GET /payment_gateways.',
|
|
|
+ ]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ throw $e;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // =================================================================
|
|
|
+ // POST /tools/get_product_compatibility
|
|
|
+ // =================================================================
|
|
|
+ public static function getProductCompatibility(): void
|
|
|
+ {
|
|
|
+ Auth::requireBearer();
|
|
|
+ $body = Router::jsonBody();
|
|
|
+
|
|
|
+ $ajaxUrl = Env::get('WP_AJAX_URL');
|
|
|
+ if ($ajaxUrl === '') {
|
|
|
+ Router::sendJson(200, [
|
|
|
+ 'ok' => false,
|
|
|
+ 'code' => 'NOT_CONFIGURED',
|
|
|
+ 'message' => 'WP_AJAX_URL is not set in .env.',
|
|
|
+ ]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $params = ['action' => 'chat_get_product_compatibility'];
|
|
|
+
|
|
|
+ $productName = $body['product_name'] ?? ($body['productName'] ?? null);
|
|
|
+ $carBrand = $body['car_brand'] ?? ($body['carBrand'] ?? null);
|
|
|
+ $carModel = $body['car_model'] ?? ($body['carModel'] ?? null);
|
|
|
+ $carYear = $body['car_year'] ?? ($body['carYear'] ?? null);
|
|
|
+ $carEngine = $body['car_engine'] ?? ($body['carEngine'] ?? null);
|
|
|
+
|
|
|
+ if ($productName !== null && $productName !== '') $params['product_name'] = $productName;
|
|
|
+ if ($carBrand !== null && $carBrand !== '') $params['car_brand'] = $carBrand;
|
|
|
+ if ($carModel !== null && $carModel !== '') $params['car_model'] = $carModel;
|
|
|
+ if ($carYear !== null && $carYear !== '') $params['car_year'] = $carYear;
|
|
|
+ if ($carEngine !== null && $carEngine !== '') $params['car_engine'] = $carEngine;
|
|
|
+
|
|
|
+ $url = $ajaxUrl . '?' . http_build_query($params);
|
|
|
+ $res = HttpClient::request('GET', $url);
|
|
|
+
|
|
|
+ Router::sendJson(200, ['ok' => true, 'data' => $res['json'] ?? $res['body']]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // =================================================================
|
|
|
+ // POST /tools/get_car_data
|
|
|
+ // =================================================================
|
|
|
+ public static function getCarData(): void
|
|
|
+ {
|
|
|
+ Auth::requireBearer();
|
|
|
+ $body = Router::jsonBody();
|
|
|
+
|
|
|
+ $ajaxUrl = Env::get('WP_AJAX_URL');
|
|
|
+ if ($ajaxUrl === '') {
|
|
|
+ Router::sendJson(200, [
|
|
|
+ 'ok' => false,
|
|
|
+ 'code' => 'NOT_CONFIGURED',
|
|
|
+ 'message' => 'WP_AJAX_URL is not set in .env.',
|
|
|
+ ]);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ $params = ['action' => 'chat_get_car_data'];
|
|
|
+
|
|
|
+ $carBrand = $body['car_brand'] ?? ($body['make'] ?? null);
|
|
|
+ $carModel = $body['car_model'] ?? ($body['model'] ?? null);
|
|
|
+ $carYear = $body['car_year'] ?? ($body['year'] ?? null);
|
|
|
+ $carEngine = $body['car_engine'] ?? ($body['engine'] ?? null);
|
|
|
+
|
|
|
+ if ($carBrand !== null && $carBrand !== '') $params['car_brand'] = $carBrand;
|
|
|
+ if ($carModel !== null && $carModel !== '') $params['car_model'] = $carModel;
|
|
|
+ if ($carYear !== null && $carYear !== '') $params['car_year'] = $carYear;
|
|
|
+ if ($carEngine !== null && $carEngine !== '') $params['car_engine'] = $carEngine;
|
|
|
+
|
|
|
+ $url = $ajaxUrl . '?' . http_build_query($params);
|
|
|
+ $res = HttpClient::request('GET', $url);
|
|
|
+
|
|
|
+ Router::sendJson(200, ['ok' => true, 'data' => $res['json'] ?? $res['body']]);
|
|
|
+ }
|
|
|
+
|
|
|
+ // =================================================================
|
|
|
+ // Formatters (curate data for LLM consumption)
|
|
|
+ // =================================================================
|
|
|
+
|
|
|
+ private static function formatOrder(array $order): array
|
|
|
+ {
|
|
|
+ return [
|
|
|
+ 'id' => $order['id'] ?? null,
|
|
|
+ 'number' => $order['number'] ?? null,
|
|
|
+ 'status' => $order['status'] ?? null,
|
|
|
+ 'date_created' => $order['date_created'] ?? null,
|
|
|
+ 'total' => $order['total'] ?? null,
|
|
|
+ 'currency' => $order['currency'] ?? null,
|
|
|
+ 'billing' => [
|
|
|
+ 'first_name' => $order['billing']['first_name'] ?? '',
|
|
|
+ 'last_name' => $order['billing']['last_name'] ?? '',
|
|
|
+ 'email' => $order['billing']['email'] ?? '',
|
|
|
+ 'phone' => $order['billing']['phone'] ?? '',
|
|
|
+ 'city' => $order['billing']['city'] ?? '',
|
|
|
+ 'country' => $order['billing']['country'] ?? '',
|
|
|
+ ],
|
|
|
+ 'shipping' => [
|
|
|
+ 'first_name' => $order['shipping']['first_name'] ?? '',
|
|
|
+ 'last_name' => $order['shipping']['last_name'] ?? '',
|
|
|
+ 'city' => $order['shipping']['city'] ?? '',
|
|
|
+ 'country' => $order['shipping']['country'] ?? '',
|
|
|
+ 'address_1' => $order['shipping']['address_1'] ?? '',
|
|
|
+ ],
|
|
|
+ 'payment_method' => $order['payment_method_title'] ?? null,
|
|
|
+ 'shipping_total' => $order['shipping_total'] ?? null,
|
|
|
+ 'line_items' => array_map(fn($item) => [
|
|
|
+ 'name' => $item['name'] ?? '',
|
|
|
+ 'sku' => $item['sku'] ?? '',
|
|
|
+ 'quantity' => $item['quantity'] ?? 0,
|
|
|
+ 'total' => $item['total'] ?? '0',
|
|
|
+ ], $order['line_items'] ?? []),
|
|
|
+ 'shipping_lines' => array_map(fn($sl) => [
|
|
|
+ 'method_title' => $sl['method_title'] ?? '',
|
|
|
+ 'total' => $sl['total'] ?? '0',
|
|
|
+ ], $order['shipping_lines'] ?? []),
|
|
|
+ 'customer_note' => $order['customer_note'] ?? '',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+
|
|
|
+ private static function formatProduct(array $product): array
|
|
|
+ {
|
|
|
+ return [
|
|
|
+ 'id' => $product['id'] ?? null,
|
|
|
+ 'name' => $product['name'] ?? '',
|
|
|
+ 'sku' => $product['sku'] ?? '',
|
|
|
+ 'slug' => $product['slug'] ?? '',
|
|
|
+ 'status' => $product['status'] ?? '',
|
|
|
+ 'price' => $product['price'] ?? '',
|
|
|
+ 'regular_price' => $product['regular_price'] ?? '',
|
|
|
+ 'sale_price' => $product['sale_price'] ?? '',
|
|
|
+ 'stock_status' => $product['stock_status'] ?? '',
|
|
|
+ 'stock_quantity' => $product['stock_quantity'] ?? null,
|
|
|
+ 'short_description' => strip_tags((string)($product['short_description'] ?? '')),
|
|
|
+ 'categories' => array_map(fn($c) => $c['name'] ?? '', $product['categories'] ?? []),
|
|
|
+ 'attributes' => array_map(fn($a) => [
|
|
|
+ 'name' => $a['name'] ?? '',
|
|
|
+ 'options' => $a['options'] ?? [],
|
|
|
+ ], $product['attributes'] ?? []),
|
|
|
+ 'permalink' => $product['permalink'] ?? '',
|
|
|
+ ];
|
|
|
+ }
|
|
|
+}
|