272 lines
9.9 KiB
PHP
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);
|
|
}
|
|
}
|
|
}
|