# QR счёт пен телефонға счёт — қайсысын таңдау керек

> Екі счёт түрінің толық салыстыруы: kind мәні, клиент не істейді, нөмір керек пе, сипаттама мен сома шектеулері, мерзімі және қай сценарийге қайсысы келеді.

## Қысқаша

Счёт жасағанда `kind` өрісінің екі мәні бар:

- **`qr`** (әдепкі) — QR коды мен төлем сілтемесі жасалады. Клиент QR-ды сканерлейді немесе сілтемені ашады.
- **`phone`** — счёт клиенттің Kaspi қосымшасына push түрінде барады. `customer.phone` міндетті.

Ең қысқа ереже: **клиент сіздің экраныңызды көріп тұрса — `qr`, алыста болса және нөмірі белгілі болса — `phone`**.

## Толық салыстыру

| | QR счёт | Телефонға счёт |
|---|---|---|
| `kind` мәні | `qr` (әдепкі) | `phone` |
| Клиент не істейді | QR-ды сканерлейді немесе `payUrl` сілтемесін ашады | Kaspi қосымшасындағы хабарламаны ашып, растайды |
| Телефон нөмірі керек пе | Жоқ | Иә, `customer.phone` міндетті |
| Сипаттама шектеуі | 100 таңба | 60 таңба |
| Сома | Ең көбі 2 ондық белгі | Тек бүтін теңге |
| Мерзімі | QR сканерлеу терезесі шектеулі, `expiresAt` өрісінен алыңыз | Хабарлама қосымшада тұрады, терезесі ұзағырақ |
| Клиенттің Kaspi қосымшасы | Міндетті емес: сілтеме браузерде де ашылады | **Міндетті** |
| Жауап өрістері | `payUrl`, `qrUrl`, `qrImageUrl`, `deepLink` | `payUrl` бар, бірақ негізгі арна — push |
| Қай жерде ыңғайлы | Касса, витрина, вендинг, сайт, әлеуметтік желі | Қоңыраудан кейін, чат, жазылым, топтама |

## Сипаттама шектеуі

`description` өрісі клиентке көрінеді, бірақ шегі екі түрде әртүрлі: **QR-да 100 таңба, phone-да 60**.

Екі түрді бір кодта қолдансаңыз, мәтінді қауіпсіз шекке қиып жіберген дұрыс:

```js
const LIMIT = { qr: 100, phone: 60 };

function invoiceBody(kind, amount, text, phone) {
  return {
    kind,
    amount,
    description: text.slice(0, LIMIT[kind]),
    ...(kind === 'phone' ? { customer: { phone } } : {}),
  };
}
```

Мәтін шектен асып кетсе счёт қабылданбауы мүмкін, ал бұл жаппай жіберуде бірден көзге түспейді — сондықтан қиюды кодта жасаңыз.

## Сома айырмашылығы

| Түрі | Рұқсат етілген сома |
|---|---|
| `qr` | Ең көбі екі ондық белгі: `1250.50` жарайды |
| `phone` | Тек бүтін теңге: `1250` жарайды, `1250.50` жарамайды |

Телефонға счётқа тиын жіберсеңіз `amount_must_be_whole_tenge` қатесі келеді. Сондықтан есептеу жүйеңізде тиын болса, `phone` түрінде **дөңгелектеу ережесін алдын ала шешіп қойыңыз** — жоғары қарай ма, төмен қарай ма. Дөңгелектеуді клиентке түсіндіру оңай болу үшін, әдетте жоғары қарай дөңгелектеп, айырманы жеңілдік ретінде көрсетеді немесе керісінше.

QR-да екі ондық белгі рұқсат етілгенімен, іс жүзінде клиентке дөңгел сома әрқашан түсініктірек.

## Мерзім: QR терезесі туралы

QR-дың сканерлеу терезесі **шектеулі және оны Kaspi белгілейді**. Ол нақты неше минут екенін кодыңызда тұрақты сан ретінде жазбаңыз — жауаптағы **`expiresAt`** өрісінен алыңыз.

```js
const inv = await createInvoice({ amount: 2500, kind: 'qr' });
const msLeft = new Date(inv.expiresAt) - Date.now();
showCountdown(msLeft);          // терезе бітерде QR-ды өзіңіз жасырыңыз
```

Терезе біткен QR-ды сканерлеген клиент Kaspi қосымшасынан «кейінірек көріңіз» деген мазмұндағы хабар алады. Бұл ақау емес — жай ғана счёттың мерзімі өткен. Шешімі біреу: **жаңа счёт жасау**.

Сондықтан экранда QR ұзақ тұратын жерде (вендинг, турникет, витрина) кері санақ қойып, терезе біткенде QR-ды автоматты жаңартып отырыңыз.

Телефонға жіберілген счётта хабарлама клиенттің қосымшасында тұрады, сондықтан ол асығыс шешім қабылдамайды. Бұл — `phone` түрінің басты артықшылығы.

## Клиентте Kaspi қосымшасы болмаса

`phone` счёт клиенттің Kaspi қосымшасына барады. Қосымшасы жоқ немесе оның Kaspi-і басқа нөмірге тіркелген болса, **хабарлама ешқайда келмейді** — счёт `pending` күйінде тұрып, ақыры мерзімі бітеді.

Ондай жағдайда:

1. Сол клиентке `qr` счёт жасаңыз.
2. Оған `payUrl` сілтемесін WhatsApp немесе SMS арқылы жіберіңіз — сілтеме браузерде де ашылады.
3. Немесе тұрақты [төлем сілтемесін](/kb/payment-links) беріңіз.

Толығырақ: [Клиентте Kaspi қосымшасы жоқ](/kb/customer-no-kaspi).

## Нөмір пішімі

`phone` түрінде нөмір `7XXXXXXXXXX` пішімінде, 11 сан болуы керек: `77011234567`. `+7`, `8`, бос орын, сызықша қабылданбайды — `invalid_phone` қатесі келеді.

Нөмірді жіберер алдында тазалап алыңыз:

```js
const normalize = (raw) => {
  const d = String(raw).replace(/\D/g, '');
  if (d.length === 11 && d.startsWith('8')) return '7' + d.slice(1);
  if (d.length === 10) return '7' + d;
  return d;
};
```

## Қай сценарийге қайсысы

| Сценарий | Дұрысы | Себебі |
|---|---|---|
| Кассадағы экран, витрина | `qr` | Клиент қасыңызда, нөмірі керек емес |
| Вендинг, турникет, шлагбаум | `qr` | Сканерлеп, бірден төлейді |
| Интернет-дүкен, тапсырыс беті | `qr` | Клиентті `payUrl`-ге жібересіз |
| Instagram, TikTok, директ | `qr` немесе төлем сілтемесі | Нөмірін сұрамай-ақ сілтеме жібересіз |
| Қоңыраудан кейінгі есеп-шот | `phone` | Клиент алыста, нөмірі белгілі |
| Жазылым бойынша кезекті төлем | `phone` | Клиент өзінің қосымшасынан көреді |
| Топтама: жүргізушілер, тыңдаушылар | `phone` | Нөмір базада бар, сілтеме таратудың қажеті жоқ |
| Курьер жеткізген сәтте | `qr` | Курьердің телефонындағы QR |

Екеуін қатар қолдануға болады: алдымен `phone` жіберіп, клиент бір-екі минутта ашпаса, оған `qr` сілтемесін чатпен қоса жіберу — жиі кездесетін әрі жақсы жұмыс істейтін тәсіл.

## Екеуіне ортақ нәрселер

- Күйлер бірдей: `new` → `pending` → `paid` / `cancelled` / `expired`.
- Webhook оқиғалары бірдей: `invoice.paid` және қалғандары.
- `externalOrderId`, `metadata`, `Idempotency-Key` екеуінде де жұмыс істейді.
- Қайтару екеуінде де бірдей жүреді.
- Ақша екі жағдайда да тіке сіздің Kaspi шотыңызға түседі.
- Тариф лимитіне екеуі де бірдей есептеледі.

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

**`kind` жазбасам не болады?** Әдепкі мән `qr` қолданылады.

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

**`phone` счёттың да `payUrl` сілтемесі бола ма?** Иә, жауапта келеді. Клиент push хабарламасын байқамай қалса, сол сілтемені жіберуге болады.

**Клиент кассир нөмірін көре ме?** Иә, Kaspi хабарламасында кассир нөмірі көрінеді — бұл Kaspi-дің қалыпты жұмысы, екі түрге де қатысты.

**QR суретін өзім жасай алам ба?** Қажеті жоқ: жауапта дайын `qrImageUrl` және `deepLink` келеді.

**Счёт `pending` күйінде ұзақ тұрса, бұл қалыпты ма?** Клиент әлі төлемеген деген сөз. Қашан `expired` болатыны жауаптағы `expiresAt` өрісінде: [Счёт pending күйінде тұрып қалды](/kb/invoice-stuck-pending).
