Webhook EasyPay: структура данных платежей Stripe
Этот документ описывает webhook, который EasyPay отправляет на ваш сервер при платёжных событиях Stripe. Если вам нужно получить платёжные объекты по запросу (а не push-уведомлением), см. Endpoint: Получение платёжных объектов Stripe.
Общая информация
EasyPay получает данные из API Stripe и передаёт их партнёру, сохраняя оригинальную структуру объектов Stripe.
Передача id клиента/заказа через форму оплаты
Вы можете передать свой идентификатор клиента или заказа через параметр client_reference_id в URL платёжной ссылки:
https://short.appsign.me/test_xxx?client_reference_id=your_order_idПодробнее: Stripe URL Parameters
Принцип формирования payload
Мы получаем необходимые объекты через Stripe API и объединяем их в единый JSON, размещая каждый объект в отдельное поле. Итоговая структура содержит исчерпывающую информацию о событии, хотя и имеет некоторое дублирование данных между объектами.
Как передаётся webhook
Мы отправляем данные на ваш webhook endpoint:
Параметр | Значение |
|---|---|
Метод |
|
Content-Type |
|
Timeout | 10 секунд на ответ |
Успешный ответ | любой |
Повторы | до 6 попыток с нарастающим интервалом: ~1 → 3 → 9 → 27 → 30 минут (всего ~70 минут) |
Как убедиться, что запрос действительно пришёл от EasyPay (HMAC-подпись в заголовках, включается после настройки секрета), — Webhook EasyPay: проверка подписи.
Когда мы повторяем отправку
Повторяем, если повтор способен помочь:
ответ
5xx, обрыв соединения или отсутствие ответа за 10 секунд;ответ
429(слишком много запросов), а также408,409,425.
На остальные 4xx (400, 401, 404 и подобные) повторов не будет — повтор их не исправит. Вместо этого срабатывает внутренний алерт, и с вами свяжется команда EasyPay.
Если вы отдаёте заголовок Retry-After (например, вместе с 429 или 503), мы его учитываем, но ждём не дольше 30 минут.
Идемпотентность на вашей стороне обязательна
Если ответ не пришёл за 10 секунд, мы не знаем, дошёл ли запрос, и повторим отправку. Доставка гарантируется по принципу «хотя бы один раз», поэтому одно и то же событие может прийти к вам дважды.
Повтор несёт ровно то же тело, что и первая отправка. Для дедупликации используйте пару «Event + идентификатор объекта, к которому относится событие»:
Charge.id— для платежей, возвратов и диспутов;Subscription.id— для событий подписки.
Отдельный случай — Stripe.payment_intent.payment_failed: если платёж отклонён до того, как списание было создано (например, сработала защита от фрода), объект Charge приходит без id.
Если событие уже обработано, просто верните 2xx.
Поддерживаемые события
Каждый payload содержит поле Event, которое указывает тип события в формате Stripe.<event_type>.
Event | Описание |
|---|---|
| Завершена оплата через Checkout (только для one-time payment) |
| Успешный платёж по инвойсу (первый и повторные платежи подписки) |
| Создана новая подписка |
| Подписка обновлена (изменение плана, статуса и т.д.) |
| Подписка отменена/удалена |
| Платёж не прошёл |
| Выполнен возврат средств |
| Открыт диспут — средства списаны |
| Диспут выигран — средства возвращены |
Структура payload по типам событий
Успешный платёж
Отправляется при событиях:
Stripe.checkout.session.completedStripe.invoice.payment_succeeded
Структура payload
{
"Event": "Stripe.checkout.session.completed",
"Initial_Checkout_Session": <Checkout Session Object>,
"Subscription": <Subscription Object>,
"Invoice": <Invoice Object>,
"Charge": <Charge Object>,
"Balance_Transaction": <Balance Transaction Object>
}Описание полей
Поле | Тип | Описание |
|---|---|---|
| string | Тип события |
| object | Объект сессии оплаты (для первого платежа). Содержит |
| object | Сырой объект подписки Stripe (Subscription object). |
| object | Инвойс платежа (для подписок). Содержит детали выставленного счёта. |
| object | Детали списания. Содержит информацию о карте, статус, результат проверок |
| object | Транзакция баланса. Содержит комиссии Stripe, нетто-сумму |
Пример payload
Подписка
Отправляется при событиях:
Stripe.customer.subscription.createdStripe.customer.subscription.updatedStripe.customer.subscription.deleted
Структура payload
{
"Event": "Stripe.customer.subscription.created",
"Initial_Checkout_Session": <Checkout Session Object>,
"Subscription": <Subscription Object>,
"Subscription_Product": <Product Object>,
"Subscription_from_db": <Subscription Summary Object>
}Описание полей
Поле | Тип | Описание |
|---|---|---|
| string | Тип события |
| object | Объект сессии оплаты (может быть пустым |
| object | Полный объект подписки из Stripe |
| object | Объект продукта подписки |
| object | Сокращённый объект с ключевой информацией о подписке |
Структура Subscription_from_db
{
"subscription_id": "sub_1ROFheLoVAqE08foy7R5vWgT",
"easypay_partner_name": "your_partner_name",
"creation_date": "2025-05-13T12:04:54.855Z",
"customer_id": "cus_SIrQVLdMX8FBGs",
"customer_name": "Test Customer",
"customer_email": "test@example.com",
"subscription_product_name": "Premium Plan",
"subscription_product_id": "prod_S3bQuNWvDDRHEf",
"amount_in_cents": 999,
"currency": "usd",
"source": "Stripe",
"current_status": "active",
"cancel_requested_at": null,
"ended_at": null
}Пример payload
Неуспешный платёж
Отправляется при событии:
Stripe.payment_intent.payment_failed
Структура payload
{
"Event": "Stripe.payment_intent.payment_failed",
"Initial_Checkout_Session": <Checkout Session Object>,
"Subscription": <Subscription Object>,
"Invoice": <Invoice Object>,
"Charge": <Charge Object>,
"Balance_Transaction": {}
}Описание полей
Поле | Тип | Описание |
|---|---|---|
| string | Тип события |
| object | Объект сессии оплаты (может быть пустым |
| object | Сырой объект подписки Stripe (Subscription object). |
| object | Инвойс (для подписок) |
| object | Детали неуспешного списания. Содержит информацию об ошибке |
| object | Пустой объект |
Ключевые поля для обработки ошибки
Информация об ошибке находится в объекте Charge (или в Payment Intent):
{
"failure_code": "card_declined",
"failure_message": "Your card was declined.",
"outcome": {
"type": "issuer_declined",
"network_decline_code": "05",
"seller_message": "The bank returned the decline code `do_not_honor`."
}
}Пример payload
Возврат (Refund)
Отправляется при событии:
Stripe.charge.refunded
Структура payload
{
"Event": "Stripe.charge.refunded",
"Initial_Checkout_Session": <Checkout Session Object>,
"Subscription": <Subscription Object>,
"Invoice": <Invoice Object>,
"Charge": <Charge Object>,
"Balance_Transaction": <Balance Transaction Object>,
"Refund": <Refund Object>,
"Dispute": {}
}Описание полей
Поле | Тип | Описание |
|---|---|---|
| string | Тип события |
| object | Объект сессии оплаты (может быть пустым |
| object | Объект подписки (если возврат связан с подпиской) |
| object | Инвойс оригинального платежа |
| object | Детали оригинального списания. |
| object | Транзакция возврата (отрицательная сумма) |
| object | Детали возврата — ID, сумма, причина, статус |
| object | Пустой объект |
Пример payload
Диспут (Dispute)
Отправляется при событиях:
Stripe.charge.dispute.funds_withdrawn— открыт диспут, средства списаныStripe.charge.dispute.funds_reinstated— диспут выигран, средства возвращены
Структура payload
{
"Event": "Stripe.charge.dispute.funds_withdrawn",
"Initial_Checkout_Session": <Checkout Session Object>,
"Subscription": <Subscription Object>,
"Invoice": <Invoice Object>,
"Charge": <Charge Object>,
"Balance_Transaction": <Balance Transaction Object>,
"Refund": {},
"Dispute": <Dispute Object>
}Описание полей
Поле | Тип | Описание |
|---|---|---|
| string | Тип события |
| object | Объект сессии оплаты (может быть пустым |
| object | Объект подписки (если диспут связан с подпиской) |
| object | Инвойс оригинального платежа |
| object | Детали оригинального списания. |
| object | Транзакция диспута (списание или возврат средств) |
| object | Пустой объект |
| object | Детали диспута — причина, статус, сроки, доказательства |
Ключевые поля Dispute объекта
{
"id": "du_1S7FL2LoVAqE08foJsFPwcD0",
"amount": 1999,
"reason": "fraudulent",
"status": "needs_response",
"evidence_details": {
"due_by": 1759276799,
"has_evidence": false,
"past_due": false
}
}Поле | Описание |
|---|---|
| Причина диспута: |
| Статус: |
| Дедлайн для предоставления доказательств (timestamp) |
Пример payload
Оценка риска платежа (risk score)
Stripe Radar оценивает каждый платёж: risk_score от 0 до 100 (чем выше — тем подозрительнее) и категория risk_level. Оценка приходит в объекте Charge, в поле outcome:
{
"Charge": {
"outcome": {
"type": "authorized",
"network_status": "approved_by_network",
"risk_level": "normal",
"risk_score": 12,
"seller_message": "Payment complete."
}
}
}Поле | Описание |
|---|---|
| Оценка риска 0–100. Чем выше — тем подозрительнее платёж |
| Категория риска: |
| Итог проверки: |
| Пояснение итога. Для неуспешных платежей: |
Для неуспешных платежей (Stripe.payment_intent.payment_failed) поля risk_score, risk_level и seller_message дополнительно продублированы на верхнем уровне объекта Charge. Если платёж отклонён до создания списания (например, сработала защита от фрода), объект Charge приходит без id, и risk-поля могут быть null.
Как использовать risk score, чтобы защитить выдачу товара от оплат крадеными картами, — Защита от фрода: работа с risk score в Stripe.
Справочник объектов Stripe
Объект | Документация Stripe |
|---|---|
Checkout Session | |
Payment Intent | |
Charge | |
Balance Transaction | |
Subscription | |
Invoice | |
Refund | |
Dispute |
FAQ
Как отличить one-time payment от подписки?
Если
Subscriptionне пустой — это платёж по подпискеЕсли
Invoiceсодержитbilling_reason: "subscription_create"— это первый платёж подпискиЕсли
Invoiceсодержитbilling_reason: "subscription_cycle"— это повторный платёж подписки
Как получить мой client_reference_id?
Поле client_reference_id находится в объекте Initial_Checkout_Session:
{
"Initial_Checkout_Session": {
"client_reference_id": "your_order_id",
...
}
}Как получить комиссию Stripe?
Комиссия находится в объекте Balance_Transaction:
{
"Balance_Transaction": {
"fee": 2057,
"fee_details": [
{
"amount": 2057,
"description": "Stripe processing fees",
"type": "stripe_fee"
}
],
"net": 67843
}
}fee— общая комиссия в центахnet— сумма к зачислению после вычета комиссии