120 lines
4.1 KiB
PHP
120 lines
4.1 KiB
PHP
<?php
|
|
|
|
namespace App\Services;
|
|
|
|
use App\Models\WooCommerceStore;
|
|
|
|
/**
|
|
* WooCommerceService
|
|
* Interacts with WooCommerce REST API of merchant stores securely.
|
|
*/
|
|
class WooCommerceService
|
|
{
|
|
/**
|
|
* Fetch order from WooCommerce by ID and verify customer phone
|
|
*/
|
|
public static function fetchOrder(array $store, int $orderId, ?string $customerPhone = null): ?array
|
|
{
|
|
$credentials = WooCommerceStore::getDecryptedCredentials($store);
|
|
$storeUrl = rtrim($credentials['store_url'], '/');
|
|
|
|
$url = $storeUrl . '/wp-json/wc/v3/orders/' . $orderId;
|
|
|
|
// Basic Authentication header
|
|
$auth = base64_encode($credentials['consumer_key'] . ':' . $credentials['consumer_secret']);
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'Authorization: Basic ' . $auth,
|
|
'Content-Type: application/json'
|
|
]);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Handle local dev self-signed certs gracefully
|
|
|
|
$res = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode !== 200) {
|
|
error_log("[WooCommerce API Error] Failed to fetch order $orderId from $storeUrl. HTTP: $httpCode, Response: $res");
|
|
return null;
|
|
}
|
|
|
|
$order = json_decode($res, true);
|
|
if (!$order) {
|
|
return null;
|
|
}
|
|
|
|
// Security check: Match customer phone if provided
|
|
if ($customerPhone) {
|
|
$orderPhone = $order['billing']['phone'] ?? $order['shipping']['phone'] ?? '';
|
|
if (!self::comparePhones($customerPhone, $orderPhone)) {
|
|
error_log("[WooCommerce Security Warning] Order $orderId phone ($orderPhone) does not match customer phone ($customerPhone)");
|
|
return ['unauthorized' => true];
|
|
}
|
|
}
|
|
|
|
return $order;
|
|
}
|
|
|
|
/**
|
|
* Fetch recent orders from WooCommerce
|
|
*/
|
|
public static function fetchRecentOrders(array $store, int $perPage = 30): ?array
|
|
{
|
|
$credentials = WooCommerceStore::getDecryptedCredentials($store);
|
|
$storeUrl = rtrim($credentials['store_url'], '/');
|
|
|
|
$url = $storeUrl . '/wp-json/wc/v3/orders?per_page=' . $perPage;
|
|
|
|
// Basic Authentication header
|
|
$auth = base64_encode($credentials['consumer_key'] . ':' . $credentials['consumer_secret']);
|
|
|
|
$ch = curl_init($url);
|
|
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
'Authorization: Basic ' . $auth,
|
|
'Content-Type: application/json'
|
|
]);
|
|
curl_setopt($ch, CURLOPT_TIMEOUT, 15);
|
|
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Handle local dev self-signed certs gracefully
|
|
|
|
$res = curl_exec($ch);
|
|
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
curl_close($ch);
|
|
|
|
if ($httpCode !== 200) {
|
|
error_log("[WooCommerce API Error] Failed to fetch orders from $storeUrl. HTTP: $httpCode, Response: $res");
|
|
return null;
|
|
}
|
|
|
|
return json_decode($res, true);
|
|
}
|
|
|
|
/**
|
|
* Compare two phone numbers by matching their trailing digits (ignoring country codes/symbols)
|
|
*/
|
|
public static function comparePhones(string $phone1, string $phone2): bool
|
|
{
|
|
$clean1 = preg_replace('/\D/', '', $phone1);
|
|
$clean2 = preg_replace('/\D/', '', $phone2);
|
|
|
|
if (empty($clean1) || empty($clean2)) {
|
|
return false;
|
|
}
|
|
|
|
// Compare trailing 9 digits (common standard for Saudi and international numbers)
|
|
$len1 = strlen($clean1);
|
|
$len2 = strlen($clean2);
|
|
$matchLen = min(9, $len1, $len2);
|
|
|
|
if ($matchLen < 6) {
|
|
// If phone numbers are very short, require exact match
|
|
return $clean1 === $clean2;
|
|
}
|
|
|
|
return substr($clean1, -$matchLen) === substr($clean2, -$matchLen);
|
|
}
|
|
}
|