דמיינו שחנות המכירות המקוונת שלכם מוכרת 10,000 מוצרים, והספק משנה מחירים פעמיים ביום. העלאה ידנית של Excel אורכת שעות ומובילה למחירים מיושנים ושגיאות במלאי. אינטגרציה ישירה דרך API של הספק פותרת את זה — הנתונים מתעדכנים אוטומטית ללא מעורבות אנושית. במשך למעלה מ-10 שנים, יישמנו יותר מ-50 אינטגרציות כאלה, מקישורי REST פשוטים ועד מערכות מרובות ספקים עם OAuth 2.0 ו-SOAP. הניסיון שלנו מאשר שארכיטקטורה נכונה מבטיחה יציבות ודיוק נתונים. לדוגמה, עבור חנות חלקי רכב עם 500,000 פריטים (SKU), הקמנו סנכרון מצטבר עם 5 ספקים, שלכל אחד מהם API משלו. התוצאה: מחירים ומלאי מדויקים עם עיכוב של לא יותר מ-15 דקות.
האתגר הוא ש-API של ספקים משתנים מאוד. פורמטי אימות, מבני תגובה, מודלי פג'ינציה (עימוד) — כולם ייחודיים. ללא ארכיטקטורה נכונה, אינטגרציה הופכת לכאוס. אנו משתמשים בתבניות מוכחות שמפשטות הוספת ספקים חדשים ומבטיחות יציבות.
כיצד לבחור את סוג ה-API עבור ספק?
| סוג | דוגמה | מאפיינים |
|---|---|---|
| REST JSON | רוב המערכות המודרניות | פג'ינציה מסוג Cursor/Offset, JWT/API-key |
| REST XML | מערכות Legacy (1C) | נדרש מפענח תגובות XML |
| SOAP | מערכות ERP ארגוניות | WSDL, SOAPClient |
| GraphQL | נדיר בקרב ספקים | בחירה גמישה של שדות |
| oData | SAP, Microsoft | $filter, $top, $skip |
קביעת הסוג היא הצעד הראשון. אנחנו תמיד מתחילים בניתוח התיעוד של הספק: אם קיימת מפרט REST API, חצי מהעבודה כבר בוצעה.
לקוח בסיסי עם ניסיונות חוזרים (Retry) והגבלת קצב (Rate Limiting)
class SupplierApiClient {
private \GuzzleHttp\Client $http;
private RateLimiter $rateLimiter;
public function __construct(
private SupplierApiConfig $config,
) {
$this->http = new \GuzzleHttp\Client([
'base_uri' => $config->baseUrl,
'timeout' => 30,
'handler' => $this->buildHandlerStack(),
]);
}
private function buildHandlerStack(): \GuzzleHttp\HandlerStack
{
$stack = \GuzzleHttp\HandlerStack::create();
$stack->push(\GuzzleHttp\Middleware::retry(
function (int $retries, $request, $response, $exception) {
if ($retries >= 3) return false;
if ($exception instanceof \GuzzleHttp\Exception\ConnectException) return true;
if ($response && $response->getStatusCode() >= 500) return true;
return false;
},
fn(int $retries) => 1000 * (2 ** $retries)
));
return $stack;
}
public function get(string $path, array $params = []): array
{
$this->rateLimiter->throttle($this->config->id, $this->config->rateLimit);
$response = $this->http->get($path, [
'query' => $params,
'headers' => $this->buildHeaders(),
]);
return json_decode($response->getBody(), true);
}
private function buildHeaders(): array
{
return match ($this->config->authType) {
'bearer' => ['Authorization' => 'Bearer ' . $this->config->token],
'api_key' => ['X-API-Key' => $this->config->apiKey],
'basic' => ['Authorization' => 'Basic ' . base64_encode(
$this->config->login . ':' . $this->config->password
)],
default => [],
};
}
}השהיה אקספוננציאלית (1 שנייה, 2 שניות, 4 שניות) מפחיתה את העומס על שרת הספק ומגבירה את סיכויי ההצלחה בזמן כשלים זמניים. הגבלת קצב מונעת חסימה עקב חריגה ממגבלות הבקשות.
מדוע נורמליזציה של נתונים חשובה?
לכל ספק יש שדה JSON משלו לשם, מחיר, SKU. ללא נורמליזציה, הקוד הופך למבולגן — בדיקות וחילוצים בכל מקום. אנו משתמשים ב-fieldMap עם סימון נקודה (dot-notation), המאוחסן במסד הנתונים כ-JSON. הוספת ספק חדש היא רק רשומה בטבלה, ללא שינויי קוד.
class SupplierResponseNormalizer {
private array $fieldMap;
public function normalize(array $raw): array
{
return [
'sku' => $this->extract($raw, $this->fieldMap['sku']),
'name' => $this->extract($raw, $this->fieldMap['name']),
'price' => (float) $this->extract($raw, $this->fieldMap['price']),
'qty' => (int) $this->extract($raw, $this->fieldMap['qty']),
'description' => $this->extract($raw, $this->fieldMap['description']),
'images' => $this->extractImages($raw),
];
}
private function extract(array $data, string $path): mixed
{
return data_get($data, $path);
}
} מתי נדרש סנכרון מצטבר?
סנכרון מצטבר הוא הכרחי כאשר נפח הנתונים גדול או תדירות העדכון גבוהה. הוא מבקש רק שינויים מאז העדכון האחרון, באמצעות הפרמטר class SupplierApiClient { private \GuzzleHttp\Client $http; private RateLimiter $rateLimiter; public function __construct( private SupplierApiConfig $config, ) { $this->http = new \GuzzleHttp\Client([ 'base_uri' => $config->baseUrl, 'timeout' => 30, 'handler' => $this->buildHandlerStack(), ]); } private function buildHandlerStack(): \GuzzleHttp\HandlerStack { $stack = \GuzzleHttp\HandlerStack::create(); $stack->push(\GuzzleHttp\Middleware::retry( function (int $retries, $request, $response, $exception) { if ($retries >= 3) return false; if ($exception instanceof \GuzzleHttp\Exception\ConnectException) return true; if ($response && $response->getStatusCode() >= 500) return true; return false; }, fn(int $retries) => 1000 * (2 ** $retries) )); return $stack; } public function get(string $path, array $params = []): array { $this->rateLimiter->throttle($this->config->id, $this->config->rateLimit); $response = $this->http->get($path, [ 'query' => $params, 'headers' => $this->buildHeaders(), ]); return json_decode($response->getBody(), true); } private function buildHeaders(): array { return match ($this->config->authType) { 'bearer' => ['Authorization' => 'Bearer ' . $this->config->token], 'api_key' => ['X-API-Key' => $this->config->apiKey], 'basic' => ['Authorization' => 'Basic ' . base64_encode( $this->config->login . ':' . $this->config->password )], default => [], }; } } . זמן הסנכרון המוצלח האחרון נשמר במסד הנתונים. זה מפחית את נפח העברת הנתונים פי כמה — בפרויקט אחד עם 500,000 פריטים (SKU), העומס על ה-API ירד ב-90%.
פג'ינציה והשוואת שיטות
| סוג | פשטות | יעילות עם שינויים | נפח העברה |
|---|---|---|---|
| Offset | גבוהה | נמוכה | איפוס מלא |
| Cursor | בינונית | גבוהה | הפרש בלבד |
| Scroll | נמוכה | גבוהה | סטרימינג |
פג'ינציה מסוג Cursor יעילה עד פי 10 מ-Offset עבור מערכי נתונים גדולים תחת שינויים תכופים, מכיוון שהיא משתמשת במזהה ייחודי לרשומה האחרונה. Offset פשוט אך לא יעיל כאשר הנתונים משתנים. עבור נפחים גדולים, אנו ממליצים על Cursor או Scroll.
אימות OAuth 2.0
ספקים רבים דורשים OAuth 2.0 client credentials. הטוקן נשמר במטמון עד לפקיעתו — מה שמונע בקשות מיותרות.
class OAuth2TokenProvider {
private ?string $accessToken = null;
private ?int $expiresAt = null;
public function getToken(): string {
if ($this->accessToken && time() < ($this->expiresAt - 60)) {
return $this->accessToken;
}
$response = Http::asForm()->post($this->tokenUrl, [
'grant_type' => 'client_credentials',
'client_id' => $this->clientId,
'client_secret' => $this->clientSecret,
'scope' => 'products:read stocks:read',
]);
$data = $response->json();
$this->accessToken = $data['access_token'];
$this->expiresAt = time() + $data['expires_in'];
return $this->accessToken;
}
} לקוח SOAP עבור ספקים תואמי 1C
לאינטגרציה עם מערכות מבוססות 1C, אנו משתמשים ב-SOAP. תיעוד WSDL מתאר את השיטות ומבני הנתונים.
$client = new \SoapClient($this->wsdlUrl, [
'login' => $this->login,
'password' => $this->password,
'encoding' => 'UTF-8',
'soap_version' => SOAP_1_2,
'cache_wsdl' => WSDL_CACHE_DISK,
]);
$result = $client->GetProductList([
'DateFrom' => $since->format('Y-m-d\TH:i:s'),
'Categories' => $this->categoryFilter,
]);
foreach ($result->Products->Product as $product) {
yield (array) $product;
} בעיות נפוצות ופתרונות
| בעיה | פתרון |
|---|---|
| פורמטי שדות שונים | נורמליזציה באמצעות fieldMap |
| כשלי רשת | ניסיונות חוזרים עם השהיה אקספוננציאלית |
| חריגה ממגבלות בקשות | הגבלת קצב + תור |
| מלאי מיושן | סנכרון מצטבר (הפחתת נתונים ב-90%) |
| פג'ינציה איטית | פג'ינציה מסוג Cursor (מהירה פי 10 מ-Offset) |
מה כלול בעבודה
- ניתוח תיעוד API של הספק (OpenAPI, WSDL, אוספי Postman).
- פיתוח לקוח עם ניסיונות חוזרים, הגבלת קצב, אימות (OAuth 2.0, API-key, Basic).
- יישום פג'ינציה (offset, cursor, scroll).
- נורמליזציה של שדות לפורמט אחיד (sku, name, price, qty).
- הגדרת סנכרון מצטבר לפי updated_after.
- בדיקות יציבות תחת כשלי רשת וזמני timeout.
- תיעוד אינטגרציה (סכמת נתונים, תצורה, הוראות להוספת ספק חדש).
- הדרכה לצוות שלכם (סדנה של 1–2 שעות).
- חודש תמיכה לאחר ההשקה (תיקוני באגים, כיוונון).
לוחות זמנים ועלות יישום
אנו מספקים פתרון סוהר. לוחות זמנים ועלות משוערים:
- ספק REST אחד עם פג'ינציה מסוג offset ונורמליזציה — מיומיים, עלות החל מ-$2,000.
- הוספת OAuth 2.0, פג'ינציה מסוג cursor וסנכרון מצטבר — +יום אחד, +$1,000.
- ריבוי ספקים עם הגדרות גמישות, SOAP, הגבלת קצב — +יומיים, +$2,000.
לוחות הזמנים משוערים — הערכה מדויקת ניתנת לאחר ניתוח תיעוד הספק. עם ניסיון של 10+ שנים, אנו מבטיחים אינטגרציה חלקה. בקשו הערכה מוקדמת לפרויקט שלכם — נחשב את לוח הזמנים והעלות באופן אישי. צרו קשר, ונכין הצעה מפורטת. קבלו ייעוץ — נעריך את הפרויקט שלכם תוך יום עסקים אחד.







