# Endpoint (API v2): Перенос даты автосписания подписки (подарочные дни)

> ℹ️ **EasyPay API v2.** Это ручка **второго поколения** EasyPay API (база `https://api.appsign.me`), пришедшего на смену legacy-API v1 (`https://n8n.thenextgen.store/webhook/*`). Отличия v2 от v1: авторизация заголовком `X-Partner-Api-Key`, **плоское** тело запроса, идемпотентность через опциональный `client_token` (полный ключ собирает сервер), коды ошибок — в `UPPER_CASE`.

## Когда это нужно

Вы хотите подарить подписчику несколько дней доступа — как компенсацию за неудобство, как бонус или в рамках акции — и чтобы следующее списание прошло позже ровно на этот срок.

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

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

Пример: подписка списывалась 6-го числа каждого месяца, вы подарили 14 дней — следующее списание пройдёт 20-го, и дальше каждое 20-е.

* Деньги в момент подарка не списываются и не возвращаются.
* Может появиться служебный счёт на 0 — это нормальная часть операции, клиент по нему ничего не платит.
* Тариф не меняется: мы не трогаем ни цену, ни состав подписки. Сумма счёта может отличаться от прежней только там, где она и так зависит от периода — оплата по факту потребления, заканчивающаяся скидка, налоги, привязанные к дате.

## Что обязательно учесть в своей интеграции

**На время подарка статус подписки —** `**trialing**`**, а не** `**active**`**.**

Если доступ к вашему продукту открывается по условию `status === 'active'`, подписчик потеряет доступ ровно в подаренные дни — то есть подарок сработает наоборот. Расширьте условие: доступ открыт при `active` **и** при `trialing`.

Если вы подписаны на события подписок, вы получите `customer.subscription.updated` с новым статусом и новой датой списания — по нему удобно продлевать доступ на своей стороне автоматически.

## Ограничения

* **Только вперёд.** Сдвинуть дату списания на более раннюю нельзя.
* **Подписка должна быть действующей.** Если по ней есть неоплаченный счёт, подарок дней не заменяет оплату — сначала платёж, потом подарок.
* **Если по подписке уже запланирована отмена** — сначала снимите отмену, иначе результат сдвига непредсказуем.
* **Горизонт подарка — недели, а не годы.** Это компенсация или бонус, а не бессрочная заморозка биллинга.

## Как это сделать через API

```
POST https://api.appsign.me/postpone-partner-stripe-subscription
```

Ключ передаётся заголовком `X-Partner-Api-Key` (или полем `api_key` в теле). Метод доступен по отдельному праву — напишите команде заботы, мы его включим.

### Параметры

| Поле | Обязательное | Описание |
|------|--------------|----------|
| `subscription_id` | да           | ID подписки в Stripe, `sub_…` |
| `next_charge_at` | одно из двух | Новая дата списания. `YYYY-MM-DD` — берётся конец этих суток по UTC; можно передать и точное время в ISO |
| `extend_days` | одно из двух | На сколько суток отложить, от 1 до 90. Требует `expected_current_charge_at` |
| `expected_current_charge_at` | вместе с `extend_days` | Текущая дата списания, от которой вы считаете сдвиг. Мы сверим её с действительной |
| `client_token` | нет          | Ваш идемпотентный токен — номер заказа, uuid, что угодно короткое. Не передали — сервер дедупит по содержимому запроса: повтор того же запроса вернёт первый результат, а не подарит дни второй раз. |
| **Почему сверка обязательна для** `**extend_days**`**.** «Плюс семь дней» — это сдвиг относительно текущей даты, и если запрос уйдёт дважды (ретрай по таймауту, повтор из очереди), клиент получит четырнадцать дней вместо семи. Назовите дату, от которой считаете: если она разошлась с действительной, мы откажем, а не подарим второй раз. Абсолютный `next_charge_at` этого не требует — повтор просто просит ту же дату. |              |          |

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

```http
POST /postpone-partner-stripe-subscription
X-Partner-Api-Key: <ваш ключ>
Content-Type: application/json

{
  "subscription_id": "sub_1ABCdefGHIjklMNO",
  "extend_days": 7,
  "expected_current_charge_at": "2026-10-09T10:50:57.000Z",
  "client_token": "gift-2417"
}
```

### Пример ответа

```json
{
  "success": true,
  "subscription_id": "sub_1ABCdefGHIjklMNO",
  "environment": "live",
  "status": "trialing",
  "previous_charge_at": "2026-10-09T10:50:57.000Z",
  "next_charge_at": "2026-10-16T10:50:57.000Z",
  "granted_days": 7,
  "cancel_at_period_end": false,
  "already_applied": false
}
```

`already_applied: true` означает, что подписка уже стояла на запрошенной дате и мы ничего не меняли — так выглядит безопасный повтор запроса. В этом случае `granted_days` равен нулю.

### Ошибки

Ответ всегда приходит с HTTP 200; успех или отказ различайте по полю `success`. При отказе приходит `error_code` и понятное `error_message`.

| Код | Когда | Что делать |
|-----|-------|------------|
| `INVALID_INPUT` | не указано ни одного из двух полей даты, указаны оба сразу, `extend_days` вне 1..90 или без сверки, дата раньше текущей, сдвиг дальше 90 суток | поправить запрос по сообщению |
| `PRECONDITION_FAILED` | `expected_current_charge_at` разошёлся с действительной датой | перечитать подписку и повторить с актуальной датой |
| `SUBSCRIPTION_NOT_FOUND` | подписки нет среди ваших | проверить ID; только что созданная подписка появляется через несколько секунд |
| `SUBSCRIPTION_NOT_POSTPONABLE` | подписка не в статусе `active` или `trialing` | при неоплаченном счёте сначала нужен платёж |
| `SUBSCRIPTION_SCHEDULED_FOR_CANCEL` | по подписке запланирована отмена | снять отмену, затем дарить дни |
| `NO_AUTOMATIC_CHARGE` | у подписки не автосписание, а выставление счёта | переносить дату автосписания нечему |
| `COLLECTION_PAUSED` | сбор платежей по подписке приостановлен | снять паузу |
| `OPERATION_IN_PROGRESS` | другой запрос по этой же подписке ещё выполняется | повторить через секунду |
| `IDEMPOTENCY_KEY_REUSED_WITH_DIFFERENT_PAYLOAD` | тот же `client_token` пришёл с другим содержимым запроса | передать новый `client_token` |

Есть ещё три отказа для редких конфигураций подписки — расписание фаз, нестандартный режим биллинга, настройка «отменить подписку по окончании пробного периода». Во всех трёх перенос сработал бы не так, как обещает этот метод, поэтому мы отказываем и просим написать команде заботы.

### Если делать руками

Метод нужен не всем: разовый подарок можно попросить у команды заботы — пришлите номер подписки (`sub_…`) и срок, обработаем в течение рабочего дня.