Документация для мерчантов
Этот документ описывает интеграцию мерчанта с REST API шлюза OnePay V1: приём платежей, hosted checkout, возвраты, выплаты, сохранённые карты, подписки и webhook-уведомления.
Быстрый старт
- Получите у OnePay параметры мерчанта:
| Параметр | Где используется |
|---|---|
tenant_id | Идентификатор tenant в OnePay. |
merchant_id | Идентификатор мерчанта. |
profile_id | Профиль мерчанта: валюта, терминалы, routing rules, webhook URL. |
api_key_id | Заголовок X-Merchant-Key-Id. |
api_secret | Заголовок X-Merchant-Key-Secret; выдаётся один раз при подключении или ротации. |
webhook_signing_secret | Секрет для проверки webhook-подписей. |
Настройте URL для webhook-уведомлений на профиле через менеджера или backoffice. В create-запросах
webhook_urlне передаётся: шлюз берёт URL из профиля и сохраняет его на операции.Создайте платёж:
curl -X POST "$API_BASE_URL/v1/payments" \
-H "Content-Type: application/json" \
-H "X-Merchant-Key-Id: $API_KEY_ID" \
-H "X-Merchant-Key-Secret: $API_SECRET" \
-H "Idempotency-Key: order-10001-create" \
-d '{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"amount": 150000,
"currency": "KZT",
"payment_method": "card",
"merchant_order_id": "ORDER-10001",
"description": "Оплата заказа ORDER-10001",
"return_url": "https://merchant.example/orders/ORDER-10001/result"
}'Перенаправьте покупателя на
data.payment_page_url.Дождитесь webhook о терминальном статусе или проверьте платёж через
GET /v1/payments/{payment_id}.
Базовый URL и формат
Все запросы отправляются в JSON:
Content-Type: application/jsonПримеры ниже используют переменную:
API_BASE_URL=https://api.example.kzСуммы в KZT передаются в тиинах. Например 150000 означает 1 500.00 KZT.
Аутентификация
Для прямой серверной интеграции используйте API-ключи в заголовках:
| Header | Описание |
|---|---|
X-Merchant-Key-Id | Идентификатор ключа мерчанта. |
X-Merchant-Key-Secret | Секрет ключа мерчанта. |
Пример:
X-Merchant-Key-Id: key_live_xxx
X-Merchant-Key-Secret: secret_live_xxxПри этой схеме tenant_id и merchant_id передаются в теле POST-запросов или query-параметрах GET-запросов.
Идемпотентность
Для операций создания передавайте уникальный ключ:
Idempotency-Key: order-10001-createПравила:
| Сценарий | Результат |
|---|---|
| Тот же ключ и тот же payload | Возвращается ранее созданный ресурс. |
| Тот же ключ и другой payload | 409 idempotency_mismatch. |
| Новый бизнес-заказ | Используйте новый ключ. |
Ключ можно передать в заголовке Idempotency-Key или в JSON-поле idempotency_key. Заголовок имеет приоритет.
Стандартный формат ответа
Успешные ответы возвращаются в envelope:
{
"success": true,
"error_code": 0,
"message": "OK",
"data": {
"payment_id": 12345,
"status": "created"
}
}Ошибки возвращаются в общем формате:
{
"success": false,
"error_code": 400,
"message": "amount is required",
"message_key": "validation_error",
"data": {
"field": "amount"
}
}Типовые HTTP-статусы:
| HTTP | Когда возникает |
|---|---|
400 | Ошибка JSON или валидации. |
401 | Неверные или отсутствующие merchant credentials. |
404 | Ресурс не найден или недоступен мерчанту. |
409 | Конфликт статуса, лимиты, баланс, идемпотентность. |
502 | Коннектор банка временно недоступен. |
500 | Внутренняя ошибка шлюза. |
Приём платежа через hosted checkout
Поток
- Мерчант создаёт платёж через
POST /v1/payments. - Шлюз возвращает
payment_page_urlиclient_secret. - Покупатель открывает
payment_page_urlи вводит данные карты на стороне шлюза. - При необходимости покупатель проходит 3DS.
- Шлюз переводит платёж в терминальный статус и отправляет webhook.
- Покупатель возвращается на
return_url.
Создать платёж
POST /v1/payments
Обязательные поля:
| Поле | Тип | Описание |
|---|---|---|
tenant_id | string | Tenant мерчанта. |
merchant_id | string | Мерчант. |
profile_id | string | Профиль/терминальная конфигурация. |
amount | int64 | Сумма в тиинах. |
currency | string | Валюта, например KZT. |
payment_method | string | Обычно card; также возможны методы, разрешённые на профиле. |
return_url | string | Куда вернуть покупателя после оплаты. |
Опциональные поля:
| Поле | Тип | Описание |
|---|---|---|
merchant_order_id | string | ID заказа на стороне мерчанта. |
description | string | Описание платежа. |
customer | object | Данные клиента. Нужны для сохранения карты/мандата. |
save_card | boolean | Создать mandate для последующих списаний. |
mandate_id | string | Списать по ранее сохранённой карте. |
line_items | array | Корзина заказа. |
additional_charges_amount | int64 | Дополнительные сборы в тиинах. |
discount_amount | int64 | Скидка в тиинах. |
Пояснения по основным полям:
| Поле | Как заполнять |
|---|---|
amount | Передавайте сумму в тиинах. Например 150000 означает 1 500.00 KZT. |
currency | Используйте валюту, разрешённую на профиле. Обычно для Казахстана это KZT. |
payment_method | Для обычной карточной оплаты укажите card. Apple Pay и Google Pay подключаются отдельно как способы оплаты. |
merchant_order_id | Удобно передавать номер заказа из вашей системы, чтобы потом сопоставлять ответ и webhook. |
return_url | URL страницы, куда покупатель вернётся после оплаты или отказа. Финальный результат всё равно лучше фиксировать по webhook. |
customer | Данные клиента. Нужны, если вы хотите сохранить карту или связать платёж с конкретным клиентом. |
save_card | Если true, после успешной оплаты OnePay может создать mandate_id для последующих списаний. |
mandate_id | Используется только для списания по ранее сохранённой карте. Для первого платежа его не передают. |
line_items | Детализация корзины. Полезна для отображения состава заказа и сверки на стороне мерчанта. |
Пример запроса с комментариями:
{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"amount": 150000, // сумма в тиинах: 150000 = 1 500.00 KZT
"currency": "KZT",
"payment_method": "card", // обычная оплата банковской картой
"merchant_order_id": "ORDER-10001", // ID заказа в вашей системе
"description": "Оплата заказа ORDER-10001",
"return_url": "https://merchant.example/orders/ORDER-10001/result",
"customer": {
"customer_id": "customer-100", // ваш идентификатор клиента
"name": "Ivan Ivanov",
"email": "ivan@example.kz",
"phone": "+77010000000"
},
"save_card": false // true, если нужно сохранить карту после успешной оплаты
}Пример ответа:
{
"success": true,
"error_code": 0,
"message": "OK",
"data": {
"payment_id": 12345,
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"status": "created",
"amount": 150000,
"currency": "KZT",
"payment_method": "card",
"merchant_order_id": "ORDER-10001",
"client_secret": "sec_xxx",
"payment_page_url": "https://pay.example.kz/checkout/sec_xxx",
"redirect_url": "",
"created_at": "2026-08-17T10:00:00Z",
"updated_at": "2026-08-17T10:00:00Z"
}
}Что использовать из ответа:
| Поле | Назначение |
|---|---|
payment_id | ID платежа в OnePay. Используется для проверки статуса, отмены и сверки с webhook. |
status | Текущий статус платежа. После создания обычно created. |
client_secret | Технический ключ checkout-сессии. Не является merchant API-secret. |
payment_page_url | Готовая ссылка на hosted checkout. Перенаправьте на неё покупателя. |
redirect_url | Может быть заполнен при сценариях, где требуется внешнее действие, например 3DS. |
Проверить платёж
GET /v1/payments/{payment_id}?tenant_id=...&merchant_id=...
curl "$API_BASE_URL/v1/payments/12345?tenant_id=tenant-1&merchant_id=merchant-1" \
-H "X-Merchant-Key-Id: $API_KEY_ID" \
-H "X-Merchant-Key-Secret: $API_SECRET"Проверку можно использовать для ручной сверки или восстановления состояния, если webhook временно не был обработан. Основной источник автоматического обновления заказа на стороне мерчанта — webhook.
Статусы платежа
| Статус | Значение |
|---|---|
created | Платёж создан, покупатель ещё не завершил оплату. |
processing | Шлюз обрабатывает платёж. |
waiting_for_auth | Требуется внешняя аутентификация, например 3DS. |
pending | Ожидается финальный ответ банка. |
succeeded | Оплата успешна. |
failed | Оплата отклонена или завершилась ошибкой. |
refunded | Платёж полностью возвращён. |
Терминальные статусы для оплаты: succeeded, failed, refunded.
Server-to-server исполнение платежа
Если у мерчанта есть разрешение и PCI-контур, карту можно передать напрямую в POST /v1/payments/{payment_id}/execute.
curl -X POST "$API_BASE_URL/v1/payments/12345/execute" \
-H "Content-Type: application/json" \
-d '{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"client_secret": "sec_xxx",
"card": {
"pan": "4111111111111111",
"month": "12",
"year": "2030",
"holder": "IVAN IVANOV",
"cvv": "123"
},
"browser_info": {
"ip_address": "203.0.113.10",
"user_agent": "Mozilla/5.0",
"accept_header": "text/html,application/xhtml+xml",
"accept_language": "ru-KZ,ru;q=0.9",
"language": "ru",
"screen_width": 1440,
"screen_height": 900,
"color_depth": 24,
"time_zone_offset": -300,
"java_enabled": false,
"js_enabled": true
}
}'Пример ответа:
{
"success": true,
"error_code": 0,
"message": "OK",
"data": {
"payment_id": 12345,
"payment_status": "waiting_for_auth",
"attempt_id": 77,
"attempt_status": "waiting_for_auth",
"redirect_url": "https://api.example.kz/v1/payments/12345/3ds/start?attempt_id=77&client_secret=sec_xxx",
"rrn": "",
"external_reference": "",
"error_code": "",
"error_message": ""
}
}Если redirect_url заполнен, перенаправьте покупателя по этому URL.
3DS-страницы и возврат банка обрабатываются gateway endpoints: /v1/payments/{payment_id}/3ds/start и /v1/payments/{payment_id}/3ds/return. Обычно мерчант не вызывает их напрямую: покупатель переходит по redirect_url, а gateway завершает сценарий и затем возвращает покупателя на return_url.
Отмена платежа до финала
POST /v1/payments/{payment_id}/cancel
Отмена доступна для платежей в статусах created или waiting_for_auth. Это локальная отмена в шлюзе без вызова банка.
curl -X POST "$API_BASE_URL/v1/payments/12345/cancel" \
-H "Content-Type: application/json" \
-H "X-Merchant-Key-Id: $API_KEY_ID" \
-H "X-Merchant-Key-Secret: $API_SECRET" \
-H "Idempotency-Key: order-10001-cancel" \
-d '{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"reason": "buyer abandoned checkout"
}'Возврат платежа
POST /v1/refunds
Refund создаётся только для успешного платежа. Если amount не передан, шлюз пытается вернуть весь доступный остаток. Для частичного возврата передайте amount в тиинах.
curl -X POST "$API_BASE_URL/v1/refunds" \
-H "Content-Type: application/json" \
-H "X-Merchant-Key-Id: $API_KEY_ID" \
-H "X-Merchant-Key-Secret: $API_SECRET" \
-H "Idempotency-Key: refund-10001-1" \
-d '{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"payment_id": 12345,
"amount": 50000,
"reason": "partial return"
}'Пример ответа:
{
"success": true,
"error_code": 0,
"message": "OK",
"data": {
"refund_id": 9001,
"payment_id": 12345,
"payment_attempt_id": 77,
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"status": "succeeded",
"amount": 50000,
"currency": "KZT",
"reason": "partial return",
"connector_name": "halyk",
"connector_refund_id": "bank-ref-123",
"rrn": "123456789012",
"arn": "",
"external_reference": "9001",
"error_code": "",
"error_message": "",
"created_at": "2026-08-17T10:10:00Z",
"updated_at": "2026-08-17T10:10:03Z"
}
}Статусы refund: created, processing, pending, succeeded, failed. Терминальные: succeeded, failed.
Выплаты клиентам
Customer payout — выплата конечному получателю с баланса мерчанта. Это не то же самое, что settlement мерчанту.
Создать выплату
POST /v1/payouts
curl -X POST "$API_BASE_URL/v1/payouts" \
-H "Content-Type: application/json" \
-H "X-Merchant-Key-Id: $API_KEY_ID" \
-H "X-Merchant-Key-Secret: $API_SECRET" \
-H "Idempotency-Key: payout-20001" \
-d '{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"amount": 250000,
"currency": "KZT",
"destination_type": "card",
"destination_ref": "card_token_abc",
"recipient_name": "Ivan",
"recipient_surname": "Ivanov",
"description": "Customer refund to card"
}'Поля:
| Поле | Тип | Описание |
|---|---|---|
amount | int64 | Сумма выплаты в тиинах. |
currency | string | Валюта выплаты. |
destination_type | string | card или iban. |
destination_ref | string | Токен/референс карты или IBAN. Для card нельзя передавать raw PAN. |
recipient_name | string | Имя получателя, если требуется коннектором. |
recipient_surname | string | Фамилия получателя, если требуется коннектором. |
payout_cinfo | string | Дополнительная информация для payout-коннектора. |
description | string | Описание выплаты. |
Если доступного баланса недостаточно, API вернёт 409.
Получить выплату
GET /v1/payouts/{payout_id}?tenant_id=...&merchant_id=...
Статусы payout: created, processing, pending, succeeded, failed.
Batch-выплаты
POST /v1/payout-batches
curl -X POST "$API_BASE_URL/v1/payout-batches" \
-H "Content-Type: application/json" \
-H "X-Merchant-Key-Id: $API_KEY_ID" \
-H "X-Merchant-Key-Secret: $API_SECRET" \
-H "Idempotency-Key: batch-2026-08-17-1" \
-d '{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"items": [
{
"item_id": "item-1",
"item_idempotency_key": "batch-2026-08-17-1-item-1",
"amount": 100000,
"currency": "KZT",
"destination_type": "card",
"destination_ref": "card_token_1"
},
{
"item_id": "item-2",
"item_idempotency_key": "batch-2026-08-17-1-item-2",
"amount": 150000,
"currency": "KZT",
"destination_type": "iban",
"destination_ref": "KZ000000000000000000"
}
]
}'Статусы batch: created, processing, succeeded, failed, partially_succeeded.
Получить batch можно через GET /v1/payout-batches/{batch_id}?tenant_id=...&merchant_id=....
Сохранённые карты и рекурренты
Сохранить карту для повторных списаний
Создайте обычный платёж с save_card: true и заполненным customer. Покупатель проходит hosted checkout. После успешной оплаты в ответах и webhook может появиться mandate_id.
{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"amount": 10000,
"currency": "KZT",
"payment_method": "card",
"return_url": "https://merchant.example/cards/result",
"save_card": true,
"customer": {
"customer_id": "customer-100",
"name": "Ivan Ivanov",
"email": "ivan@example.kz",
"phone": "+77010000000"
}
}Пояснения:
| Поле | Назначение |
|---|---|
save_card | Просит OnePay сохранить карту после успешной оплаты. |
customer.customer_id | Идентификатор клиента в системе мерчанта. По нему удобно получать сохранённые карты. |
customer.email | Email клиента, если он есть в вашей системе. |
customer.phone | Телефон клиента в международном формате. |
mandate_id | Появляется после успешного сохранения карты и используется для будущих списаний. |
Список сохранённых карт
GET /v1/mandates?tenant_id=...&merchant_id=...&customer_id=...
Запрашивайте список по тому же customer_id, который передавали при сохранении карты. В ответе возвращаются доступные mandates клиента. Не храните карточные данные у себя: используйте только идентификаторы, которые вернул OnePay.
Отмена сохранённой карты
POST /v1/mandates/{mandate_id}/cancel?tenant_id=...&merchant_id=...
Платёж по сохранённой карте
Создайте платёж с payment_method: "mandate" и mandate_id.
{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"amount": 99000,
"currency": "KZT",
"payment_method": "mandate",
"mandate_id": "mandate_xxx",
"merchant_order_id": "ORDER-RECUR-1",
"return_url": "https://merchant.example/orders/ORDER-RECUR-1/result"
}Пояснения:
| Поле | Назначение |
|---|---|
payment_method | Для сохранённой карты укажите mandate. |
mandate_id | ID сохранённой карты, полученный после первого успешного платежа. |
merchant_order_id | Новый ID заказа или списания на стороне мерчанта. |
return_url | URL возврата, если платёж потребует дополнительного действия покупателя. |
Подписки
Подписка создаёт регулярные списания по mandate.
POST /v1/subscriptions
curl -X POST "$API_BASE_URL/v1/subscriptions" \
-H "Content-Type: application/json" \
-H "X-Merchant-Key-Id: $API_KEY_ID" \
-H "X-Merchant-Key-Secret: $API_SECRET" \
-H "Idempotency-Key: sub-customer-100-basic" \
-d '{
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"mandate_id": "mandate_xxx",
"amount": 299000,
"currency": "KZT",
"schedule": "monthly",
"customer_id": "customer-100",
"customer_email": "ivan@example.kz",
"customer_name": "Ivan Ivanov"
}'Основные endpoints:
| Метод | Путь | Назначение |
|---|---|---|
GET | /v1/subscriptions | Список подписок. |
GET | /v1/subscriptions/{subscription_id} | Получить подписку. |
POST | /v1/subscriptions/{subscription_id}/pause | Пауза. |
POST | /v1/subscriptions/{subscription_id}/resume | Возобновить. |
POST | /v1/subscriptions/{subscription_id}/cancel | Отменить. |
POST | /v1/subscriptions/{subscription_id}/cancel-options | Отменить с параметрами, например cancel_at_period_end. |
POST | /v1/subscriptions/{subscription_id}/change-plan | Сменить тарифный план. |
POST | /v1/subscriptions/{subscription_id}/charge | Ручное списание. |
GET | /v1/subscriptions/{subscription_id}/charges | Список списаний. |
POST | /v1/subscriptions/{subscription_id}/charges/{charge_id}/refund | Возврат списания. |
Доступные способы оплаты
GET /v1/payment-methods?tenant_id=...&merchant_id=...&profile_id=...
Ответ показывает методы, разрешённые текущими routing rules и настройками коннекторов:
Вызывайте этот endpoint перед показом способов оплаты, если на разных профилях или терминалах доступны разные методы. Например один профиль может принимать только card, а другой — card, applepay и googlepay.
{
"success": true,
"error_code": 0,
"message": "OK",
"data": {
"payment_methods": [
{ "type": "card", "enabled": true }
]
}
}Checkout endpoints для фронтенда
Hosted checkout использует browser-facing endpoints без merchant API-secret:
| Метод | Путь | Назначение |
|---|---|---|
GET | /v1/checkout/{client_secret} | HTML-страница checkout или JSON-сессия при Accept: application/json / ?format=json. |
POST | /v1/checkout/{client_secret}/cancel | Отмена checkout-сессии по client_secret, например при уходе покупателя назад в магазин. |
client_secret относится только к конкретному payment intent. Не передавайте merchant credentials в браузер.
Если вы используете готовую payment_page_url, напрямую вызывать эти endpoints обычно не нужно. JSON-режим GET /v1/checkout/{client_secret}?format=json полезен для собственной payment-page интеграции, где frontend мерчанта показывает состояние checkout-сессии, но не получает merchant API-secret.
Apple Pay и Google Pay
Apple Pay и Google Pay подключаются как способы оплаты внутри обычного pay-in flow. Сначала проверьте, что метод доступен через /v1/payment-methods, затем создайте платёж с payment_method: "applepay" или payment_method: "googlepay".
| Метод | Путь | Назначение |
|---|---|---|
POST | /v1/apple-pay/validate-merchant | Валидация Apple Pay merchant session; требует client_secret платежа с payment_method: "applepay". |
Apple Pay и Google Pay должны быть разрешены на профиле/маршрутизации. Если метод не настроен, endpoint может вернуть 404.
Google Pay
Google Pay ™ — это удобный и безопасный способ оплаты покупок с помощью карт, сохранённых в аккаунте Google. Покупатель может использовать Google Pay на сайтах, в приложениях и в других местах, где доступен этот способ оплаты.
Прежде чем интегрироваться с OnePay, ознакомьтесь с документацией Google:
- Виды интеграции и официальная документация Google
- Документация Google Pay для сайтов
- Контрольный список интеграции Google Pay для сайтов
- Правила фирменного оформления Google Pay для сайтов
- Google Pay & Wallet Console
- Правила допустимого использования Google Pay API
- Условия использования Google Pay API
Преимущества Google Pay ™
- Удобство: покупатель выбирает сохранённую карту и не вводит платёжные данные вручную при каждой покупке.
- Скорость: оплата проходит быстрее, так как нужные данные уже доступны в Google Pay.
- Безопасность: Google Pay использует токенизацию, шифрование и дополнительные механизмы аутентификации для защиты платёжных данных.
- Широкая поддержка: Google Pay можно использовать на сайтах и в приложениях, где подключён этот способ оплаты.
Как работает Google Pay ™
NFC-технология
Google Pay поддерживает бесконтактную оплату с помощью NFC на совместимых устройствах. Покупатель может подтвердить оплату на устройстве и оплатить покупку без передачи реквизитов карты продавцу.
Онлайн-покупки
Google Pay также используется для онлайн-платежей. При оформлении заказа на сайте покупатель выбирает Google Pay как способ оплаты и завершает покупку без ручного ввода карточных данных.
Совместимость
Google Pay доступен на поддерживаемых устройствах и в поддерживаемых браузерах. Перед запуском оплаты на сайте необходимо следовать требованиям Google к web-интеграции, тестированию и фирменному оформлению кнопки оплаты.
Что требуется для подключения
Для подключения Google Pay на платёжной странице необходимо уведомить менеджера OnePay. Менеджер проконсультирует по требованиям и дальнейшим шагам. Включение Google Pay выполняется на стороне эквайринга и платёжной страницы. С помощью Google Pay доступен приём платежей через платёжную страницу.
SCA и PSD2
Google Pay поддерживает токенизированный сценарий оплаты, который обычно является предпочтительным. В большинстве случаев эмитенты принимают такой сценарий как соответствующий требованиям строгой аутентификации клиента.
Все мерчанты должны соблюдать правила допустимого использования Google Pay API и условия использования Google Pay API. Google Pay является товарным знаком Google LLC.
Apple Pay
Apple Pay — это система онлайн-платежей Apple для оплаты на сайтах и в приложениях. Для web-интеграции используется Apple Pay on the Web.
Перед интеграцией ознакомьтесь с документацией Apple:
- Apple Pay on the Web
- Providing Merchant Validation
- Apple Pay on the Web troubleshooting guide
- Human Interface Guidelines: Apple Pay
Для Apple Pay домен, где показывается кнопка оплаты, должен быть зарегистрирован и верифицирован. Merchant validation выполняется через POST /v1/apple-pay/validate-merchant.
Полученный Apple Pay payment token необходимо передавать в поле apple_pay_token.
Webhook-уведомления
Webhook URL задаётся на профиле мерчанта. При создании платежа, payout или подписки шлюз сохраняет текущий URL на операции, поэтому последующая смена URL не меняет адрес доставки для уже созданных операций.
Stable формат
Заголовки:
| Header | Описание |
|---|---|
X-Webhook-Id | ID события. |
X-Webhook-Timestamp | Unix timestamp в секундах. |
X-Webhook-Attempt | Номер попытки доставки. |
X-Signature | Hex HMAC-SHA256 подпись. |
Подпись считается так:
hex(HMAC_SHA256(webhook_signing_secret, "<event_id>.<timestamp>.<raw_body>"))Пример проверки на Node.js:
import crypto from "node:crypto";
export function verifyOnePayWebhook({ secret, eventId, timestamp, rawBody, signature }) {
const payload = `${eventId}.${timestamp}.${rawBody}`;
const expected = crypto.createHmac("sha256", secret).update(payload).digest("hex");
return crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
}Пример тела payment webhook:
{
"event_id": "evt_123",
"event_type": "payment.succeeded",
"payment_id": 12345,
"tenant_id": "tenant-1",
"merchant_id": "merchant-1",
"profile_id": "profile-1",
"status": "succeeded",
"amount": 150000,
"currency": "KZT",
"merchant_order_id": "ORDER-10001",
"rrn": "123456789012",
"external_reference": "bank-operation-123",
"created_at": "2026-08-17T10:00:00Z",
"updated_at": "2026-08-17T10:02:00Z"
}Пояснения по полям webhook:
| Поле | Назначение |
|---|---|
event_id | Уникальный ID события. Используйте его для идемпотентной обработки webhook. |
event_type | Тип события: например payment.succeeded или payment.failed. |
payment_id | ID платежа в OnePay. По нему можно запросить актуальное состояние платежа. |
status | Статус платежа на момент отправки события. |
merchant_order_id | ID заказа из вашего create-запроса. По нему удобно обновлять заказ в вашей системе. |
rrn | Банковский reference number, если он доступен после обработки платежа. |
external_reference | Идентификатор операции на стороне внешнего коннектора или банка, если доступен. |
created_at | Когда платёж был создан. |
updated_at | Когда платёж был обновлён до текущего состояния. |
Типовые события:
| Событие | Когда отправляется |
|---|---|
payment.succeeded | Платёж успешно завершён. |
payment.failed | Платёж завершён ошибкой. |
payment.refunded | Платёж полностью возвращён. |
refund.created | Возврат создан. |
refund.succeeded | Возврат успешен. |
refund.failed | Возврат неуспешен. |
payout.created | Выплата создана. |
payout.succeeded | Выплата успешна. |
payout.failed | Выплата неуспешна. |
payout_batch.succeeded | Все элементы batch-выплаты успешны. |
payout_batch.partially_succeeded | У batch есть успешные и неуспешные элементы. |
subscription.* | События жизненного цикла подписок. |
Мерчант должен отвечать на webhook HTTP 2xx. При ошибке доставки шлюз повторяет отправку по retry-политике. Повтор webhook не означает повторную платёжную операцию; обработчик мерчанта должен быть идемпотентным по X-Webhook-Id.
Рекомендуемая обработка webhook на стороне мерчанта:
- Сохраните
X-Webhook-Idи проверьте, не обрабатывали ли это событие раньше. - Проверьте подпись
X-Signatureпо raw body запроса. - Найдите заказ по
merchant_order_idилиpayment_id. - Обновите заказ только если webhook пришёл с финальным статусом.
- Верните HTTP
2xx, когда событие успешно сохранено или уже было обработано.
Тестовая интеграция
Рекомендуемый checklist:
| Сценарий | Что проверить |
|---|---|
| Успешный pay-in | Создание платежа, переход на checkout, payment.succeeded, webhook. |
| Отказ pay-in | Терминальный failed, корректный error_code. |
| 3DS | waiting_for_auth, redirect URL, возврат из 3DS, финальный webhook. |
| Повтор create | Тот же Idempotency-Key не создаёт второй платёж. |
| Refund | Частичный и полный возврат, refund.succeeded, payment.refunded. |
| Payout | Успех, отказ, недостаточный баланс. |
| Webhook retry | Повторная доставка не дублирует заказ на стороне мерчанта. |
Для Halyk sandbox в локальных окружениях убедитесь, что контейнер gateway имеет системные CA-сертификаты; иначе HTTPS-вызовы к sandbox могут падать на проверке сертификата.
Отличия от некоторых PSP-документаций
В некоторых платёжных системах callback URL и параметры подписи передаются в каждом create-запросе. В OnePay V1:
- merchant API использует заголовки
X-Merchant-Key-IdиX-Merchant-Key-Secret, а не HMAC-подпись каждого запроса; - webhook URL управляется на профиле, а не в теле операции;
- суммы в KZT передаются в тиинах;
Idempotency-Keyобязателен для безопасных повторов create/refund/payout операций;- hosted checkout является рекомендуемым способом приёма карточных данных.