Payment Integration
Как принимать оплаты через NurCore — провайдеры, webhooks, refunds.
Два способа интеграции
Есть два сценария, под которые мы поддерживаем разные API:
- NurCore выбирает PSP — вы вызываете
POST /bookings/{id}/initiate-payment, мы создаём checkout-сессию у Freedom Pay / Stripe, возвращаем вамpayment_url. Пассажир оплачивает на странице провайдера, провайдер шлёт нам webhook, мы переводим бронь вCONFIRMED. Описано ниже. - У вас свой PSP — вы (или ваше мобильное приложение) сами проводите
оплату через Kaspi / MBank / любого другого эквайера, а потом
сообщаете NurCore «оплата прошла» через
POST /payments/webhooks/external. См. раздел Использовать свой PSP ниже.
Поддерживаемые провайдеры
NurCore интегрирован с несколькими платёжными провайдерами. Конкретный выбирается автоматически на основе валюты брони и региона.
| Провайдер | Регион | Валюты |
|---|---|---|
| Freedom Pay | KG / международные карты | KGS, USD, EUR, RUB |
| Bank transfer | All | All (manual confirmation) |
Flow
1. Mobile / Website → POST /bookings/{id}/initiate-payment
2. NurCore → провайдер → создаёт checkout session
3. NurCore → ваш UI {payment_url, expires_at}
4. UI → redirect на payment_url
5. Пассажир оплачивает на странице провайдера
6. Провайдер → webhook → NurCore
7. NurCore → broня переходит в CONFIRMED, отправляется email
с magic-link и e-ticketЗапрос инициации
curl -X POST \
-H "X-API-Key: $KEY" \
-H "X-Client-Id: $CLIENT_ID" \
-H "X-Client-Type: consumer_app" \
-H "Content-Type: application/json" \
-d '{
"amount": 5000.00,
"currency": "KGS",
"return_url": "https://your-site.com/payment-result"
}' \
"https://api.nurcore.kg/api/v1/bookings/$BOOKING_ID/initiate-payment"Response:
{
"payment_id": "uuid",
"payment_url": "https://provider.com/checkout/...",
"redirect_url": "https://provider.com/checkout/...",
"expires_at": "2026-04-28T12:30:00Z"
}Return URL
Передавайте return_url — после оплаты пассажир будет редиректнут
обратно на ваш сайт с query-параметрами:
?status=success&booking_id=uuid— оплата прошла?status=failed&booking_id=uuid&reason=...— отказ?status=cancelled&booking_id=uuid— пользователь закрыл checkout
Не полагайтесь только на return URL — webhook от провайдера обрабатывается независимо и является source of truth.
Webhooks для B2B-партнёров
B2B-партнёры могут регистрировать webhook URL для получения уведомлений:
| Event | Когда |
|---|---|
booking.confirmed | После успешной оплаты |
booking.cancelled | При отмене (вашей или пассажиром) |
booking.refunded | При возврате |
flight.delayed | Задержка ≥ 30 минут на одном из рейсов броней |
flight.cancelled | Отмена рейса (рекомендация — авто-rebook) |
Webhook payload подписан HMAC-SHA256 — проверяйте подпись через
заголовок X-NurCore-Signature со shared secret.
Multi-currency (B2B)
Партнёры с агентским балансом могут платить с любого из своих
кошельков. Если у партнёра нет wallet в нужной валюте — pay-with-balance
вернёт 422 "Insufficient balance for currency XYZ".
Refund
curl -X POST \
-H "X-API-Key: $KEY" \
-H "X-Client-Id: $CLIENT_ID" \
-H "X-Client-Type: agency" \
-d '{"amount": 1500, "reason": "Cancellation"}' \
"https://api.nurcore.kg/api/v1/payments/$PAYMENT_ID/refund"Refund инициируется в провайдере. Сроки зачисления:
- Freedom Pay: 3-7 рабочих дней (зависит от банка-эмитента)
- Bank transfer: 7-14 рабочих дней
- Pay-with-balance: instant зачисление на partner wallet
Использовать свой PSP
Если вы хотите проводить оплату через свой платёжный шлюз (например,
национальный эквайер Kaspi/MBank/Elsom для KG-аудитории, или собственная
B2B-интеграция), используйте endpoint POST /payments/webhooks/external.
Flow
1. Пассажир в вашем приложении нажимает «Оплатить»
2. Вы создаёте бронь в NurCore:
POST /api/v1/bookings/ (с вашим X-API-Key) → booking_id
3. Ваш backend инициирует платёж в вашем PSP
4. PSP возвращает успех
5. Ваш backend сообщает NurCore:
POST /api/v1/payments/webhooks/external (с вашим X-API-Key)
6. NurCore: Payment.COMPLETED → booking.PAID + CONFIRMED → email пассажируАутентификация
Используйте свой consumer-app secret key (sk_live_…) — тот же, что и
для создания бронирований. Ключу должен быть назначен scope
payments:confirm-external (выдаётся через ваш контакт в авиакомпании,
один раз на ключ).
curl -X POST https://api.nurcore.kg/api/v1/payments/webhooks/external \
-H "Content-Type: application/json" \
-H "X-API-Key: sk_live_xxxxxxxxxxxxxxxxx" \
-d '{
"booking_id": "11111111-1111-1111-1111-111111111111",
"amount": 5000.00,
"currency": "KGS",
"provider": "kaspi",
"transaction_id": "kaspi-ref-12345",
"payment_method": "card"
}'Требования к ключу:
key_type = secret(sk_live_*илиsk_test_*, не publishablepk_*);client_type = consumer_app;- В списке scopes —
payments:confirm-external.
Дополнительно сервис проверяет ownership брони: бронь должна быть создана этим же ключом. Партнёр A не может подтвердить оплату брони партнёра B — это намеренная изоляция.
Брони, оформленные до 2026-05-28, не содержат origin-tracking — для них подтверждение оплаты через этот endpoint недоступно; обратитесь к support через ваш контакт в авиакомпании.
Идемпотентность
Уникальность платежа — provider_transaction_id. Если ваш PSP возвращает
один и тот же ID на retry — повторный вызов webhook вернёт
{ "status": "already_processed", payment_id } (200) и не списывает
повторно.
Рекомендуем выставлять transaction_id = ваш PSP transaction_id
(а не наш booking_id) — это позволяет нам различать разные платежи
по одной броне (например, после отмены и новой попытки оплаты с другой
карты).
Response
| Код | Тело | Значение |
|---|---|---|
| 200 | {status: "created", payment_id} | Новый платёж принят |
| 200 | {status: "already_processed", payment_id} | Идемпотент — этот transaction_id уже обработан |
| 200 | {status: "updated", payment_id} | Был старый платёж с тем же transaction_id в нетерминальном статусе — поднят до COMPLETED |
| 400 | {detail: "Invalid booking_id format (must be UUID)"} | booking_id не UUID |
| 403 | {detail: "..."} | Невалидный api-key / нет scope / чужая бронь / legacy-бронь без origin-tracking |
| 404 | {detail: "Бронирование не найдено"} | Бронь не существует |
| 503 | {detail: "..."} | Сервис временно недоступен — повторите с экспоненциальной задержкой |
HMAC-подпись (опционально)
Если вы хотите добавить ещё один слой защиты, мы можем включить
проверку X-Signature: HMAC-SHA256(client_secret, raw_body). Секреты
выдаются по запросу через support, сторона NurCore включает флаг
EXTERNAL_PAYMENT_REQUIRE_HMAC=true. По умолчанию выключено — HTTPS
- per-client api-key + ownership-чек уже дают три слоя.
Тестирование
Sandbox-окружение и тестовые credentials выдаются при подписании
интеграционного соглашения. Свяжитесь с partners@nurcore.kg.