API v1

საიტის დაკავშირება API-ით

თქვენს საიტზე გაფორმებული შეკვეთა ერთი მოთხოვნით ჩნდება Gayidvebi.ge-ს დაფაზე, სვეტში „საიტიდან“. როცა გამყიდველი შეკვეთას გადაიტანს, თქვენი საიტი webhook-ით გაიგებს ახალ სტატუსს. ასევე შეგიძლიათ წაიკითხოთ პროდუქტები და მარაგი.

API-ს მხოლოდ საიტის სერვერიდან (PHP, Node, Python…) გამოიძახეთ — არასდროს ბრაუზერის JavaScript-იდან. გასაღები საიდუმლოა: შეინახეთ გარემოს ცვლადში (მაგ. GAYIDVEBI_API_KEY).
Base URL
https://www.gayidvebi.ge/api/v1
ფორმატი
JSON, UTF-8
OpenAPI
/api/v1/openapi.json

სწრაფი დაწყება

  1. გამყიდველი ქმნის გასაღებს: პარამეტრები → ინტეგრაციები → საკუთარი საიტი (API).
  2. შეამოწმეთ გასაღები — პასუხი 200 ნიშნავს, რომ ყველაფერი რიგზეა და პარამეტრებში გამოჩნდება „დაკავშირებულია“:
curl https://www.gayidvebi.ge/api/v1/ping \
  -H "Authorization: Bearer gyd_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  1. checkout-ის შემდეგ გამოგზავნეთ შეკვეთა:
<?php
// $orderId, $customer, $cart come from your checkout
$order = [
    'id'       => (string) $orderId,
    'customer' => ['name' => $customer['name'], 'phone' => $customer['phone']],
    'shipping' => ['city' => $customer['city'], 'address' => $customer['address'], 'fee' => 6],
    'payment'  => ['method' => 'cod', 'paid' => false],
    'items'    => array_map(fn ($line) => [
        'product_id' => (string) $line['product_id'],
        'name'       => $line['name'],
        'options'    => $line['options'] ?? [],      // ['ზომა' => 'L']
        'quantity'   => (int) $line['qty'],
        'price'      => (float) $line['unit_price'],
    ], $cart),
];

$ch = curl_init('https://www.gayidvebi.ge/api/v1/orders');
curl_setopt_array($ch, [
    CURLOPT_POST           => true,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_TIMEOUT        => 15,
    CURLOPT_HTTPHEADER     => [
        'Authorization: Bearer ' . getenv('GAYIDVEBI_API_KEY'),
        'Content-Type: application/json',
    ],
    CURLOPT_POSTFIELDS     => json_encode($order, JSON_UNESCAPED_UNICODE),
]);
$response = json_decode((string) curl_exec($ch), true);
$status   = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);

if ($status === 201 || $status === 200) {
    // Saved (or it was already saved). $response['order']['crm_id']
} elseif ($status === 0 || !empty($response['retry'])) {
    // No answer, 429 or 5xx: try again later with the same id — never saved twice
} else {
    error_log('Gayidvebi refused the order: ' . json_encode($response));
}

ავტორიზაცია

ყველა მოთხოვნას დაურთეთ ჰედერი Authorization: Bearer gyd_live_…. გასაღები ერთ საიტზეა მიბმული.

  • ბრაუზერიდან (Origin ჰედერით) მოსული მოთხოვნა უარყოფილია — 403.
  • გასაღები URL-ში (?key=…) — 400.
  • ლიმიტი: წუთში 120 მოთხოვნა გასაღებზე. გადაჭარბებისას — 429 და Retry-After.
  • ახალი გასაღების შექმნისას ძველი მაშინვე წყვეტს მუშაობას (401 key_revoked).
  • გამოიყენეთ www: gayidvebi.ge (www-ის გარეშე) გადამისამართებს, და ზოგი HTTP კლიენტი POST-ს გადამისამართებისას არ აგზავნის.

შეკვეთის გამოგზავნა

POST/api/v1/orders

შეკვეთა ჩნდება სვეტში „საიტიდან“ — გამყიდველი ხმოვან შეტყობინებას იღებს და ადასტურებს. მარაგი ავტომატურად აკლდება.

ველიტიპიაღწერა
id*stringთქვენი შეკვეთის ID (1–64 სიმბოლო: ასოები, ციფრები, . _ : -). ერთი და იგივე id ორჯერ არასდროს შეინახება.
numberstringშეკვეთის ნომერი, რომელსაც მყიდველი ხედავს (ნაგულისხმევად = id).
created_atISO 8601როდის გაფორმდა (ნაგულისხმევად — ახლა).
customer.namestringსახელი და გვარი (ან customer.first_name + customer.last_name).
customer.phone*stringტელეფონი ნებისმიერ ფორმატში: +995 555 12 34 56, 555123456…
customer.emailstringკომენტარში ჩაიწერება, თუ გამყიდველმა ჩართო.
customer.personal_idstringპირადი ნომერი (ზედნადებისთვის).
shipping.city / shipping.addressstringმიწოდების ქალაქი და მისამართი.
shipping.feenumberმიტანის საფასური ლარში.
shipping.paid_by"customer" | "shop"ვინ უხდის კურიერს. თუ არ მიუთითებთ: ონლაინ გადახდისას — მაღაზია, ადგილზე — მყიდველი.
payment.methodstringგადახდის კოდი: cod / cash (ადგილზე), bank_transfer ან თქვენი ნებისმიერი კოდი — გამყიდველი პარამეტრებში მიაბამს მეთოდს.
payment.title / payment.paidstring / booleanმეთოდის სახელი და გადახდილია თუ არა.
items[]*array1–200 პროდუქტი.
items[].name*stringპროდუქტის სახელი ვარიაციის გარეშე („მაისური“ და არა „მაისური - L“). ასე ემთხვევა CRM-ის პროდუქტს; თუ არ არსებობს, შეიქმნება.
items[].quantity*integerრაოდენობა (1-დან).
items[].price*numberერთეულის ფასი, რომელსაც მყიდველი იხდის.
items[].product_id / variant_id / skustringთქვენი ID-ები. პირველი შეკვეთის შემდეგ ვიმახსოვრებთ, რომელ CRM პროდუქტს შეესაბამება — სახელის შეცვლის შემდეგაც.
items[].optionsobjectვარიაცია: {"ზომა": "L"}. ემთხვევა CRM-ის ვარიაციებს (დიდ/პატარა ასოებს არ აქვს მნიშვნელობა).
discountnumberშეკვეთის ფასდაკლება ლარში — პროპორციულად ნაწილდება პროდუქტებზე.
totalnumberსულ თანხა. თუ არ ემთხვევა, გამყიდველი გაფრთხილებას ნახავს.
currency / note / admin_urlstringვალუტა (GEL), მყიდველის კომენტარი, შეკვეთის ბმული თქვენს ადმინში.
{
  "id": "1001",
  "number": "A-1001",
  "created_at": "2026-10-15T13:30:00+04:00",
  "customer": {
    "name": "ნინო ბერიძე",
    "phone": "+995 555 12 34 56",
    "email": "nino@example.ge",
    "personal_id": "01001012345"
  },
  "shipping": {
    "city": "თბილისი",
    "address": "ჭავჭავაძის გამზ. 12, ბინა 4",
    "fee": 6,
    "paid_by": "customer"
  },
  "payment": { "method": "cod", "title": "ადგილზე გადახდა", "paid": false },
  "items": [
    {
      "product_id": "202",
      "variant_id": "203",
      "sku": "TS-L",
      "name": "მაისური",
      "options": { "ზომა": "L", "ფერი": "შავი" },
      "quantity": 2,
      "price": 35
    }
  ],
  "discount": 0,
  "total": 76,
  "currency": "GEL",
  "note": "დარეკეთ მისვლამდე",
  "admin_url": "https://shop.ge/admin/orders/1001"
}

განმეორება უსაფრთხოა

თუ პასუხი არ მოვიდა, ან მოვიდა 429/5xx (retry: true), გაგზავნეთ იგივე შეკვეთა ხელახლა. ერთი და იგივე id მეორედ არ შეინახება — მიიღებთ 200-ს და result: "duplicate". სურვილისამებრ დაურთეთ Idempotency-Key ჰედერი (8–100 სიმბოლო).

შემოწმება შენახვის გარეშე

POST /api/v1/orders?dry_run=1 — ამოწმებს ყველა ველს და გიჩვენებთ, როგორ შეივსება შეკვეთა CRM-ში, მაგრამ არაფერს ინახავს. არასწორი ველები ბრუნდება ასე:

HTTP/1.1 422 Unprocessable Entity
{
  "ok": false,
  "error": "validation_failed",
  "message": "Some fields are invalid. See `errors`.",
  "field": "customer.phone",
  "errors": [
    { "field": "customer.phone", "code": "required", "message": "customer.phone is required" },
    { "field": "items[0].quantity", "code": "invalid_value", "message": "items[0].quantity must be a whole number from 1 to 100000" }
  ],
  "retry": false,
  "server_time": 1760520601
}

შეკვეთის სტატუსი

GET/api/v1/orders/{id}

{id} — თქვენი შეკვეთის id. აბრუნებს იმავე order ობიექტს, რაც შექმნისას. სტატუსის ცვლილებების მისაღებად უმჯობესია webhook-ები.

curl https://www.gayidvebi.ge/api/v1/orders/1001 \
  -H "Authorization: Bearer gyd_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

გაუქმება

POST/api/v1/orders/{id}/cancel

თუ შეკვეთა ჯერ კიდევ „საიტიდან“ სვეტშია — უქმდება და მარაგი ბრუნდება. თუ გამყიდველმა უკვე დაადასტურა — შეკვეთას ემატება შენიშვნა „⚠ საიტზე გაუქმდა“ და გადაწყვეტილებას გამყიდველი იღებს. პასუხში მიმდინარე სტატუსია.

curl -X POST https://www.gayidvebi.ge/api/v1/orders/1001/cancel \
  -H "Authorization: Bearer gyd_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "reason": "customer" }'

პროდუქტები და მარაგი

GET/api/v1/products

გამყიდველის პროდუქტები: ფასი, ვარიაციები და ხელმისაწვდომი მარაგი (თვითღირებულება არასდროს). პარამეტრები: limit (1–200, ნაგულისხმევად 100), cursor (წინა პასუხის next_cursor), updated_since (ISO თარიღი — მხოლოდ შეცვლილები). პასუხი 60 წამით შეგიძლიათ დაიმახსოვროთ.

curl "https://www.gayidvebi.ge/api/v1/products?limit=100" \
  -H "Authorization: Bearer gyd_live_xxxxxxxxxxxx_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

Webhook-ები

გამყიდველი პარამეტრებში უთითებს თქვენს https მისამართს და იღებს ხელმოწერის გასაღებს (whsec_…). ყოველ ჯერზე, როცა API-ით მიღებული შეკვეთის სტატუსი იცვლება, ამ მისამართზე მოდის POST მოთხოვნა. ფორმატი — Standard Webhooks (standardwebhooks.com): შეგიძლიათ გამოიყენოთ მათი ბიბლიოთეკები ან ქვემოთ მოცემული კოდი.

  • order.status_changed — სტატუსი შეიცვალა (დადასტურებაც).
  • order.deleted — გამყიდველმა შეკვეთა წაშალა.
  • test — ღილაკი „სატესტო შეტყობინება“ პარამეტრებში.
POST /your/webhook/url HTTP/1.1
Content-Type: application/json
webhook-id: msg_5b0c3a1e9d2f4c7b8a6e1d0c9b8a7f6e
webhook-timestamp: 1760520601
webhook-signature: v1,K5oZfzN95Z9UVu1EsfQmfVNQhnkZ2pj9o9NDN/H/pI4=

{
  "type": "order.status_changed",
  "timestamp": "2026-10-15T09:30:01.123Z",
  "data": {
    "id": "1001",
    "number": "A-1001",
    "crm_id": "5b0c…-…",
    "status":          { "id": "shipped", "label": "გაგზავნილი", "stage": "in_progress" },
    "previous_status": { "id": "pending", "label": "ახალი",      "stage": "new" },
    "changed_at": "2026-10-15T09:30:01.123Z"
  }
}
stageმნიშვნელობა
receivedმიღებულია, ელოდება გამყიდველის დადასტურებას („საიტიდან“).
newდადასტურებულია, მუშავდება.
in_progressგამყიდველის ნებისმიერი სხვა სვეტი (მაგ. „გაგზავნილი“). label-ში მისი სახელია.
completedმიწოდებულია / დასრულებულია.
cancelledგაუქმებულია (ან დაბრუნებულია).

status.id და label გამყიდველის სვეტებია და შეიძლება შეიცვალოს — კოდში stage გამოიყენეთ.

ხელმოწერის შემოწმება

<?php
$secret = getenv('GAYIDVEBI_WEBHOOK_SECRET');           // whsec_...
$id     = $_SERVER['HTTP_WEBHOOK_ID'] ?? '';
$ts     = $_SERVER['HTTP_WEBHOOK_TIMESTAMP'] ?? '';
$header = $_SERVER['HTTP_WEBHOOK_SIGNATURE'] ?? '';
$body   = file_get_contents('php://input');              // the raw body, not re-encoded

$key      = base64_decode(substr($secret, strlen('whsec_')));
$expected = base64_encode(hash_hmac('sha256', "$id.$ts.$body", $key, true));
$valid    = false;
foreach (explode(' ', $header) as $part) {
    [$version, $signature] = array_pad(explode(',', $part, 2), 2, '');
    if ($version === 'v1' && hash_equals($expected, $signature)) $valid = true;
}
if (!$valid || abs(time() - (int) $ts) > 300) {
    http_response_code(401);
    exit;
}

$event = json_decode($body, true);
// Retries reuse the same webhook-id: skip an id you already handled
if ($event['type'] === 'order.status_changed') {
    $orderId = $event['data']['id'];                       // your order id
    $stage   = $event['data']['status']['stage'];          // received|new|in_progress|completed|cancelled
    // … update the order on your site
}
http_response_code(200);
  • უპასუხეთ 2xx-ით 10 წამში. სხვა პასუხზე ან დროის ამოწურვაზე ვცდით ხელახლა: 1 წთ, 5 წთ, 30 წთ, 2 სთ, 6 სთ, 12 სთ, 24 სთ.
  • განმეორებისას webhook-id იგივეა — უკვე დამუშავებული id გამოტოვეთ.
  • თანმიმდევრობა გარანტირებული არ არის: შეადარეთ data.changed_at და ძველი გამოტოვეთ, ან წაიკითხეთ GET /orders/{id}.
  • გადამისამართებას (3xx) არ მივყვებით; მისამართი უნდა იყოს საჯარო https.

შეცდომები

შეცდომისას პასუხია {"ok": false, "error": "<code>", "message": "…", "retry": false}. retry: true ნიშნავს, რომ მოგვიანებით განმეორება დაგეხმარებათ.

errorHTTPretryმნიშვნელობა
missing_api_key / invalid_api_key / unknown_key401falseგასაღები არ არის, არასწორია ან უცნობია.
key_revoked401falseგასაღები გაუქმდა ან შეიცვალა — ახალი შექმენით პარამეტრებში.
browser_requests_not_allowed403falseმოთხოვნა ბრაუზერიდან მოვიდა. გამოიძახეთ სერვერიდან.
plan_not_allowed403falseAPI ხელმისაწვდომია Pro პაკეტიდან.
key_in_url400falseგასაღები URL-შია. გადაიტანეთ Authorization ჰედერში.
invalid_json / invalid_idempotency_key / invalid_query / invalid_id400falseმოთხოვნა არასწორადაა აწყობილი.
validation_failed / invalid_phone422falseველები არასწორია — იხ. field და errors.
order_not_found404falseამ id-ით შეკვეთა თქვენი საიტიდან არ მიგვიღია.
payload_too_large413falseსხეული 256 KB-ზე დიდია.
rate_limited429trueწუთში 120-ზე მეტი მოთხოვნა. დაელოდეთ Retry-After წამს.
server_error / not_configured500 / 503trueჩვენი მხრის შეცდომა — სცადეთ მოგვიანებით (იგივე id უსაფრთხოა).

კითხვები? მოგვწერეთ