О насБлогКонтакты
Веб-разработка6 апреля 2026 г. 6 мин 78

Интеграция Kaspi Pay API для веб и мобильных приложений (2026)

AunimedaAunimeda
📋 Содержание

Kaspi Pay — доминирующий способ оплаты в Казахстане. Более 13 миллионов активных пользователей, 85%+ взрослого населения страны. Если вы создаёте продукт для казахстанского рынка, интеграция Kaspi Pay — это не опция, а необходимость. Это полное руководство по подключению Kaspi Pay к вашему проекту.


Обзор методов оплаты Kaspi

Kaspi предлагает несколько платёжных флоу в зависимости от вашего сценария:

Метод Сценарий использования Действие пользователя
QR-код Офлайн-магазины, счета Пользователь сканирует QR в приложении Kaspi
Deep Link Мобильные приложения Переход в приложение Kaspi
Платёжная ссылка Web, WhatsApp, SMS Пользователь нажимает ссылку → приложение Kaspi
eCommerce API Веб-касса Редирект на страницу оплаты Kaspi

Для большинства веб- и мобильных интеграций: используйте eCommerce API (флоу с редиректом) или платёжную ссылку.


Получение доступа к API

  1. Зарегистрируйтесь как мерчант на kaspi.kz/merchantapi
  2. Пройдите верификацию бизнеса (БИН/ИИН, банковский счёт в Казахстане)
  3. Получите: TradePointId, учётные данные API, доступ к тестовой среде

Тестовая среда: https://testpay.kaspi.kz
Продакшн: https://pay.kaspi.kz


Платёжный флоу (eCommerce API)

1. Ваш сервер создаёт заказ → отправляет в Kaspi API
2. Kaspi возвращает URL оплаты
3. Вы перенаправляете пользователя на этот URL (или открываете в WebView на мобильном)
4. Пользователь оплачивает в приложении Kaspi / веб-интерфейсе
5. Kaspi отправляет вебхук на ваш сервер
6. Ваш сервер подтверждает оплату → обновляет заказ

Шаг 1: Создание платёжного заказа

// Пример на Node.js
const axios = require('axios');
const crypto = require('crypto');

const KASPI_TRADE_POINT_ID = process.env.KASPI_TRADE_POINT_ID;
const KASPI_API_KEY = process.env.KASPI_API_KEY;
const BASE_URL = 'https://pay.kaspi.kz'; // или testpay.kaspi.kz

async function createKaspiOrder(orderData) {
  const payload = {
    amount: orderData.amount,          // В тенге, целое число (например, 5000 = 5000 ₸)
    orderId: orderData.orderId,        // Ваш уникальный ID заказа
    returnUrl: orderData.returnUrl,    // Редирект после оплаты
    failUrl: orderData.failUrl,        // Редирект при ошибке
    tradePointId: KASPI_TRADE_POINT_ID,
    description: orderData.description,
  };

  // Генерация подписи
  const signatureString = `${payload.amount}${payload.orderId}${KASPI_API_KEY}`;
  const signature = crypto
    .createHash('sha256')
    .update(signatureString)
    .digest('hex');

  const response = await axios.post(
    `${BASE_URL}/api/v1/orders/create`,
    { ...payload, signature },
    {
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${KASPI_API_KEY}`,
      },
    }
  );

  return response.data; // Содержит { paymentUrl, orderId }
}

// Использование в обработчике оформления заказа
app.post('/checkout', async (req, res) => {
  const { cartTotal, orderId } = req.body;

  const kaspiOrder = await createKaspiOrder({
    amount: Math.round(cartTotal),
    orderId: `ORDER-${orderId}`,
    returnUrl: `https://yoursite.kz/payment/success?orderId=${orderId}`,
    failUrl: `https://yoursite.kz/payment/fail?orderId=${orderId}`,
    description: `Заказ #${orderId}`,
  });

  // Перенаправляем пользователя на страницу оплаты Kaspi
  res.json({ paymentUrl: kaspiOrder.paymentUrl });
});

Шаг 2: Обработка вебхука

Kaspi отправляет POST-запрос на ваш URL вебхука при изменении статуса оплаты.

// Обработчик вебхука
app.post('/webhooks/kaspi', express.raw({ type: 'application/json' }), async (req, res) => {
  const payload = JSON.parse(req.body);

  // Проверка подписи
  const expectedSignature = crypto
    .createHash('sha256')
    .update(`${payload.orderId}${payload.amount}${KASPI_API_KEY}`)
    .digest('hex');

  if (payload.signature !== expectedSignature) {
    return res.status(400).json({ error: 'Неверная подпись' });
  }

  switch (payload.status) {
    case 'APPROVED':
      await db.orders.update({
        where: { kaspiOrderId: payload.orderId },
        data: { 
          status: 'paid',
          paidAt: new Date(),
          kaspiTransactionId: payload.transactionId,
        }
      });
      await fulfillOrder(payload.orderId);
      break;

    case 'DECLINED':
    case 'CANCELLED':
      await db.orders.update({
        where: { kaspiOrderId: payload.orderId },
        data: { status: 'cancelled' }
      });
      break;
  }

  // Всегда отвечайте 200, чтобы подтвердить получение
  res.status(200).json({ received: true });
});

Шаг 3: Проверка статуса оплаты (резервный polling)

Не полагайтесь только на вебхуки — они могут не дойти. Проверяйте статус на URL возврата:

async function checkKaspiOrderStatus(kaspiOrderId) {
  const response = await axios.get(
    `${BASE_URL}/api/v1/orders/${kaspiOrderId}/status`,
    {
      headers: { 'Authorization': `Bearer ${KASPI_API_KEY}` }
    }
  );

  return response.data.status; // 'APPROVED' | 'PENDING' | 'DECLINED' | 'CANCELLED'
}

// Обработчик URL возврата
app.get('/payment/success', async (req, res) => {
  const { orderId } = req.query;
  const order = await db.orders.findOne({ id: orderId });

  // Всегда верифицируйте — не доверяйте параметрам URL
  const kaspiStatus = await checkKaspiOrderStatus(order.kaspiOrderId);

  if (kaspiStatus === 'APPROVED') {
    res.render('success', { order });
  } else {
    res.render('pending', { order }); // Оплата может ещё обрабатываться
  }
});

Мобильная интеграция: Flutter Deep Link

Для мобильных приложений используйте deep link для прямого открытия приложения Kaspi:

// pubspec.yaml
dependencies:
  url_launcher: ^6.2.0

// payment_service.dart
import 'package:url_launcher/url_launcher.dart';

class KaspiPaymentService {
  /// Открывает приложение Kaspi для оплаты. Возвращает в ваше приложение через deeplink.
  Future<void> openKaspiPayment(String paymentUrl) async {
    final uri = Uri.parse(paymentUrl);
    
    if (await canLaunchUrl(uri)) {
      await launchUrl(uri, mode: LaunchMode.externalApplication);
    } else {
      // Приложение Kaspi не установлено — открываем веб-версию
      await launchUrl(
        Uri.parse(paymentUrl),
        mode: LaunchMode.inAppWebView,
      );
    }
  }
}

Обработка входящего deep link в приложении:

// В AndroidManifest.xml
// <intent-filter>
//   <action android:name="android.intent.action.VIEW" />
//   <data android:scheme="yourapp" android:host="payment" />
// </intent-filter>

// В Flutter-приложении
class PaymentResultScreen extends StatefulWidget {
  @override
  void initState() {
    super.initState();
    _handleIncomingLink();
  }

  void _handleIncomingLink() {
    // yourapp://payment?status=success&orderId=ORDER-123
    final uri = Uri.parse(widget.deepLink);
    final status = uri.queryParameters['status'];
    final orderId = uri.queryParameters['orderId'];
    
    if (status == 'success') {
      // Верифицируйте на сервере перед показом экрана успеха
      _verifyPayment(orderId);
    }
  }
}

Оплата через QR-код

Для розничной торговли или оплаты по счёту:

async function generateKaspiQR(amount, orderId) {
  const response = await axios.post(`${BASE_URL}/api/v1/qr/create`, {
    amount,
    orderId,
    tradePointId: KASPI_TRADE_POINT_ID,
    signature: generateSignature(amount, orderId),
  });

  // response.data.qrCode — QR-изображение в base64
  // response.data.qrToken — токен для проверки статуса оплаты
  return response.data;
}

// Polling статуса QR (пользователь ещё не подтвердил)
async function pollQRStatus(qrToken, maxAttempts = 60) {
  for (let i = 0; i < maxAttempts; i++) {
    await sleep(3000); // Проверяем каждые 3 секунды
    
    const status = await axios.get(`${BASE_URL}/api/v1/qr/${qrToken}/status`, {
      headers: { Authorization: `Bearer ${KASPI_API_KEY}` }
    });

    if (status.data.status === 'APPROVED') return 'paid';
    if (status.data.status === 'DECLINED') return 'failed';
    // 'PENDING' — продолжаем polling
  }
  return 'timeout';
}

Типичные ошибки и их решения

Код ошибки Значение Решение
INVALID_SIGNATURE Несовпадение подписи Проверьте порядок полей в строке подписи
ORDER_ALREADY_EXISTS Дублирующийся orderId Используйте уникальные ID (UUID или timestamp)
TRADE_POINT_NOT_FOUND Неверный TradePointId Проверьте переменную окружения
AMOUNT_TOO_SMALL Ниже минимума (100 ₸) Валидируйте сумму перед отправкой
INVALID_RETURN_URL URL не добавлен в белый список Зарегистрируйте URL возврата в портале мерчанта

Тестирование

В тестовой среде Kaspi:

  • Принимается любая сумма
  • Используйте endpoint testpay.kaspi.kz
  • Тестовое приложение Kaspi предоставляется отдельно (запросите у поддержки мерчантов)

Важно перед запуском в продакшн

  • Зарегистрируйте все домены returnUrl и failUrl в портале мерчанта до выхода в прод
  • Сохраняйте kaspiTransactionId из вебхука — потребуется для возвратов
  • API возврата: POST /api/v1/orders/{orderId}/refund с суммой и подписью
  • Ограничение запросов: 100 запросов в минуту на один TradePointId

Нужна помощь с интеграцией Kaspi Pay? Напишите в Aunimeda →


Aunimeda разрабатывает сайты и веб-приложения для бизнеса в Казахстане — корпоративные сайты, интернет-магазины, порталы и платформы под заказ.

Связаться с нами и обсудить проект. Смотрите также: Разработка интернет-магазина, Мобильные приложения в Алматы

Читайте также

Разработка сайта в Алматы 2026: цены, технологии и как выбрать студиюaunimeda
Веб-разработка

Разработка сайта в Алматы 2026: цены, технологии и как выбрать студию

Сколько стоит разработка сайта в Алматы в 2026 году? Сравниваем технологии, типы сайтов и даём практическое руководство по выбору IT-партнёра для бизнеса в Казахстане.

Node.js vs Bun vs Deno 2026: бенчмарки и выбор runtime для продакшнaunimeda
Веб-разработка

Node.js vs Bun vs Deno 2026: бенчмарки и выбор runtime для продакшн

Bun 1.x стабилен в production. Deno 2.0 поддерживает npm-пакеты. Node.js 22 запускает TypeScript нативно. Реальные бенчмарки производительности, сравнение инструментов и конкретные рекомендации для казахстанских разработчиков.

Чистая архитектура и DDD в Node.js: практическое руководство для productionaunimeda
Веб-разработка

Чистая архитектура и DDD в Node.js: практическое руководство для production

Clean Architecture + Domain-Driven Design в Node.js TypeScript - без академизма. Use cases, Domain Entities, Repository Pattern, Aggregate Root. Бизнес-логика изолирована от инфраструктуры - тестируется без базы данных. Рабочий код для production.

Нужна IT-разработка для вашего бизнеса?

Разрабатываем сайты, мобильные приложения и AI-решения для бизнеса в Казахстане. Бесплатная консультация.

Разработка сайтов

Получить консультацию Все статьи