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

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

{
  "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)

{
  "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

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

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 / …) наследуется от продукта.

{
  "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 prefixidempotency_key не должен начинаться с ingest:. Сгенерируйте новый ключ (например, через uuid.uuid4()).

  • **invalid_input** с сообщением про currencyprice.currency должен быть "USD", "EUR", "GBP" или "BRL" (case-insensitive). Если хотите унаследовать валюту от продукта — просто не передавайте это поле.

  • Любая другая ситуация — напишите в вашу EasyPay-группу с командой заботы или попросите MCP-агента передать запрос команде заботы.