Приём платежей

Платежи

Платёж — это запрос денег у покупателя через Kaspi. Вы выбираете способ оплаты, Sezgir Pay создаёт операцию в Kaspi, следит за её статусом и сообщает о результате вебхуком.

Типы платежей

typeКак платит покупательСрок жизниЧто вы получаете
linkОткрывает страницу оплаты и нажимает «Оплатить»: на компьютере видит QR, на телефоне сразу переходит в Kaspi.kzЗадаётся expires_in: от 5 минут до 30 дней, по умолчанию 7 днейcheckout_url
qrСканирует QR с вашего экрана камерой или в приложении Kaspi.kzНесколько минут — срок QR задаёт Kaspiprovider.qr_url, provider.qr_expires_at
invoiceПолучает счёт в приложении Kaspi.kz по номеру телефона и подтверждает его24 часаСчёт уже у покупателя

Если не уверены, начните со ссылки: она работает везде, сама выпускает новый QR, если покупатель не успел оплатить, и не требует от вас интерфейса оплаты.

Создание платежа

Все платежи создаются одним запросом POST /payments; поле type выбирает способ. Обязательны только type и amount (и phone для счёта). Полный список параметров — в справочнике.

curl https://pay.sezgir.com/api/v1/payments \
  -H "Authorization: Bearer $SEZGIR_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: 9f1c2a4e-7b3d-4e8a-a1f0-5c6d7e8f9a0b" \
  -d '{
    "type": "link",
    "amount": 15000,
    "description": "Заказ №1042",
    "external_id": "order_1042",
    "metadata": { "order_id": 1042, "channel": "web" }
  }'
  • amount — целое число тенге от 1 до 50 000 000. Дробные суммы отклоняются с ошибкой validation_failed.
  • external_id — ваш идентификатор заказа (до 200 символов). Уникальность не проверяется; по нему можно искать платежи.
  • metadata — до 50 пар «ключ → значение» для ваших нужд. Ключ — до 40 символов, значение — строка до 500 символов, число или true/false. Покупатель метаданные не видит.
  • description — до 500 символов. Показывается покупателю на странице оплаты, а для счёта передаётся в Kaspi как комментарий.
Передавайте Idempotency-Key
Если ответ потерялся из-за сети, повтор с тем же ключом вернёт уже созданный платёж вместо второго. См. Идемпотентность.

Ссылка не создаёт операцию в Kaspi сразу. Она появляется, когда покупатель открывает checkout_url и нажимает «Оплатить»: Sezgir Pay выпускает QR на несколько минут. Если покупатель не успел, на странице можно получить новый QR — до 20 попыток на одну ссылку. Неудачная попытка не закрывает ссылку: платёж остаётся pending, пока его не оплатят, пока не истечёт expires_in или пока вы его не отмените.

Срок жизни задаётся параметром expires_in в секундах: от 300 (5 минут) до 2592000 (30 дней), по умолчанию — 7 дней. Момент истечения возвращается в поле expires_at.

Ссылка на 1 час
curl "https://pay.sezgir.com/api/v1/payments" \
  -H "Authorization: Bearer $SEZGIR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "link", "amount": 5000, "expires_in": 3600 }'

QR-код — qr

Платёж типа qr сразу создаёт операцию в Kaspi. В ответе приходит provider.qr_url — ссылка вида https://pay.kaspi.kz/pay/…. Закодируйте её в QR и покажите на экране, а на телефоне покупателя откройте её напрямую — запустится приложение Kaspi.kz.

curl
curl "https://pay.sezgir.com/api/v1/payments" \
  -H "Authorization: Bearer $SEZGIR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "type": "qr", "amount": 2500, "description": "Кофе и круассан" }'
Ответ · 201 Created
{
  "id": "pay_8Rt2XvL5nQ1mZ7cK4wB9pJ3s",
  "object": "payment",
  "livemode": true,
  "project_id": "prj_7HcTq2LwZ9mRk4VbN1sXe8Dy",
  "type": "qr",
  "status": "pending",
  "amount": 2500,
  "amount_refunded": 0,
  "currency": "KZT",
  "description": "Кофе и круассан",
  "phone": null,
  "external_id": "pos-77-1029",
  "metadata": {},
  "checkout_url": "https://pay.sezgir.com/pay/pay_8Rt2XvL5nQ1mZ7cK4wB9pJ3s",
  "provider": {
    "name": "kaspi",
    "operation_id": "18734512",
    "kind": "qr",
    "status": "QrTokenCreated",
    "qr_url": "https://pay.kaspi.kz/pay/fT9kq2Lx7mWc",
    "qr_expires_at": "2026-10-25T10:05:41.000Z"
  },
  "failure_reason": null,
  "requires_review": false,
  "expires_at": "2026-10-25T10:06:41.000Z",
  "paid_at": null,
  "canceled_at": null,
  "created_at": "2026-10-25T10:02:41.204Z"
}

QR живёт несколько минут; точное время — в provider.qr_expires_at. Поле expires_at платежа чуть позже: после истечения QR Sezgir Pay ещё минуту ждёт подтверждения от Kaspi, на случай если покупатель оплатил в последний момент. Новый QR для того же платежа не выпускается — создайте новый платёж. Если нужен перевыпуск, используйте link и показывайте checkout_url.

Параметр expires_in действует только для link; для qr и invoice он вызывает ошибку 400. Номер phone для любого типа проверяется: неверный формат — 400.

Счёт по телефону — invoice

Kaspi отправляет счёт в приложение Kaspi.kz покупателя, и тот подтверждает оплату в разделе уведомлений. Нужен номер телефона, к которому привязан Kaspi покупателя. Подойдёт любой казахстанский формат: +7 701 234 56 78, 87012345678, 7012345678, +7 (701) 234-56-78. В ответе номер приводится к виду 77012345678.

curl
curl "https://pay.sezgir.com/api/v1/payments" \
  -H "Authorization: Bearer $SEZGIR_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "invoice",
    "amount": 48900,
    "phone": "+7 701 234 56 78",
    "description": "Доставка, заказ 5521",
    "external_id": "order_5521"
  }'
Ответ · 201 Created
{
  "id": "pay_4Vb9Kq2Lm7Xc1Zt8Rn5Ws3Pd",
  "object": "payment",
  "livemode": true,
  "project_id": "prj_7HcTq2LwZ9mRk4VbN1sXe8Dy",
  "type": "invoice",
  "status": "pending",
  "amount": 48900,
  "amount_refunded": 0,
  "currency": "KZT",
  "description": "Доставка, заказ 5521",
  "phone": "77012345678",
  "external_id": "order_5521",
  "metadata": {
    "courier": "Алматы-3"
  },
  "checkout_url": "https://pay.sezgir.com/pay/pay_4Vb9Kq2Lm7Xc1Zt8Rn5Ws3Pd",
  "provider": {
    "name": "kaspi",
    "operation_id": "55120934",
    "kind": "invoice",
    "status": "RemotePaymentCreated",
    "qr_url": null,
    "qr_expires_at": null
  },
  "failure_reason": null,
  "requires_review": false,
  "expires_at": "2026-10-26T11:20:00.000Z",
  "paid_at": null,
  "canceled_at": null,
  "created_at": "2026-10-25T11:20:00.000Z"
}

Счёт действует 24 часа. Если по истечении срока он не оплачен, Sezgir Pay отменяет его в Kaspi и закрывает платёж со статусом expired. Без телефона или с неверным номером API вернёт 400 с param: "phone".

Жизненный цикл

Каждый платёж создаётся в статусе pending и переходит в один из итоговых статусов. Каждый переход сопровождается событием.

Жизненный цикл платежаоплаченочастичный возвратполный возвратпоздняя оплатаподтверждена Kaspipendingsucceededfailedexpiredcanceledpartially_refundedrefunded
Сплошные стрелки — обычные переходы. Пунктир — подтверждение оплаты от Kaspi, пришедшее после закрытия платежа.
statusЧто значитСобытие
pendingЖдём оплату. Для ссылки — покупатель ещё не оплатил ни одну попытку.payment.created
succeededKaspi подтвердил оплату, деньги на счёте кассира. Заполнено paid_at.payment.succeeded
failedKaspi отклонил оплату (qr, invoice). Причина — в failure_reason.payment.failed
expiredСрок оплаты истёк, и операция в Kaspi больше не может пройти.payment.expired
canceledВы отменили платёж через API или кабинет либо покупатель отказался от оплаты в Kaspi (qr, invoice).payment.canceled
partially_refundedЧасть суммы возвращена; amount_refunded < amount.payment.refunded
refundedСумма возвращена полностью.payment.refunded

Отклонённая попытка оплаты по ссылке не переводит её в failed — ссылка остаётся открытой, а причина последней неудачи видна в failure_reason. Если Kaspi отказал уже при создании qr или invoice, API отвечает ошибкой, платёж сохраняется в статусе failed для истории, а вебхуков по нему не будет.

Поздняя оплата

payment.succeeded может прийти в любой момент

Деньги, которые подтвердил Kaspi, — главный источник истины. Если подтверждение пришло после того, как платёж был закрыт как expired, canceled или failed, платёж переходит в succeeded и вы получаете payment.succeeded.

Так бывает, например, когда:

  • вы отменили QR-платёж, а покупатель уже отсканировал код — отозвать выданный QR в Kaspi нельзя, поэтому Sezgir Pay следит за ним до конца;
  • покупатель оплатил в последние секунды, и ответ Kaspi пришёл после истечения срока;
  • Kaspi временно не отвечал, а позже подтвердил оплату.

Что делать на своей стороне:

  1. Не удаляйте заказ и не считайте его окончательно отменённым только по payment.expired или payment.canceled.
  2. Обрабатывайте payment.succeeded для любого заказа: если товар уже недоступен, сделайте возврат.
  3. Не создавайте новый платёж для заказа, пока старый в статусе pending, — иначе покупатель может оплатить оба.

Флаг requires_review

Иногда Kaspi не отвечает в момент, когда деньги могут двигаться. Тогда Sezgir Pay не делает вид, что знает результат, а ставит платежу requires_review: true и пишет пояснение в failure_reason:

  • Счёт выставлен, но Kaspi не ответил. Счёт мог дойти до покупателя, поэтому платёж создаётся (201) и остаётся pending — не выставляйте второй счёт.
  • Срок истёк, но Kaspi сутки не подтверждал статус. Платёж закрывается как expired с флагом.

Администратор сверяет операцию в Kaspi Pay и снимает флаг в кабинете. Если оплата всё-таки прошла, вы получите payment.succeeded как обычно.

Страница оплаты

У каждого платежа любого типа есть checkout_url — страница Sezgir Pay вида https://pay.sezgir.com/pay/pay_…. На ней покупатель видит название и иконку сервиса, сумму и описание, а также:

typeЧто показывает страница
linkКнопку «Оплатить». На компьютере — QR для сканирования, на телефоне — кнопку «Открыть в Kaspi.kz» и таймер QR. Если QR истёк, можно получить новый.
qrТот же QR, что в provider.qr_url, и кнопку «Открыть в Kaspi.kz».
invoiceМаскированный номер, на который отправлен счёт, и подсказку, где его найти в Kaspi.kz.
  • Страница сама обновляет статус каждые несколько секунд и показывает результат: «Оплата прошла», «Время оплаты истекло», «Платёж отменён».
  • Метаданные, external_id и идентификаторы ваших объектов покупателю не показываются; номер телефона маскируется.
  • Тестовые платежи помечены плашкой «Тест» и содержат кнопки симуляции «Оплатить» и «Отклонить».
  • Возврата покупателя на ваш сайт после оплаты нет. Узнавайте о результате по вебхуку и показывайте покупателю статус заказа на своей стороне.

Отмена

Отменить можно только платёж в статусе pending: POST /payments/:id/cancel. Для счёта Sezgir Pay отменяет его в Kaspi, и покупатель больше не сможет его оплатить. Для уже выданного QR отмена в Kaspi невозможна — платёж становится canceled, но если покупатель всё же оплатит этот QR, вы получите payment.succeeded.

Отмена платежа
curl -X POST "https://pay.sezgir.com/api/v1/payments/pay_2Nf8kQ1xYbV7cR4tLm9Pa3Ws/cancel" \
  -H "Authorization: Bearer $SEZGIR_API_KEY"

Повторная отмена или отмена завершённого платежа вернёт 409 с кодом payment_not_pending. Если Kaspi недоступен в момент отмены счёта — 503 provider_unavailable, платёж остаётся pending; повторите запрос позже.

Поиск и сверка

GET /payments/:id возвращает актуальное состояние платежа, GET /payments — список с фильтрами status, type и external_id, от новых к старым.

curl "https://pay.sezgir.com/api/v1/payments?external_id=order_1042" \
  -H "Authorization: Bearer $SEZGIR_API_KEY"
Ответ · 200 OK
{
  "object": "list",
  "data": [
    {
      "id": "pay_2Nf8kQ1xYbV7cR4tLm9Pa3Ws",
      "object": "payment",
      "livemode": false,
      "project_id": "prj_7HcTq2LwZ9mRk4VbN1sXe8Dy",
      "type": "link",
      "status": "succeeded",
      "amount": 15000,
      "amount_refunded": 0,
      "currency": "KZT",
      "description": "Заказ №1042",
      "phone": null,
      "external_id": "order_1042",
      "metadata": {
        "order_id": 1042,
        "channel": "web"
      },
      "checkout_url": "https://pay.sezgir.com/pay/pay_2Nf8kQ1xYbV7cR4tLm9Pa3Ws",
      "provider": {
        "name": "test",
        "operation_id": null,
        "kind": null,
        "status": null,
        "qr_url": null,
        "qr_expires_at": null
      },
      "failure_reason": null,
      "requires_review": false,
      "expires_at": "2026-11-01T09:14:03.512Z",
      "paid_at": "2026-10-25T09:16:47.020Z",
      "canceled_at": null,
      "created_at": "2026-10-25T09:14:03.512Z"
    }
  ],
  "has_more": false
}

Не опрашивайте статус в цикле — дождитесь вебхука. Для периодической сверки используйте GET /events: события упорядочены по времени и не теряются, даже если ваш сервер был недоступен. Подробнее о пагинации — в справочнике. Базовый URL всех запросов — https://pay.sezgir.com/api/v1.