# Endpoint: Создание Payment Link для существующего продукта

## Зачем это нужно

EasyPay создаёт первую платёжную ссылку через **мини-апп с модерацией** — проверив, что описание продукта соответствует требованиям Stripe.

Этот API нужен, когда продукт уже одобрен и первая платёжная ссылка на него работает, а вам понадобилась **ещё одна** ссылка к нему: с другой ценой, **другой валютой** (USD/EUR/GBP/BRL), другим набором способов оплаты, или другим `success_url` для отдельной воронки. Повторно проходить модерацию не нужно — ручка переиспользует параметры уже одобренного продукта (тип, интервал подписки, тестовый/боевой режим) и создаёт только новый Stripe Price + Payment Link. Валюту можно либо унаследовать от продукта (по умолчанию), либо передать явно через `price.currency`.

Запрос идемпотентный: если повторить с тем же `idempotency_key` и тем же телом — вернётся та же ссылка, без дублей. Если тело отличается — вернётся ошибка `idempotency_conflict`.

**Internal beta.** Сохраняйте `short_url` из ответа на своей стороне — это рабочая ссылка, которую вы дальше используете и отдаёте клиенту.

## Как подключиться


1. Запросите API-ключ у вашего менеджера EasyPay — или скопируйте его из мини-апп («Show key» на первом шаге онбординга).
2. Менеджер активирует разрешение `product_create` для вашего аккаунта (тот же permission, что и для создания продукта через мини-апп — отдельного разрешения не нужно).
3. Узнайте `stripe_product_id` нужного одобренного продукта — он есть в мини-апп в карточке продукта или в вашей Internal Tariffs Google-таблице.

## Endpoint

```
POST https://n8n.thenextgen.store/webhook/payment-link-create
Content-Type: application/json
```

## Формат запроса

```json
{
  "auth_mode": "api_key",
  "api_key": "ваш-api-ключ",
  "stripe_product_id": "prod_XXXXXXXXXXXX",
  "price": {
    "unit_amount": 9900,
    "currency": "EUR",
    "interval_count": 1,
    "trial_days": 0
  },
  "payment_link": {
    "payment_method_types": ["card", "link"],
    "allow_promotion_codes": false,
    "success_url": "https://example.com/thanks",
    "quantity": 1
  },
  "idempotency_key": "ваш-уникальный-ключ-этого-запроса"
}
```

**Параметры:**

| Параметр | Обязательный | Описание |
|----------|--------------|----------|
| `auth_mode` | Да           | Всегда `"api_key"` |
| `api_key` | Да           | API-ключ партнёра (UUID) |
| `stripe_product_id` | Да           | ID одобренного продукта партнёра в Stripe (`prod_…`) |
| `price.unit_amount` | Да           | Цена в наименьшей единице валюты, целое положительное (для USD/EUR/GBP/BRL — сотые доли; `9900` = $99.00) |
| `price.currency` | Нет          | Валюта Price: `"USD"`, `"EUR"`, `"GBP"` или `"BRL"` (case-insensitive). BRL — для продаж в Бразилии: только под реал Stripe показывает на чекауте Pix. Если не указана — наследуется от продукта. Позволяет продавать одобренный продукт в другой валюте без повторной модерации. |
| `price.interval_count` | Нет (только для подписок) | Сколько интервалов между списаниями: `1` — раз в интервал, `3` — раз в три. По умолчанию `1`. Для one-time продуктов игнорируется. |
| `price.trial_days` | Нет (только для подписок) | Длина бесплатного триала в днях. По умолчанию `0`. Для one-time игнорируется. |
| `payment_link.payment_method_types` | Нет          | Массив способов оплаты для этой конкретной ссылки. Если не указан — Stripe покажет дефолтный набор продукта. Значения проверяются: неподключённый или несовместимый с валютой способ вернёт `invalid_input`. См. раздел «Способы оплаты» ниже. |
| `payment_link.allow_promotion_codes` | Нет          | Разрешить ли вводить промокод на чекауте. По умолчанию `false`. |
| `payment_link.success_url` | Нет          | HTTPS-URL, куда перенаправить клиента после успешной оплаты. Максимум 2048 символов. Если не указан — Stripe покажет свою страницу успеха. |
| `payment_link.quantity` | Нет          | Количество единиц продукта в чекауте. По умолчанию `1`. |
| `idempotency_key` | Рекомендуется | Любая строка до 128 символов. Если не передать — сервер сгенерирует UUID и вернёт его в ответе. Передавайте свой ключ, если хотите безопасный retry. **НЕ должен начинаться с** `**ingest:**` — этот префикс зарезервирован под внутренние записи EasyPay; запрос с таким ключом отклоняется ошибкой `invalid_input`. |

**Что наследуется от продукта** (нельзя переопределить в этом запросе): тип (`one_time` / `subscription`), интервал подписки (`month` / `year` / …), тестовый/боевой режим. **Валюта** наследуется по умолчанию, но её можно явно переопределить через `price.currency`. Если нужно поменять тип или интервал — создайте отдельный продукт через стандартный flow в мини-апп.

**Способы оплаты.** Проверенные и работающие: `card`, `link` (обе — под USD, EUR, GBP, BRL), `sepa_debit` (только EUR), `pix` (только BRL), `us_bank_account` (только USD).

Два способа **не подключены** в Stripe-аккаунте EasyPay и отклоняются с ошибкой `invalid_input`: `zip` и `bacs_debit`. Если они вам нужны — напишите команде заботы EasyPay, это включается на нашей стороне.

Остальные способы Stripe (`cashapp`, `alipay`, `wechat_pay`, `klarna`, `afterpay_clearpay`, `affirm` и любые новые) передавайте, если нужны: мы их не блокируем, но и не обещаем — совместимость определяет Stripe. Если Stripe откажет в каком-то из них, ссылка всё равно будет создана — без этого способа, и вы получите об этом отдельное уведомление.

Способ, несовместимый с валютой ссылки, отклоняется с `invalid_input` и текстом, называющим и способ, и валюту (например, `klarna` работает у нас только с USD). При override валюты совместимость проверяется по **новой** валюте. Проверка идёт до обращения в Stripe — без побочных эффектов.

## Формат ответа

### Успешный ответ (200)

```json
{
  "success": true,
  "stripe_product_id": "prod_XXXXXXXXXXXX",
  "stripe_price_id": "price_YYYYYYYYYYYY",
  "stripe_payment_link_id": "plink_ZZZZZZZZZZZZ",
  "short_url": "https://short.appsign.me/abc",
  "short_url_id": "lnk_5hfk_...",
  "currency": "EUR",
  "currency_source": "overridden",
  "unit_amount": 9900,
  "type": "one_time",
  "is_test": false,
  "idempotency_key": "ваш-уникальный-ключ-этого-запроса",
  "is_replay": false,
  "retry_count": 0
}
```

| Поле | Описание |
|------|----------|
| `short_url` | Короткая ссылка (через short.io, домен `short.appsign.me`) — это **единственная партнёр-смотрящая ссылка для чекаута**. Отправляйте её клиентам в SMS / мессенджере / на физическом носителе. Сохраните на своей стороне — метода для повторного чтения ссылок нет. |
| `short_url_id` | ID короткой ссылки в short.io — пригодится если в будущем понадобится её удалить или обновить. |
| `stripe_price_id` / `stripe_payment_link_id` | **Идентификаторы** объектов в Stripe (не URL), для сверки в Stripe Dashboard и операций отмены/возврата. Полный Stripe URL не возвращается партнёру — он остаётся внутренней деталью EasyPay. |
| `currency` | Эффективная валюта Price: либо переданная партнёром через `price.currency`, либо унаследованная от продукта если override не передан. |
| `currency_source` | Источник валюты: `'inherited'` (от продукта) или `'overridden'` (явно передан партнёром). На replay этот же источник вернётся, даже если в Sheet с тех пор поменяли валюту. |
| `is_replay` | `true`, если запрос с этим `idempotency_key` уже был обработан и вы получили закэшированный результат. Полезно для отладки. |
| `idempotency_key` | Тот же ключ, что вы передали (или сгенерированный сервером, если не передавали). |
| `retry_count` | Сколько раз пришлось повторить попытку до успеха (`0` для первой попытки). |

### Ошибки

Все ошибки возвращаются с **HTTP 200** + полем `success: false`. Это нужно, чтобы Telegram-боты, n8n-флоу и MCP-агенты не путали клиентскую ошибку с сетевым сбоем.

| `error_code` | Когда срабатывает |
|------------|-------------------|
| `invalid_input` | Невалидное тело запроса: пропущенные поля, отрицательная сумма, неверный формат, `**idempotency_key**` **начинается с зарезервированного префикса** `**ingest:**`, `price.currency` не входит в `["USD", "EUR", "GBP", "BRL"]` |
| `unauthorized` | Невалидный API-ключ |
| `forbidden` | У партнёра нет разрешения `product_create` |
| `product_not_found` | `stripe_product_id` не принадлежит этому партнёру или не существует |
| `product_not_approved` | Продукт ещё не прошёл модерацию (нет `Stripe_Product_ID` в Internal Tariffs) |
| `product_data_corrupt` | В Internal Tariffs у продукта повреждены данные — обратитесь в команду заботы EasyPay |
| `invalid_input` (способы оплаты) | Передан способ, не подключённый у EasyPay (`zip`, `bacs_debit`), или несовместимый с валютой ссылки. Текст ошибки называет конкретный способ |
| `idempotency_conflict` | Тот же `idempotency_key` уже использовался с другим телом запроса **или с другой эффективной валютой**. Сгенерируйте новый ключ. |
| `short_url_unavailable` | Stripe Payment Link создан, но short.io временно недоступен — короткой ссылки нет. См. секцию ниже. |
| `stripe_error` | Сбой на стороне Stripe (4xx — детали в `error_message`; на 5xx EasyPay автоматически ретраит и не вернёт эту ошибку, если успели восстановиться) |
| `internal_error` | Сбой EasyPay. Безопасно повторить запрос с тем же `idempotency_key` — это либо вернёт уже созданную ссылку, либо продолжит работу с той точки, где она прервалась |

## Короткая ссылка (`short_url`)

Каждая успешная Payment Link автоматически получает короткий алиас через short.io (тот же сервис, который генерирует ссылки в основном flow создания продуктов через мини-апп). Короткая ссылка живёт на домене `short.appsign.me` и указывает на полный Stripe URL внутри EasyPay. Партнёр **только её и видит** — полный Stripe URL вам не возвращается.

Поведение:

* **Создаётся всегда** — и в live, и в test режиме (тест-продукты получают рабочую короткую ссылку).
* **Создаётся один раз** — на первой успешной попытке. На повторах с тем же `idempotency_key` (replay, resume) возвращается тот же `short_url` — мы не пересоздаём её.
* **Hard-fail при недоступности short.io.** Если short.io не отвечает после трёх попыток в рамках одного запроса, ответ будет `success: false` с `error_code: 'short_url_unavailable'`. Stripe Payment Link при этом **создан** (мы его не теряем), но партнёр-смотрящего URL у вас нет. Повторный запрос с тем же `idempotency_key` вернёт ту же ошибку, **не дёргая ни Stripe, ни short.io** — пока команда заботы EasyPay вручную не доcоздаст короткую ссылку. Дальнейший partner-retry уже вернёт `success: true` с `short_url`. Если кейс срочный — напишите в вашу группу с командой заботы EasyPay с `idempotency_key`.

## Идемпотентность и повторы

Сервис рассчитан на безопасные повторы.

* **Тот же** `**idempotency_key**` **+ то же тело запроса.** Возвращается та же ссылка, что и в первый раз. В ответе `is_replay: true`. Никаких дублей в Stripe. Лимит на 24 часа — после этого та же комбинация ключ+тело создаст новую ссылку.
* **Тот же** `**idempotency_key**` **+ другое тело.** Возвращается `idempotency_conflict`. Никаких изменений на стороне Stripe. Сгенерируйте новый ключ для нового запроса.
* **Тот же** `**idempotency_key**` **+ другая эффективная валюта.** Также `idempotency_conflict`. EasyPay сравнивает валюту current request (после override-fallback) с валютой сохранённой ссылки. Если они отличаются — конфликт, даже если остальное тело то же. Для смены валюты сгенерируйте новый ключ.
* **Сетевой сбой посреди обработки** (вы не получили ответ). Повторите с тем же ключом и телом — EasyPay подхватит работу с того места, где она прервалась, и вернёт ту же ссылку.
* **Лимит ретраев.** На один `idempotency_key` допустимо 5 неудачных попыток. После шестого ретрая, который попадает на ту же ошибку, ключ блокируется как `failed_terminal` — следующие запросы с ним вернут закэшированную ошибку. Чтобы повторить — сгенерируйте новый `idempotency_key`.
* **Зарезервированный префикс.** `idempotency_key` не должен начинаться с `ingest:` — этот префикс EasyPay использует внутри себя для записи продуктов, проходящих модерацию через мини-апп. Запрос с таким ключом отклоняется как `invalid_input`.

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

* Endpoint доступен только для **уже одобренных продуктов**. Чтобы создать первую ссылку для нового продукта, используйте мини-апп — чтобы он прошёл модерацию.
* Сохраните `short_url` на своей стороне сразу после получения ответа — это рабочая ссылка для дальнейшего использования.
* `success_url` максимум 2048 символов.
* `idempotency_key` максимум 128 символов и не должен начинаться с `ingest:`.
* `price.currency` (если передан) — `"USD"`, `"EUR"`, `"GBP"` или `"BRL"`.

## Пример на Python

```python
import urllib.request
import json
import uuid

url = "https://n8n.thenextgen.store/webhook/payment-link-create"
payload = {
    "auth_mode": "api_key",
    "api_key": "ваш-api-ключ",
    "stripe_product_id": "prod_XXXXXXXXXXXX",
    "price": {
        "unit_amount": 9900,
        "currency": "EUR"  # optional — по умолчанию наследуется от продукта
    },
    "payment_link": {
        "payment_method_types": ["card", "link"],
        "allow_promotion_codes": False,
        "success_url": "https://example.com/thanks",
        "quantity": 1
    },
    "idempotency_key": str(uuid.uuid4())
}

req = urllib.request.Request(
    url,
    data=json.dumps(payload).encode("utf-8"),
    headers={"Content-Type": "application/json"},
    method="POST"
)

with urllib.request.urlopen(req) as resp:
    result = json.loads(resp.read().decode("utf-8"))

if result.get("success"):
    print(f"Short link to share with customer: {result['short_url']}")
    print(f"Currency: {result['currency']} ({result['currency_source']})")
    print(f"is_replay: {result.get('is_replay')}")
else:
    print(f"Error: {result.get('error_code')} — {result.get('error_message')}")
```

## Пример на cURL

```bash
curl -X POST https://n8n.thenextgen.store/webhook/payment-link-create \
  -H "Content-Type: application/json" \
  -d '{
    "auth_mode": "api_key",
    "api_key": "ваш-api-ключ",
    "stripe_product_id": "prod_XXXXXXXXXXXX",
    "price": { "unit_amount": 9900, "currency": "EUR" },
    "payment_link": {
      "payment_method_types": ["card", "link"],
      "success_url": "https://example.com/thanks"
    },
    "idempotency_key": "your-unique-key-here"
  }'
```

## Подписки: пример с триалом

Для продукта-подписки добавьте `interval_count` и `trial_days`. Интервал (`month` / `year` / …) наследуется от продукта.

```json
{
  "auth_mode": "api_key",
  "api_key": "ваш-api-ключ",
  "stripe_product_id": "prod_SUBSCRIPTION_PRODUCT_ID",
  "price": {
    "unit_amount": 4900,
    "interval_count": 1,
    "trial_days": 14
  },
  "payment_link": {
    "allow_promotion_codes": true
  },
  "idempotency_key": "subscription-link-2026-04-29"
}
```

## Если что-то пошло не так

* **Получили** `**unauthorized**` — обновите ключ через мини-апп («Show again» на первом шаге онбординга или получить у вашего менеджера).
* `**product_not_found**` **для продукта, который вы видите в мини-апп** — проверьте, что копируете именно `stripe_product_id` (формат `prod_…`), а не внутренний номер из Internal Tariffs.
* `**idempotency_conflict**` — сгенерируйте новый ключ. Не пытайтесь «починить» существующий: каждое тело запроса (включая валюту) должно иметь свой ключ.
* `**short_url_unavailable**` — Stripe Payment Link создан, но short.io в момент запроса не ответил. Напишите в вашу EasyPay-группу с командой заботы с `idempotency_key` — короткая ссылка будет добавлена вручную, и ваш повторный запрос с тем же ключом вернёт `success: true`.
* `**invalid_input**` **с сообщением про reserved prefix** — `idempotency_key` не должен начинаться с `ingest:`. Сгенерируйте новый ключ (например, через `uuid.uuid4()`).
* `**invalid_input**` **с сообщением про currency** — `price.currency` должен быть `"USD"`, `"EUR"`, `"GBP"` или `"BRL"` (case-insensitive). Если хотите унаследовать валюту от продукта — просто не передавайте это поле.
* **Любая другая ситуация** — напишите в вашу EasyPay-группу с командой заботы или попросите MCP-агента передать запрос команде заботы.

---

**Documents**

- [Endpoint (API v2): Корзина T-Bank — один рублёвый платёж на несколько товаров](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-api-v2-korzina-t-bank-odin-rublyovyj-platyozh-na-neskolko-tovarov-GiKE6pds32)
- [Endpoint: Корзина Stripe — одна платёжная ссылка на несколько продуктов](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-korzina-stripe-odna-platyozhnaya-ssylka-na-neskolko-produktov-IwfZcVa3fp)
- [Endpoint (API v2): Рублёвый платёж T-Bank под заказ](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-api-v2-rublyovyj-platyozh-t-bank-pod-zakaz-ypmkgCKGjv)
- [Endpoint (API v2): Платёж Stripe по payment_intent_id](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-api-v2-platyozh-stripe-po-payment_intent_id-YClkCUGdKc)
- [Endpoint (API v2): Список платежей Stripe за период](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-api-v2-spisok-platezhej-stripe-za-period-HLmrFXTXwS)
- [Что делать, если у вас хай-риск продукт?](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/chto-delat-esli-u-vas-haj-risk-produkt-iz6C33qYOE)
- [Endpoint (API v2): Регистрация вебхука уведомлений](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-api-v2-registraciya-vebhuka-uvedomlenij-yMW2D2iTuK)
- [Endpoint (API v2): Ротация секрета подписи вебхуков](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-api-v2-rotaciya-sekreta-podpisi-vebhukov-WRlr8kxNX2)
- [Не открывается мини-приложение или Dashboard из России — что делать](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/ne-otkryvaetsya-mini-prilozhenie-ili-dashboard-iz-rossii-chto-delat-N75kNI0Z9o)
- [QuickStart Stripe: первая оплата и webhook за 15 минут](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/quickstart-stripe-pervaya-oplata-i-webhook-za-15-minut-eXWpaeyh1t)
- [Endpoint: Список рабочих платёжных ссылок Stripe](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-spisok-rabochih-platyozhnyh-ssylok-stripe-kDG4Zav1iv)
- [Webhook EasyPay: структура данных платежей T-Bank](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/webhook-easypay-struktura-dannyh-platezhej-t-bank-IjWtNIwzgd)
- [MCP-сервер EasyPay: настройка платежей в чате с AI](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/mcp-server-easypay-nastrojka-platezhej-v-chate-s-ai-cqtpnWTk2i)
- [Webhook EasyPay: структура данных платежей Stripe](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/webhook-easypay-struktura-dannyh-platezhej-stripe-2aK80hyqzo)
- [Webhook EasyPay: проверка подписи](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/webhook-easypay-proverka-podpisi-YS2uy0j2It)
- [Endpoint: Немедленная отмена подписки](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-nemedlennaya-otmena-podpiski-j8M2K8H8FQ)
- [Endpoint: Отмена подписки в конце периода](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-otmena-podpiski-v-konce-perioda-YPDXDsaVC0)
- [Endpoint: Получение платёжных объектов Stripe](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-poluchenie-platyozhnyh-obuektov-stripe-tu4gaOUuSJ)
- [Endpoint: Создание Payment Link для существующего продукта](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-sozdanie-payment-link-dlya-sushestvuyushego-produkta-BpkGOi94hv)
- [Endpoint: Создание промокода Stripe для существующих продуктов](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-sozdanie-promokoda-stripe-dlya-sushestvuyushih-produktov-k0Jz6aJx7Q)
- [Checkout: Публичная T-Bank ссылка — параметры через Base64](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/checkout-publichnaya-t-bank-ssylka-parametry-cherez-base64-dSXBIR2pat)
- [Интеграция EasyPay с GetCourse](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/integraciya-easypay-s-getcourse-ejM3K01cmB)
- [Инструкция для партнёров: как пользоваться ботом EasyPay](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/instrukciya-dlya-partnyorov-kak-polzovatsya-botom-easypay-pzstTctBLZ)
- [Endpoint (API v2): Payment Link для корзины — несколько продуктов](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-api-v2-payment-link-dlya-korziny-neskolko-produktov-zJKlJx9zui)
- [Endpoint (API v2): Перенос даты автосписания подписки (подарочные дни)](https://docs.thenextgen.store/s/1fa79577-e778-443b-a4e4-187f47b93798/doc/endpoint-api-v2-perenos-daty-avtospisaniya-podpiski-podarochnye-dni-tGi3aL5Y1g)