NurCore API
Guides

Payment Integration

Как принимать оплаты через NurCore — провайдеры, webhooks, refunds.

Два способа интеграции

Есть два сценария, под которые мы поддерживаем разные API:

  1. NurCore выбирает PSP — вы вызываете POST /bookings/{id}/initiate-payment, мы создаём checkout-сессию у Freedom Pay / Stripe, возвращаем вам payment_url. Пассажир оплачивает на странице провайдера, провайдер шлёт нам webhook, мы переводим бронь в CONFIRMED. Описано ниже.
  2. У вас свой PSP — вы (или ваше мобильное приложение) сами проводите оплату через Kaspi / MBank / любого другого эквайера, а потом сообщаете NurCore «оплата прошла» через POST /payments/webhooks/external. См. раздел Использовать свой PSP ниже.

Поддерживаемые провайдеры

NurCore интегрирован с несколькими платёжными провайдерами. Конкретный выбирается автоматически на основе валюты брони и региона.

ПровайдерРегионВалюты
Freedom PayKG / международные картыKGS, USD, EUR, RUB
Bank transferAllAll (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_*, не publishable pk_*);
  • 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.

On this page