Быстрая интеграция

Промпт для нейросети

Скопируйте промпт ниже и вставьте его своему ИИ-ассистенту (Cursor, Claude Code, GitHub Copilot, Windsurf) прямо в проекте - он сам внедрит приём платежей Cashera по нашей спецификации

Как пользоваться

  1. Откройте свой проект в ИИ-редакторе (Cursor, Claude Code, Copilot Chat, Windsurf и т.п.)
  2. Скопируйте промпт кнопкой ниже и вставьте в чат ассистента
  3. Получите ключи pk_… и sk_… в кабинете и положите их в переменные окружения проекта
  4. Проверьте предложенный ассистентом план файлов и подтвердите реализацию
Промпт уже содержит всю спецификацию

Базовый URL, авторизацию, создание платежа, вебхуки с проверкой подлинности, сверку статуса, а также опциональные H2H, выплаты и подписки со сменой тарифа. Ассистент адаптирует код под ваш стек.

Промпт для ИИ-ассистента
Ты - опытный backend-разработчик. Внедри приём платежей Cashera в ЭТОТ проект.
Действуй строго по спецификации ниже. Придерживайся текущего стека, стиля и
структуры проекта, не добавляй лишних зависимостей. Сначала покажи план файлов и
изменений, затем реализуй.

=== КОНТЕКСТ ===
Cashera - платёжный шлюз. Интеграция server-to-server по HTTPS, формат JSON.
Базовый URL: https://api.cashera.cash/api/v1
Все суммы - ЦЕЛЫЕ числа в минорных единицах: RUB умножать на 100,
USDT - на 1 000 000. Пример: 49900 = 499,00 RUB.

=== СЕКРЕТЫ (только на сервере, через переменные окружения; НИКОГДА в клиентском
коде, в репозитории или в логах) ===
- CASHERA_API_KEY    - публичный ключ (pk_...), передаётся в заголовке X-Api-Key
- CASHERA_API_SECRET - секрет (sk_...), для проверки подлинности вебхуков

=== ЗАДАЧА 1. Создание платежа ===
POST {BASE}/integration/transactions
Заголовки: X-Api-Key: <CASHERA_API_KEY>, Content-Type: application/json
Тело:
{
  "amount": <int, минорные единицы>,
  "currency": "RUB",
  "payment_method": "<код метода, например sbp>",
  "external_id": "<твой уникальный id заказа>",
  "description": "<назначение платежа, обязательно, до 255 символов>",
  "callback_url": "https://<твой-домен>/webhooks/cashera",
  "success_url": "https://<твой-домен>/pay/ok",
  "fail_url": "https://<твой-домен>/pay/fail"
}
Ответ 201 содержит "uuid", "status": "pending" и "payment_url".
Действие: перенаправь покупателя на payment_url.
Идемпотентность: повтор запроса с тем же external_id вернёт ту же транзакцию
(дубль не создаётся) - безопасно повторять при таймаутах.
callback_url ОБЯЗАТЕЛЕН и должен быть HTTPS на публичном хосте.

=== ЗАДАЧА 2. Обработчик вебхуков (как узнать об оплате) ===
Создай HTTP-эндпоинт POST /webhooks/cashera (или по роутингу проекта), который:
1) Читает заголовки X-Api-Key и X-Secret и сравнивает их В ПОСТОЯННОМ ВРЕМЕНИ
   (constant-time) с CASHERA_API_KEY и CASHERA_API_SECRET. Если хотя бы один не
   совпал - верни 401 и НЕ обрабатывай. X-Secret не логировать.
2) Парсит тело:
   {
     "event": "transaction.status_updated",
     "transaction": {
       "uuid": "...", "external_id": "...", "status": "paid",
       "amount": 49900, "currency": "RUB", "payment_method": "sbp",
       "paid_at": "..."
     }
   }
3) Идемпотентность по ключу transaction.uuid + transaction.status: уже
   обработанное событие - просто верни 200, не повторяя бизнес-логику.
4) Заказ считается ОПЛАЧЕННЫМ только при status == "paid". Перед выдачей товара
   сверь amount и currency со своим заказом по external_id.
5) Всегда возвращай HTTP 200 (любой 2xx) после приёма.
Статусы: pending (ждёт оплаты) - нефинальный; paid - успех; canceled (отмена),
failed, expired - оплата не подтверждена; refunded - возврат; chargeback - оспаривание.
Для платежа отмена называется canceled (одна l); cancelled относится к подписке.
После canceled, failed или expired может прийти paid при позднем подтверждении
оплаты провайдером. Обработай его идемпотентно после проверки суммы и валюты.
После paid также возможны refunded и chargeback.

=== ЗАДАЧА 3. Сверка статуса (резерв, если вебхук не дошёл) ===
GET {BASE}/integration/transactions/{uuid}                       (заголовок X-Api-Key)
GET {BASE}/integration/transactions/by-external-id/{external_id} (заголовок X-Api-Key)
Возвращает текущий объект транзакции со status. Используй для ручной сверки.

=== ТРЕБОВАНИЯ ===
- НЕ считай возврат клиента на success_url подтверждением оплаты - только вебхук
  "paid" или запрос статуса.
- Все вызовы Cashera выполняй ТОЛЬКО с сервера, ключи не попадают в браузер.
- Обрабатывай ошибки по HTTP-коду: 401 (ключ), 403 (мерчант/callback), 422
  (валидация - детали в поле errors), 429 (лимит - повтори позже), 502 (провайдер).
- Повторяй сетевые ошибки и 5xx с тем же external_id (экспоненциальная задержка).
- Сохрани в БД: external_id, uuid, status, amount, currency, timestamps.
- Добавь CASHERA_API_KEY и CASHERA_API_SECRET в .env.example без значений.

=== ОПЦИОНАЛЬНО: H2H (свой UI оплаты вместо редиректа) ===
GET {BASE}/integration/transactions/{uuid}/h2h  (заголовок X-Api-Key)
Ответ: { "amount": <number>, "qr": "<строка QR СБП или платёжная ссылка>" }
Отрендери qr как QR-код / кнопку оплаты в своём интерфейсе.

=== ОПЦИОНАЛЬНО: выплаты (вывод средств с RUB/EUR-баланса) - ТРЕБУЮТ ПОДПИСИ ===
POST {BASE}/integration/payouts  и  GET {BASE}/integration/payouts/{uuid}
Помимо X-Api-Key добавь заголовки:
  X-Timestamp: <unix-время в секундах>
  X-Signature: hex( HMAC-SHA256( CASHERA_API_SECRET, СТРОКА ) )
где СТРОКА - это склейка через перевод строки:
  <timestamp>\n<METHOD>\n<path>\n<sha256_hex(raw_body)>
path включает префикс, например /api/v1/integration/payouts; для GET тело пустое
(sha256 от пустой строки). Допустимое расхождение времени - ±300 секунд.
Тело создания выплаты: { "amount": <int, минорные единицы (копейки)>, "currency": "RUB" или "EUR",
"external_id": "...", "wallet_uuid": "<uuid кошелька из личного кабинета>" }.
Вместо wallet_uuid можно передать destination: {"type":"usdt_trc20","address":"<адрес>","memo":null}.
Допустимые type: usdt_trc20/usdt_erc20/usdt_bep20/usdt_base/usdt_avalanche/usdt_polygon/usdt_solana/usdt_ton/card.
Ровно один вариант: wallet_uuid или destination. Memo только TON/Solana. Кошелёк не сохраняется, wallet в ответе null.
Поля metadata и callback_url в теле НЕ поддерживаются - вернётся 422.
callback_url берётся из настроек мерчанта и должен быть HTTPS.
RUB/EUR можно вывести в USDT в выбранной сети; карты МИР — только RUB,
если выплаты на карты включены. Код Telegram нужен при добавлении кошелька в ЛК,
но не передаётся при создании выплаты через подписанный API.
Сначала получи GET {BASE}/integration/payouts/quote?amount=...&currency=...&wallet_uuid=...
или подписанный POST {BASE}/integration/payouts/quote с JSON {amount,currency,destination} без external_id.
Реквизиты в query не передавай. Расчёт и создание используют одинаковый destination.
Для USDT можно передать expected_usdt_amount_micro из quote.usdt_amount_micro:
если новая сумма меньше, POST вернёт 409 без создания заявки. Для карт параметр не применяется.
201 означает создание pending-заявки, а не завершение перевода. amount резервируется целиком,
комиссия включена в amount. Сумма получателю: payout_usdt_amount_micro для USDT
или card_payout.net_card_rub_amount для карты.
Повтор с тем же external_id и теми же amount/currency и получателем (wallet_uuid или type/address/memo) возвращает ту же заявку;
изменённые параметры — 409. После таймаута не генерируй новый external_id.
Статусы: pending, processing, completed, failed.
Финальный статус (completed/failed) приходит вебхуком "payout.status_updated".

=== ОПЦИОНАЛЬНО: рекуррентные СБП подписки ===
Создание: POST {BASE}/integration/subscriptions, заголовок X-Api-Key.
Тело: {"amount":1900,"external_id":"trial-customer-10428","interval":"monthly",
"interval_count":1,"description":"Триал 19 ₽, затем 290 ₽ в месяц","email":"customer@example.com"}.
По умолчанию agreement_mode=h2h с обязательным email. В agreement_mode=hosted
email необязателен. В обоих режимах открой возвращённый confirmation_url.
Все суммы в целых копейках; RUB с копейками допустимы, числовые строки запрещены.
Для состояния/истории: GET {BASE}/integration/subscriptions/{uuid}
и GET {BASE}/integration/subscriptions/{uuid}/charges?page=1&per_page=20.
UUID в URL - UUID подписки Cashera, а не провайдера и не отдельной транзакции.
Подписка active не доказывает оплату периода. Выдавай доступ только после paid
конкретного платежа, проверки суммы, валюты и корневого subscription.uuid в webhook.

Изменение: POST {BASE}/integration/subscriptions/{uuid}/update с X-Api-Key,
Content-Type: application/json и обязательным Idempotency-Key (до 200 символов).
Можно передать amount, next_charge_at (ISO 8601 с часовым поясом, не в прошлом),
interval и interval_count. Нужно хотя бы одно поле, interval/count только вместе.
Границы count: daily 1–31, weekly 1–4, monthly 1–12, yearly 1–3.
Непереданные параметры сохраняются. Изменение периода не сдвигает ближайшую дату;
изменение даты не меняет период. Update не создаёт оплату и не переписывает старые транзакции.
Для триала после paid 1900 и active СРАЗУ отправь update с amount=29000,
next_charge_at=paid_at первой оплаты + 120 часов, interval=monthly, interval_count=1.
Не жди пять дней для update. Сохрани ключ и вычисленную дату до HTTP в БД;
при retry используй ту же запись. Полей trial_days/trial_amount в API нет.

Внеочередное списание: POST {BASE}/integration/subscriptions/{uuid}/charge-now
без тела, с X-Api-Key и Idempotency-Key. Использует текущую сумму, не меняет
расписание. HTTP 202 - попытка принята, а не оплата; сохрани UUID транзакции
и дождись paid. Не вызывай этот метод по cron вместо расписания провайдера.
Разные ключи - разные попытки. После тайм-аута/409/5xx ключ не менять.
Не запускай вторую операцию до выяснения результата первой.
GET {BASE}/integration/subscriptions/{uuid}/update-supported возвращает
{"supported":true|false}; это справочная проверка изменения, не разрешение charge-now.
Изменение и новое списание доступны для active/past_due, не возобновляют cancelled/failed.
POST {BASE}/integration/subscriptions/{uuid}/cancel отменяет будущие списания
без возврата прошлых платежей. Failed/canceled у платежа не отменяет подписку автоматически.
Обрабатывай subscription.status_updated со всеми условиями: при том же active
могли измениться цена и дата. Не отбрасывай события только по subscription.uuid+status.
Подробные примеры: https://docs.cashera.cash/subscriptions/trial
Ошибки и повторы: https://docs.cashera.cash/subscriptions/recovery

Полная документация: https://docs.cashera.cash
Безопасность

Никогда не вставляйте реальные pk_… / sk_… в промпт или в клиентский код - храните их только в переменных окружения на сервере. Промпт намеренно использует плейсхолдеры.

Что дальше