Files
nabeh/backend/app/Controllers/WooCommerceController.php
2026-05-22 23:55:19 +03:00

272 lines
9.9 KiB
PHP

<?php
namespace App\Controllers;
use App\Core\Request;
use App\Core\Response;
use App\Models\WooCommerceStore;
use App\Models\WhatsAppSession;
class WooCommerceController extends BaseController
{
/**
* Connect WooCommerce store.
* Protected by AuthMiddleware.
* Accessible via POST /api/integrations/woocommerce/connect
*/
public function connect(Request $request, Response $response)
{
$body = $request->getBody();
$storeUrl = $body['store_url'] ?? '';
$consumerKey = $body['consumer_key'] ?? '';
$consumerSecret = $body['consumer_secret'] ?? '';
$webhookSecret = $body['webhook_secret'] ?? null;
if (empty($storeUrl) || empty($consumerKey) || empty($consumerSecret)) {
$response->status(400)->json([
'status' => 'error',
'message' => 'Missing store_url, consumer_key, or consumer_secret'
]);
return;
}
try {
$storeId = WooCommerceStore::saveStore(
$request->company_id,
$storeUrl,
$consumerKey,
$consumerSecret,
$webhookSecret
);
// Generate delivery URL for webhooks to display to the user
$appUrl = rtrim(getenv('APP_URL') ?: 'https://nabeh.intaleqapp.com', '/');
$webhookUrl = $appUrl . '/api/webhooks/woocommerce?company_id=' . $request->company_id;
$response->json([
'status' => 'success',
'message' => 'WooCommerce store connected successfully',
'webhook_url' => $webhookUrl
]);
} catch (\Exception $e) {
$response->status(500)->json([
'status' => 'error',
'message' => 'Connection failed: ' . $e->getMessage()
]);
}
}
/**
* Get WooCommerce connection status.
* Protected by AuthMiddleware.
* Accessible via GET /api/integrations/woocommerce/status
*/
public function status(Request $request, Response $response)
{
$store = WooCommerceStore::findByCompany($request->company_id);
if ($store) {
$appUrl = rtrim(getenv('APP_URL') ?: 'https://nabeh.intaleqapp.com', '/');
$webhookUrl = $appUrl . '/api/webhooks/woocommerce?company_id=' . $request->company_id;
$response->json([
'status' => 'success',
'connected' => true,
'store_url' => $store['store_url'],
'webhook_url' => $webhookUrl,
'has_webhook_secret' => !empty($store['webhook_secret'])
]);
} else {
$response->json([
'status' => 'success',
'connected' => false
]);
}
}
/**
* Disconnect WooCommerce integration.
* Protected by AuthMiddleware.
* Accessible via POST /api/integrations/woocommerce/disconnect
*/
public function disconnect(Request $request, Response $response)
{
WooCommerceStore::deleteByCompany($request->company_id);
$response->json([
'status' => 'success',
'message' => 'WooCommerce integration disconnected successfully'
]);
}
/**
* Handle incoming webhook from WooCommerce.
* Accessible via POST /api/webhooks/woocommerce?company_id=XYZ
*/
public function webhook(Request $request, Response $response)
{
$companyId = (int)($request->get('company_id') ?? 0);
if (empty($companyId)) {
$response->status(400)->json(['error' => 'Missing company_id query parameter']);
return;
}
$store = WooCommerceStore::findByCompany($companyId);
if (!$store) {
$response->status(404)->json(['error' => 'Store connection details not found for this tenant']);
return;
}
$rawPayload = file_get_contents('php://input');
// 1. Verify signature if webhook_secret is configured
if (!empty($store['webhook_secret'])) {
$signatureHeader = $request->getHeader('x-wc-webhook-signature') ?: '';
if (empty($signatureHeader)) {
$response->status(401)->json(['error' => 'Missing x-wc-webhook-signature header']);
return;
}
$calculated = base64_encode(hash_hmac('sha256', $rawPayload, $store['webhook_secret'], true));
if (!hash_equals($calculated, $signatureHeader)) {
error_log("[WooCommerce Webhook Error] Signature mismatch for company {$companyId}");
$response->status(401)->json(['error' => 'Signature verification failed']);
return;
}
}
// 2. Parse payload
$payload = json_decode($rawPayload, true);
if (!$payload) {
$response->status(400)->json(['error' => 'Invalid JSON payload']);
return;
}
$topic = $request->getHeader('x-wc-webhook-topic') ?: '';
if (empty($topic)) {
$response->status(400)->json(['error' => 'Missing x-wc-webhook-topic header']);
return;
}
// 3. Process events
if ($topic === 'order.created' || $topic === 'order.updated') {
$this->handleOrderWebhook($companyId, $topic, $payload);
}
$response->json(['status' => 'success']);
}
/**
* Process order webhook events and send automated WhatsApp message
*/
private function handleOrderWebhook(int $companyId, string $topic, array $order)
{
$orderId = $order['id'] ?? '';
$customerName = trim(($order['billing']['first_name'] ?? '') . ' ' . ($order['billing']['last_name'] ?? ''));
if (empty($customerName)) {
$customerName = 'عميلنا العزيز';
}
$customerPhone = $order['billing']['phone'] ?? $order['shipping']['phone'] ?? '';
$status = $order['status'] ?? '';
$total = $order['total'] ?? '';
$currency = $order['currency'] ?? 'USD';
if (empty($customerPhone) || empty($orderId)) {
return;
}
// Normalize phone number
$customerPhone = preg_replace('/\D/', '', $customerPhone);
if (substr($customerPhone, 0, 2) === '00') {
$customerPhone = substr($customerPhone, 2);
}
// Normalize Saudi numbers starting with 05
if (strlen($customerPhone) === 9 && $customerPhone[0] === '5') {
$customerPhone = '966' . $customerPhone;
} elseif (strlen($customerPhone) === 10 && substr($customerPhone, 0, 2) === '05') {
$customerPhone = '966' . substr($customerPhone, 1);
}
// Formulate Arabic message based on topic
$message = '';
if ($topic === 'order.created') {
$message = "مرحباً {$customerName}،\nتم استلام طلبك رقم ({$orderId}) بنجاح! 🎉\nإجمالي الفاتورة: {$total} {$currency}\nحالة الطلب الحالية: *قيد المراجعة*.\nشكرًا لتسوقك معنا!";
} elseif ($topic === 'order.updated') {
$translatedStatus = $this->translateStatus($status);
$message = "مرحباً {$customerName}،\nتم تحديث حالة طلبك رقم ({$orderId}) إلى: *{$translatedStatus}*.\n";
// If completed, add standard courier notice
if ($status === 'completed') {
$message .= "🚚 طلبك الآن في طريقه إليك! نتمنى لك تجربة ممتعة.";
}
}
if (empty($message)) {
return;
}
// Send WhatsApp Message via active session
$session = WhatsAppSession::findByCompany($companyId);
if (!$session || $session['status'] !== 'connected') {
error_log("[WooCommerce Webhook Warning] Cannot send auto-notification: No active connected WhatsApp session for company: " . $companyId);
return;
}
$this->sendWhatsAppNotification($session['session_key'], $customerPhone, $message);
}
/**
* Translate WooCommerce order status to Arabic
*/
private function translateStatus(string $status): string
{
$translations = [
'pending' => 'بانتظار الدفع',
'processing' => 'قيد التجهيز',
'on-hold' => 'قيد الانتظار',
'completed' => 'مكتمل',
'cancelled' => 'ملغي',
'refunded' => 'مسترجع',
'failed' => 'فشل الدفع',
'checkout-draft' => 'مسودة السلة'
];
return $translations[$status] ?? $status;
}
/**
* Send a WhatsApp message through the gateway
*/
private function sendWhatsAppNotification(string $sessionKey, string $phone, string $message)
{
$gatewayUrl = rtrim(getenv('WHATSAPP_GATEWAY_URL') ?: 'http://localhost:3722', '/');
if (substr($gatewayUrl, -4) === '/api') {
$sendUrl = $gatewayUrl . '/messages/send';
} else {
$sendUrl = $gatewayUrl . '/api/messages/send';
}
$payload = json_encode([
'session_key' => $sessionKey,
'phone' => $phone,
'message' => $message
]);
$ch = curl_init($sendUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json',
'X-Webhook-Secret: ' . getenv('WEBHOOK_SECRET')
]);
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode !== 200) {
error_log("[WooCommerce Webhook Gateway Error] Failed to send WhatsApp notification. Gateway response: " . $response);
}
}
}