# Metadata және тапсырыс нөмірі

> externalOrderId мен metadata өрістерінің айырмашылығы, олардың webhook-та қалай қайта келетіні, metadata ішіне нені жазуға болады және нені ешқашан жазбау керек — нақты мысалдармен.

## Қысқаша

Счёт жасағанда екі өріс сіздің жүйеңіздің деректерін счётқа тіркеп қоюға арналған:

- **`externalOrderId`** — сіздің тапсырыс нөміріңіз. Қысқа жол, кабинетте көрінеді, іздеуге жарайды, webhook-та қайта келеді.
- **`metadata`** — кез келген JSON объектісі. Клиентке көрінбейді, іздеуге кірмейді, бірақ webhook-та да, күйді сұрағанда да сол күйінде қайтады.

Ережесі бір: **екеуіне де құпия дерек жазбаңыз.** `metadata` шифрланбайды және кабинетке кірген кез келген қызметкер оны көре алады.

## externalOrderId

Бұл — сіздің дүкеніңіздегі немесе CRM-іңіздегі тапсырыс нөмірі. Kaspi-ге кетпейді, клиент оны көрмейді.

```json
{
  "amount": 12500,
  "description": "Тапсырыс №4471",
  "externalOrderId": "4471"
}
```

Не үшін керек:

| Мүмкіндік | Қалай жұмыс істейді |
|---|---|
| Webhook-та қайту | `invoice.externalOrderId` өрісінде келеді — қай тапсырысты жабуды бірден білесіз |
| Іздеу | `GET /api/v1/invoices?externalOrderId=4471` — сол тапсырыстың счёттарын алады |
| Кабинетте көріну | Счёттар тізімінде және счёт карточкасында көрінеді |
| Экспорт | CSV бағанына түседі, бухгалтерияға салыстыруға ыңғайлы |

Бір тапсырысқа бірнеше счёт болуы мүмкін (біріншісі өтіп кетті, клиент қайта төледі). Сондықтан `externalOrderId` — бірегей кілт емес. Қайталанудан қорғау үшін бөлек `Idempotency-Key` тақырыбы бар: [Идемпоттылық](/kb/idempotency).

Не жазу керек: тапсырыс нөмірі, брондау коды, шот-фактура нөмірі. Не жазбау керек: клиенттің телефоны, ЖСН, email — бұлар үшін `customer` өрісі бар.

## metadata

`metadata` — кез келген JSON объектісі. Біз оның ішіне қарамаймыз, тексермейміз, тек сақтап, кері қайтарамыз.

```json
{
  "amount": 3000,
  "kind": "qr",
  "description": "Су, 19 л",
  "externalOrderId": "4471",
  "metadata": {
    "machineId": "vnd-07",
    "slot": "A3",
    "branch": "almaty-abay",
    "courierId": 22,
    "source": "telegram-bot"
  }
}
```

Осы объект өзгертілмеген күйінде:

- `invoice.paid` және басқа webhook оқиғаларының `invoice.metadata` өрісінде келеді;
- `GET /api/v1/invoices/{id}` жауабында болады;
- кабинеттегі счёт карточкасында көрінеді.

Сондықтан «төленді» хабары келгенде дерекқорға қайта бармай-ақ, қай автоматтың қай ұяшығын ашу керегін бірден білесіз.

### Нені жазуға болады

| Мысал | Не үшін |
|---|---|
| `orderLineId`, `dealId` | CRM-дегі ішкі идентификатор |
| `machineId`, `slot`, `terminalId` | Вендинг автоматы, ұяшық, құрылғы нөмірі |
| `courierId`, `routeId` | Курьер мен маршрут — жеткізу сәтінде төлем |
| `branch`, `pointId` | Нүкте, филиал — есепті бөлу үшін |
| `source`, `campaign` | Трафик көзі, науқан белгісі |
| `tableNo`, `waiterId` | Мейрамханадағы үстел мен даяшы |
| `cartHash`, `attempt` | Ішкі техникалық белгілер |

Ұстанымы: **мұнда тек өз жүйеңіздің техникалық белгілері жатуы керек.**

### Нені ешқашан жазбау керек

| Жазбаңыз | Себебі |
|---|---|
| Пароль, токен, API кілт | Кабинетке кірген кез келген қызметкер көреді |
| Карта нөмірі, CVV, банк реквизиттері | Мұндай деректі сақтауға бізде негіз жоқ, ол сізге тәуекел |
| ЖСН, БИН, құжат нөмірі | Артық дербес дерек — қажет емес |
| Клиенттің мекенжайы, диагнозы, өтініш мәтіні | Дербес және құпия дерек |
| Толық себет мазмұны, ұзын журнал | `metadata` дерекқор емес, өз жүйеңізге идентификатор жазыңыз |

Клиенттің аты, телефоны, email керек болса — оларды `metadata`-ға емес, арнайы `customer` өрісіне жазыңыз: сонда ғана чек пен хабарлама дұрыс жұмыс істейді. Не сақталатыны туралы: [Деректер және құпиялық](/kb/data-and-privacy).

### Көлемі

`metadata` — қысқа объект болуға тиіс: бірнеше ондаған өріс, мәндері қысқа жолдар мен сандар. Ондаған килобайт JSON жіберсеңіз, счёт жасау баяулайды және webhook денесі ауырлайды. Ұзын мәтінді өз дерекқорыңызда сақтап, мұнда тек оның идентификаторын қалдырыңыз.

## Webhook-та қалай қайтады

```json
{
  "event": "invoice.paid",
  "invoice": {
    "id": "inv_01J8…",
    "externalOrderId": "4471",
    "status": "paid",
    "amount": 3000,
    "metadata": { "machineId": "vnd-07", "slot": "A3" },
    "receiptUrl": "https://…"
  },
  "sentAt": "2026-09-14T09:12:04.000Z"
}
```

Өңдеу мысалы:

```js
if (event === 'invoice.paid') {
  const { externalOrderId, metadata } = invoice;
  await markOrderPaid(externalOrderId);          // тапсырысты жабу
  if (metadata?.machineId) openSlot(metadata.machineId, metadata.slot);
}
```

Webhook бір оқиғаны екі рет жеткізуі мүмкін, сондықтан өңдеу идемпотентті болсын — `(invoice.id, status)` жұбы бойынша тексеріңіз.

Жазылым бойынша шыққан счёттарда `metadata` ішінде `subscriptionId` және `run` өрістері автоматты пайда болады. Өз өрістеріңізді сол атаулармен атамаңыз.

## Қайсысын таңдау керек

| Сұрақ | Жауап |
|---|---|
| Кабинеттен іздегім келеді | `externalOrderId` |
| CSV экспортта көрінуі керек | `externalOrderId` |
| Клиент көрсін деймін | Ешқайсысы емес — `description` жазыңыз |
| Webhook-та қайтса жетеді | `metadata` |
| Бірнеше мән керек | `metadata` |

## Жиі қойылатын сұрақтар

**metadata бойынша іздеуге бола ма?** Жоқ. Іздеу `externalOrderId` бойынша жүреді. Іздеу керек мәнді сол өріске шығарыңыз.

**metadata-ны кейін өзгертуге бола ма?** Жоқ, ол счёт жасалған сәтте бекітіледі. Жаңа дерек пайда болса, оны өз жүйеңізде сақтаңыз.

**Клиент metadata-ны көре ме?** Жоқ. Төлем бетінде де, Kaspi қосымшасында да, чекте де көрінбейді. Клиент тек `description` мәтінін, соманы және сатушыны көреді.

**externalOrderId бірегей болуы міндетті ме?** Жоқ. Бір тапсырысқа бірнеше счёт болуы қалыпты жағдай. Қосарланудан қорғау `Idempotency-Key` арқылы жасалады.

**metadata-ға массив немесе кірістірілген объект салуға бола ма?** Иә, кез келген жарамды JSON. Бірақ өңдеуді жеңілдету үшін бір деңгейлі, қысқа объект ұстаған дұрыс.
