Документация · KAPSULA
Подключить

Документация KAPSULA

Платёжный шлюз для приёма платежей через СБП на сайтах и в ботах. Полное руководство — от регистрации до интеграции и проверки подписи webhook'ов.

О платформе

KAPSULA — платёжный посредник между вами (мерчантом) и платёжной системой. Мы упрощаем интеграцию: вместо того чтобы проходить верификацию у банка-эквайера месяцами, вы регистрируетесь у нас за 5 минут, проходите модерацию и принимаете оплаты.

Сейчас поддерживается:

В планах: банковские карты (когда будут пройдены требования PCI DSS).

💡 Базовый URL API: https://api.kapsula.pro/v1

Быстрый старт

Минимальная интеграция — 5 шагов и около 10 минут.

  1. Зарегистрируйтесьkapsula.pro/auth
  2. Создайте кассу в кабинете → Кассы. Укажите сайт или бота
  3. Пройдите верификацию (для сайтов — загрузка HTML-файла) и дождитесь модерации
  4. Получите API-ключ вида pk_live_... на странице кассы
  5. Сделайте первый запрос:
    curl -X POST https://api.kapsula.pro/v1/payment/create \
      -H "Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" \
      -H "Content-Type: application/json" \
      -d '{ "amount": 50000, "order_id": "test-1", "mode": "hosted" }'
    В ответе придёт payment_url — откройте его в браузере и протестируйте оплату с телефона.

Регистрация

Откройте страницу авторизации и заполните форму:

Подтверждение по email сейчас не требуется — после регистрации вы сразу попадаете в кабинет.

Создание кассы

«Касса» — это отдельная точка приёма платежей: один сайт или один бот. У вас может быть сколько угодно касс.

Перейдите в Кассы → Создать кассу. Заполните 2 шага:

Шаг 1. Тип кассы

Шаг 2. Реквизиты

Верификация сайта

Чтобы подтвердить что вы владеете сайтом — нужно загрузить наш файл в его корень.

  1. В кабинете кассы нажмите «Скачать файл верификации» — скачается kapsula-verify-<токен>.html
  2. Загрузите файл в корень вашего сайта так чтобы он открывался по адресу: https://ваш-сайт.com/kapsula-verify-<токен>.html
  3. В кабинете нажмите «Проверить» — мы скачаем файл и убедимся что токен совпадает
  4. После успешной проверки касса автоматически уйдёт на модерацию
⚠ Для ботов верификация не требуется — статус сразу pending_moderation.

Модерация

После верификации модератор проверяет вашу кассу: законность деятельности, соответствие правилам платёжной системы, наличие политики конфиденциальности и оферты на сайте.

API-ключи

После одобрения кассы в её карточке появятся два ключа:

КлючПрефиксНазначение
API-ключpk_live_Авторизация запросов от вашего сервера к нашему API
Webhook secretwhsec_Проверка подписи входящих webhook-уведомлений

Оба ключа можно пересоздать в любой момент — старые сразу перестают работать.

Безопасность

Платёжная система обрабатывает деньги — безопасность критична. Минимальные правила:

1. Хранение ключей

2. HTTPS обязателен

3. Проверка подписи webhook

Каждый webhook от нас подписан HMAC-SHA256. Всегда проверяйте подпись — иначе злоумышленник может прислать вам поддельное «уведомление об оплате» и вы выдадите товар бесплатно.

Подробности с примерами на разных языках — в разделе Подпись webhook.

4. Идемпотентность

Webhook может прийти несколько раз для одного и того же события (например, при сбое сети мы повторим). На вашей стороне используйте payment.id для дедупликации — если уже обработали, не выдавайте товар повторно.

5. Сверяйте сумму

Перед выдачей товара в обработчике webhook'а проверяйте что payment.amount совпадает с ожидаемой суммой заказа. Это защищает от ситуации когда ваш сервис создал заказ на 1000 ₽, а кто-то заплатил 500 ₽.

6. Сверяйте order_id

Используйте поле order_id чтобы привязать платёж к вашему заказу. В webhook'е оно вернётся в payment.order_id. Это защищает от подмены.

Авторизация

Все запросы к публичному API требуют заголовок:

Authorization: Bearer pk_live_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Если ключ невалиден или касса не активна — вернём 401 или 403.

Жизненный цикл платежа

[Ваш сервер] [KAPSULA] [6Tech / СБП] │ │ │ │ POST /payment/create │ │ ├─────────────────────────────────>│ │ │ │ POST /v1/payment/pay │ │ ├─────────────────────────────>│ │ │<──── request_uuid ───────────┤ │ │<──── callback (QR данные) ───┤ │<─── 200 + payment_url / qr ──────┤ │ │ │ │ │ [Покупатель сканирует QR] │ │ │ │ │ │<──── callback (Success) ─────┤ │<─── webhook payment.success ─────┤ │ │ │ │ ▼ ▼ ▼

Режим Hosted (рекомендуется)

Самый простой способ. Покупатель видит страницу оплаты на нашем домене (pay.kapsula.pro/p/...) — мы обеспечиваем UI, QR, обработку статусов.

  1. Создаёте платёж с mode: "hosted"
  2. В ответе получаете payment_url
  3. Перенаправляете покупателя туда (302 Redirect или открываете в новом окне)
  4. После оплаты мы:
    • Шлём webhook на ваш webhook_url
    • Перенаправляем покупателя на ваш success_url (или fail_url при отказе)
✅ Hosted-режим не требует реализации UI оплаты на вашей стороне — идеально для быстрого старта.

Режим Host (свой UI)

Если нужно полностью кастомное оформление — используйте режим host. Мы вернём вам данные QR, дальше вы рисуете и обрабатываете всё сами.

  1. Создаёте платёж с mode: "host"
  2. В ответе получаете qr.data (PNG в base64) и qr.link (deeplink на qr.nspk.ru)
  3. Рисуете QR на своей странице:
    <img src="data:image/png;base64,..." alt="СБП QR">
  4. Опционально — кнопка «Открыть в банке» с qr.link (для оплаты с того же телефона)
  5. Опрашиваете GET /payment/:id/status каждые 2–3 секунды или просто ждёте webhook

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

POST /api/v1/payment/create

Параметры запроса

ПараметрТипОбяз.Описание
amountintegerдаСумма в копейках. От 50000 (500 ₽) до 3000000 (30 000 ₽)
currencystringнетТолько RUB (по умолчанию)
methodstringнетТолько sbp (по умолчанию)
modestringнетhosted (по умолчанию) или host
order_idstringнетВаш ID заказа (до 128 символов). Вернётся в webhook'е
descriptionstringнетОписание (до 500 символов). Видит покупатель
customer_emailstringнетEmail покупателя (для будущих чеков)
success_urlstringнетOverride для hosted-режима
fail_urlstringнетOverride для hosted-режима

Примеры запроса

curl -X POST https://api.kapsula.pro/v1/payment/create \
  -H "Authorization: Bearer $KAPSULA_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "amount": 50000,
    "order_id": "order_12345",
    "description": "Подписка Premium на месяц",
    "mode": "hosted",
    "customer_email": "user@example.com"
  }'
// Node.js (fetch — встроен в Node 18+)
const res = await fetch('https://api.kapsula.pro/v1/payment/create', {
  method: 'POST',
  headers: {
    'Authorization': `Bearer ${process.env.KAPSULA_API_KEY}`,
    'Content-Type': 'application/json',
  },
  body: JSON.stringify({
    amount: 50000,
    order_id: order.id,
    description: 'Подписка Premium',
    mode: 'hosted',
    customer_email: order.customerEmail,
  }),
});
if (!res.ok) throw new Error('Kapsula error: ' + await res.text());
const payment = await res.json();
// Сохраните payment.id у себя для последующего матчинга с webhook
await db.orders.update(order.id, { kapsula_payment_id: payment.id });
return res.redirect(payment.payment_url);
<?php
$ch = curl_init('https://api.kapsula.pro/v1/payment/create');
curl_setopt_array($ch, [
  CURLOPT_RETURNTRANSFER => true,
  CURLOPT_POST => true,
  CURLOPT_HTTPHEADER => [
    'Authorization: Bearer ' . getenv('KAPSULA_API_KEY'),
    'Content-Type: application/json',
  ],
  CURLOPT_POSTFIELDS => json_encode([
    'amount' => 50000,
    'order_id' => $order_id,
    'description' => 'Подписка Premium',
    'mode' => 'hosted',
  ]),
]);
$resp = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code !== 200) {
    error_log("Kapsula error: $resp");
    die('Payment error');
}
$payment = json_decode($resp, true);
header('Location: ' . $payment['payment_url']);
exit;
import os, requests

def create_kapsula_payment(amount_kop: int, order_id: str, description: str = ''):
    r = requests.post(
        'https://api.kapsula.pro/v1/payment/create',
        headers={
            'Authorization': f"Bearer {os.environ['KAPSULA_API_KEY']}",
            'Content-Type': 'application/json',
        },
        json={
            'amount': amount_kop,
            'order_id': order_id,
            'description': description,
            'mode': 'hosted',
        },
        timeout=15,
    )
    r.raise_for_status()
    return r.json()

# использование во Flask:
@app.route('/checkout/<order_id>')
def checkout(order_id):
    order = Order.get(order_id)
    payment = create_kapsula_payment(int(order.amount * 100), order.id, order.title)
    order.kapsula_payment_id = payment['id']
    order.save()
    return redirect(payment['payment_url'])
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "net/http"
    "os"
)

type CreateReq struct {
    Amount      int    `json:"amount"`
    OrderID     string `json:"order_id"`
    Description string `json:"description"`
    Mode        string `json:"mode"`
}
type CreateResp struct {
    ID         string `json:"id"`
    PaymentURL string `json:"payment_url"`
}

func createPayment(amount int, orderID string) (*CreateResp, error) {
    body, _ := json.Marshal(CreateReq{amount, orderID, "", "hosted"})
    req, _ := http.NewRequest("POST", "https://api.kapsula.pro/v1/payment/create", bytes.NewReader(body))
    req.Header.Set("Authorization", "Bearer "+os.Getenv("KAPSULA_API_KEY"))
    req.Header.Set("Content-Type", "application/json")
    resp, err := http.DefaultClient.Do(req)
    if err != nil { return nil, err }
    defer resp.Body.Close()
    if resp.StatusCode != 200 { return nil, fmt.Errorf("kapsula: HTTP %d", resp.StatusCode) }
    var p CreateResp
    json.NewDecoder(resp.Body).Decode(&p)
    return &p, nil
}

Ответ — Hosted

{
  "id": "pay_a1b2c3d4e5f6789012345678abcdef01",
  "status": "awaiting_confirmation",
  "amount": 50000,
  "currency": "RUB",
  "method": "sbp",
  "mode": "hosted",
  "payment_url": "https://pay.kapsula.pro/p/pay_a1b2c3d4e5f6789012345678abcdef01",
  "created_at": 1714478400000,
  "expires_at": 1714480200000
}

Ответ — Host

{
  "id": "pay_a1b2c3d4e5f6789012345678abcdef01",
  "status": "awaiting_confirmation",
  "amount": 50000,
  "currency": "RUB",
  "method": "sbp",
  "mode": "host",
  "qr": {
    "data": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAA...",
    "link": "https://qr.nspk.ru/AS10000XXXXXXXXX?type=02&bank=1000..."
  },
  "qr_pending": false,
  "created_at": 1714478400000,
  "expires_at": 1714480200000
}
💡 Если qr_pending: true — QR ещё не пришёл от платёжной системы за 8 секунд (бывает редко). В этом случае дёрните GET /payment/:id через 1–2 секунды.

Получение информации о платеже

GET /api/v1/payment/:id

Полная информация о платеже.

curl https://api.kapsula.pro/v1/payment/pay_a1b2c3d4e5f6... \
  -H "Authorization: Bearer $KAPSULA_API_KEY"
GET /api/v1/payment/:id/status

Лёгкий запрос для polling — только статус и время оплаты:

{ "status": "success", "paid_at": 1714478560000 }

Статусы платежа

СтатусФинальный?Описание
pendingНетСоздан, ждём ответа от шлюза
awaiting_confirmationНетQR выдан, ждём оплату
successДаПлатёж успешно прошёл
declineДаОтклонён банком
expiredДаQR истёк (30 минут не было оплаты)
errorДаВнутренняя ошибка

Webhook-уведомления

При смене статуса на финальный (success, decline, expired, error) — мы шлём POST на ваш webhook_url (настраивается в кабинете кассы).

Структура события

POST https://your-site.com/your/webhook/path
Content-Type: application/json
X-Kapsula-Signature: 4a8b2d6e9f0a1b3c5d7e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c

{
  "event": "payment.success",
  "payment": {
    "id": "pay_a1b2c3d4e5f6789012345678abcdef01",
    "order_id": "order_12345",
    "status": "success",
    "amount": 50000,
    "currency": "RUB",
    "method": "sbp",
    "paid_at": 1714478560000,
    "error_code": null,
    "error_message": null,
    "created_at": 1714478400000
  },
  "timestamp": 1714478561234
}

События

Проверка подписи webhook

Обязательно проверяйте подпись. Без проверки злоумышленник может прислать поддельный webhook и обмануть вашу систему.

Алгоритм: HMAC-SHA256(webhook_secret, raw_body). Подпись приходит в hex в заголовке X-Kapsula-Signature.

Важно: подписывается сырое тело запроса как байты, до парсинга JSON. Иначе malformed JSON или whitespace-различия сломают проверку.

const express = require('express');
const crypto = require('crypto');
const app = express();

// ВАЖНО: для проверки подписи нужно raw body, не parsed JSON
app.post('/kapsula/webhook',
  express.raw({ type: 'application/json' }),
  (req, res) => {
    const sig = req.headers['x-kapsula-signature'];
    const expected = crypto
      .createHmac('sha256', process.env.KAPSULA_WEBHOOK_SECRET)
      .update(req.body)
      .digest('hex');

    // timing-safe сравнение
    if (!sig || sig.length !== expected.length ||
        !crypto.timingSafeEqual(Buffer.from(sig), Buffer.from(expected))) {
      return res.status(401).send('Bad signature');
    }

    const event = JSON.parse(req.body);
    handleKapsulaEvent(event);
    res.json({ ok: true });
  }
);

async function handleKapsulaEvent(event) {
  const p = event.payment;
  const order = await db.orders.findOne({ kapsula_payment_id: p.id });
  if (!order || order.status === 'paid') return; // идемпотентность
  if (event.event === 'payment.success' && p.amount === order.amount_kop) {
    order.status = 'paid';
    await order.save();
    await sendOrderConfirmation(order);
  }
}
<?php
$body = file_get_contents('php://input');
$sig  = $_SERVER['HTTP_X_KAPSULA_SIGNATURE'] ?? '';
$secret = getenv('KAPSULA_WEBHOOK_SECRET');

$expected = hash_hmac('sha256', $body, $secret);

if (!hash_equals($expected, $sig)) {
    http_response_code(401);
    echo 'Bad signature';
    exit;
}

$event = json_decode($body, true);
$p = $event['payment'];

// Идемпотентность + проверка суммы
$order = $pdo->query("SELECT * FROM orders WHERE kapsula_payment_id = '{$p['id']}'")->fetch();
if (!$order || $order['status'] === 'paid') {
    echo json_encode(['ok' => true]);
    exit;
}

if ($event['event'] === 'payment.success' && $p['amount'] == $order['amount_kop']) {
    $pdo->exec("UPDATE orders SET status = 'paid' WHERE id = {$order['id']}");
    sendOrderConfirmation($order);
}

echo json_encode(['ok' => true]);
import hmac, hashlib, json, os
from flask import Flask, request, jsonify

app = Flask(__name__)
SECRET = os.environ['KAPSULA_WEBHOOK_SECRET'].encode()

@app.post('/kapsula/webhook')
def kapsula_webhook():
    raw = request.get_data()  # сырое тело — НЕ request.json
    sig = request.headers.get('X-Kapsula-Signature', '')
    expected = hmac.new(SECRET, raw, hashlib.sha256).hexdigest()

    if not hmac.compare_digest(sig, expected):
        return 'Bad signature', 401

    event = json.loads(raw)
    p = event['payment']

    order = Order.query.filter_by(kapsula_payment_id=p['id']).first()
    if not order or order.status == 'paid':
        return jsonify(ok=True)  # идемпотентность

    if event['event'] == 'payment.success' and p['amount'] == order.amount_kop:
        order.status = 'paid'
        db.session.commit()
        send_order_confirmation(order)

    return jsonify(ok=True)
package main

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "io"
    "net/http"
    "os"
)

func webhookHandler(w http.ResponseWriter, r *http.Request) {
    body, _ := io.ReadAll(r.Body)
    sig := r.Header.Get("X-Kapsula-Signature")

    secret := []byte(os.Getenv("KAPSULA_WEBHOOK_SECRET"))
    mac := hmac.New(sha256.New, secret)
    mac.Write(body)
    expected := hex.EncodeToString(mac.Sum(nil))

    if !hmac.Equal([]byte(sig), []byte(expected)) {
        http.Error(w, "Bad signature", 401)
        return
    }

    var event struct {
        Event   string `json:"event"`
        Payment struct {
            ID      string `json:"id"`
            OrderID string `json:"order_id"`
            Amount  int    `json:"amount"`
            Status  string `json:"status"`
        } `json:"payment"`
    }
    json.Unmarshal(body, &event)

    // обработка...
    w.Write([]byte(`{"ok":true}`))
}

Идемпотентность

Webhook может прийти несколько раз для одного и того же события (при retry). Чтобы не выдать товар дважды:

Повторы и сбои

💡 Делайте обработчик webhook'а быстрым — отвечайте 200 сразу, тяжёлую работу делайте в фоне (очередь, async). Иначе таймаут.

Сценарий: интеграция на сайт

Типовой flow для интернет-магазина:

  1. Пользователь нажимает «Оплатить» — ваш сервер создаёт платёж через POST /payment/create и редиректит покупателя на payment_url
  2. Покупатель оплачивает на странице KAPSULA
  3. Мы шлём webhook на ваш /webhook/kapsula
  4. Ваш обработчик webhook'а помечает заказ как оплаченный, отправляет товар/чек
  5. Мы редиректим покупателя на ваш success_url (там вы показываете «Спасибо!»)
⚠ Не выдавайте товар на странице success_url — это ненадёжный сигнал. Покупатель мог открыть URL вручную не оплатив. Источник истины — webhook.

Сценарий: Telegram-бот

  1. Пользователь пишет в боте «/buy» — бот вызывает POST /payment/create с mode: "host"
  2. Бот отправляет покупателю QR-картинку (из qr.data) и кнопку «Открыть в банке» (с qr.link)
  3. Бот сохраняет payment.id с привязкой к chat_id
  4. На webhook от KAPSULA — бот уведомляет пользователя «Оплата прошла» и выдаёт услугу

Пример отправки QR в Telegram (Python + aiogram)

import base64, io
from aiogram.types import BufferedInputFile, InlineKeyboardMarkup, InlineKeyboardButton

@dp.message(Command('buy'))
async def cmd_buy(message):
    payment = create_kapsula_payment(50000, f"tg_{message.from_user.id}", "Premium")
    qr_b64 = payment['qr']['data'].split(',')[1]
    qr_bytes = base64.b64decode(qr_b64)

    kb = InlineKeyboardMarkup(inline_keyboard=[[
        InlineKeyboardButton(text='Открыть в банке', url=payment['qr']['link'])
    ]])

    await message.answer_photo(
        BufferedInputFile(qr_bytes, 'qr.png'),
        caption=f"Оплатите {500} ₽ через СБП. QR действует 30 минут.",
        reply_markup=kb,
    )

Готовые модули для CMS

WordPress + WooCommerce

Готовый плагин с поддержкой WooCommerce. Покупатель видит способ оплаты «Оплата через СБП» при оформлении заказа, после оплаты заказ автоматически помечается как «Оплачен» через webhook.

📦
kapsula-payments.zip
Плагин для WordPress 5.6+ с интеграцией WooCommerce. Версия 1.0.0
⬇ Скачать плагин

Установка через админку WordPress (рекомендуется)

  1. Скачайте архив по кнопке выше — получите файл kapsula-payments.zip.
  2. Откройте админку WordPressПлагины → Добавить новый → Загрузить плагин.
  3. Выберите файл kapsula-payments.zip и нажмите «Установить», затем «Активировать».
  4. В админке появится пункт Настройки → KAPSULA — откройте его.
  5. Вставьте API-ключ и Webhook secret из карточки кассы (kapsula.pro/shops → ваша касса).
  6. Скопируйте Webhook URL со страницы настроек плагина.
  7. Откройте кабинет KAPSULA → Настройки кассы → вставьте этот Webhook URL и сохраните.
  8. Если используете WooCommerce: WooCommerce → Настройки → Платежи → KAPSULA → переключатель Включить.
  9. Готово. На странице оформления заказа появится способ оплаты «Оплата через СБП».

Установка вручную (через FTP)

  1. Распакуйте zip — получите папку kapsula-payments.
  2. Загрузите эту папку в /wp-content/plugins/ на вашем сервере.
  3. В админке WordPress → Плагины — найдите «KAPSULA — Платёжный шлюз»Активировать.
  4. Дальше как в п. 4-9 выше.

Что делает плагин

💡 Минимальная сумма заказа для СБП — 500 ₽, максимальная — 30 000 ₽. Если в корзине меньше 500 ₽ — способ оплаты KAPSULAй будет недоступен.

1C-Битрикс / другие CMS

Готовых модулей для других CMS пока нет. Подключение делается через стандартный API:

Если нужна помощь с интеграцией — напишите на support@kapsula.pro.

Кабинет: Главная

На главной кабинета отображается:

Быстрая оплата

Раздел /quick-pay в кабинете позволяет создавать платёжные ссылки вручную, без вызова API. Удобно для:

Как это работает:

  1. Откройте Быстрая оплата в боковом меню
  2. Выберите кассу (доступны только кассы со статусом active)
  3. Введите сумму (от 500 до 30 000 ₽), при желании — описание и email клиента
  4. Нажмите Создать ссылку — получите URL вида https://pay.kapsula.pro/p/pay_...
  5. Скопируйте ссылку и отправьте клиенту любым удобным способом
Изоляция платежей: платёж жёстко привязан к выбранной кассе через shop_id. Если у вас несколько касс, оплата по ссылке поступит именно на ту кассу, которая была выбрана при создании.
Срок действия: ссылка живёт 30 минут (ограничение СБП на стороне 6Tech). По истечении статус платежа меняется на expired, и оплатить будет нельзя — просто создайте новую.

Внутренне эндпоинт работает идентично POST /v1/payment/create в hosted-режиме, но авторизация идёт через cookie сессии кабинета, а не через Bearer pk_live_....

Аналитика

На странице конкретной кассы (/shops/:id/manage) есть блок аналитики:

Транзакции

Список последних 25 платежей по кассе с фильтрацией по статусу. Каждая транзакция показывает:

Через API: GET /api/shops/:id/payments?status=success&limit=50

Выплаты

Раздел в разработке. Сейчас принятые средства учитываются за вашей кассой; механизм автоматического вывода на расчётный счёт мерчанта — будет добавлен в ближайших обновлениях. Текущий процесс — связь с поддержкой для ручного вывода.

Настройки профиля

Раздел /profile позволяет:

Если забыли пароль — на странице входа есть ссылка Забыли пароль?. Восстановление в 3 шага: ввод email → код из письма → новый пароль.

Безопасность: код действителен 10 минут, максимум 5 неверных попыток, минимум 60 секунд между запросами кода. После сброса пароля на текущую сессию выдаётся новый JWT.

Ошибки

Все ошибки возвращаются в формате:

{ "error": "Текст ошибки на русском" }
КодПричина
400Невалидные параметры (сумма вне диапазона, неверный email и т.д.)
401API-ключ не передан или неверный
403Касса не активна (на модерации, заблокирована)
404Платёж не найден или принадлежит другой кассе
429Превышен лимит запросов (rate limit)
502Платёжный шлюз недоступен или вернул ошибку

Лимиты

FAQ

Можно ли тестировать без реальных платежей?
Сейчас нет — sandbox-окружение в разработке. Тестировать можно с реальной картой на минимальной сумме (500 ₽), потом запросить возврат через поддержку.
Какая комиссия?
Текущая ставка комиссии видна в кабинете для каждой кассы. По умолчанию — 2% от суммы платежа. Уточняйте у поддержки если ваш профиль предполагает льготные условия.
Что делать если webhook не пришёл?
Проверьте: (1) webhook_url задан в настройках кассы, (2) URL HTTPS и доступен снаружи (не localhost), (3) ваш сервер отвечает 2xx за 10 секунд, (4) не блокирует ваш фаервол. В крайнем случае — статус всегда можно получить через GET /payment/:id.
Можно ли изменить сумму платежа после создания?
Нет. Создайте новый платёж и используйте новый payment_url / QR.
Что если QR истёк а покупатель его уже отсканировал?
Если оплата была завершена в банке до истечения — мы получим callback и установим статус success. Если банк не успел подтвердить — придёт expired и потребуется создать новый платёж.
Поддерживаются ли возвраты (refund)?
Сейчас возврат делается через поддержку вручную. API для refund будет добавлен в ближайших версиях.
Можно ли использовать один API-ключ для нескольких сайтов?
Технически да, но мы рекомендуем создавать отдельную кассу для каждого сайта/проекта — это даёт раздельную аналитику, упрощает модерацию и компрометация одного ключа не затрагивает остальные.
Что если мой сайт DDoS'ят и мы не успеваем отвечать на webhook?
Мы повторим webhook 3 раза (через 0с / 30с / 5мин). Если все попытки неуспешны — поднимайте сервис и подтягивайте статусы через GET /payment/:id (например, для всех заказов в статусе awaiting_confirmation старше N минут).

Поддержка

📧 support@kapsula.pro

В обращении укажите:

Удачи в подключении! Если что-то непонятно — пишите, доработаем документацию.