Endpoint: Создание промокода Stripe для существующих продуктов

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

EasyPay позволяет создавать процентные промокоды Stripe (Coupon + Promotion Code) и привязывать их сразу к 1..N вашим уже одобренным продуктам — одним запросом. Промокод — это текст, который клиент вводит на чекауте Stripe (например, BLACKFRIDAY, SUMMER25), чтобы получить процентную скидку на конкретные продукты.

Используйте этот API когда:

  • хотите запустить рекламную кампанию с промокодом на несколько ваших продуктов разом (например, скидка 20% на «Pro plan» и «Pro Annual» одновременно);

  • партнёрский маркетолог хочет выпускать промокоды самостоятельно, не дёргая команду заботы;

  • AI-агент партнёра должен оформить промокод по запросу клиента прямо в чате (через MCP).

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

MVP — только процентная скидка (percent_off). Фиксированная сумма (amount_off) — в будущей версии.

Internal beta. Сохраняйте code, stripe_coupon_id и stripe_promotion_code_id из ответа на своей стороне — это рабочие идентификаторы промокода для дальнейшего использования. Отдельная ручка для деактивации промокода (deactivate_partner_stripe_promotion_code) появится позже.

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

  1. Запросите API-ключ у вашего менеджера EasyPay — или скопируйте его из мини-апп («Show key» на первом шаге онбординга).

  2. Менеджер активирует разрешение product_create для вашего аккаунта (тот же permission, что и для создания продукта / Payment Link — отдельного не нужно).

  3. Узнайте stripe_product_id нужных одобренных продуктов — они есть в мини-апп в карточках продуктов или в вашей Internal Tariffs Google-таблице. Все привязываемые продукты должны принадлежать вам и быть одного окружения (все live ИЛИ все test) — смешанный список вернёт mixed_environment.

Endpoint

POST https://n8n.thenextgen.store/webhook/promotion-code-create
X-Partner-Api-Key: ваш-api-ключ
Content-Type: application/json

Ключ передаётся в header X-Partner-Api-Key — это unified стандарт EasyPay для MCP-совместимых ручек. Для совместимости с legacy-клиентами поле api_key в теле тоже поддерживается, но header — рекомендуемый способ.

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

{
  "stripe_product_ids": ["prod_XXXXXXXXXXXX", "prod_YYYYYYYYYYYY"],
  "code": "BLACKFRIDAY",
  "percent_off": 25,
  "duration": "once",
  "max_redemptions": 100,
  "expires_at": "2026-12-01T00:00:00Z",
  "idempotency_key": "ваш-уникальный-ключ-этого-запроса"
}

Параметры:

Параметр

Обязательный

Описание

stripe_product_ids

Да

Массив из 1..50 ID одобренных продуктов партнёра в Stripe (prod_…). Все продукты должны быть одного окружения (live ИЛИ test).

code

Да

Текст, который клиент введёт на чекауте Stripe (1..500 символов; разрешены A-Z, a-z, 0-9, _, -). Stripe нормализует в верхний регистр и матчит case-insensitive. Текст должен быть уникальным в рамках вашего аккаунта — если такой код уже занят, вернётся code_already_exists.

percent_off

Да

Размер скидки в процентах. Целое число 1..100.

duration

Нет

Длительность скидки Stripe Coupon. Возможные значения: "once" (по умолчанию) — применяется к одному следующему платежу; "forever" — применяется к каждому биллинг-циклу подписки; "repeating" — применяется N месяцев (требует duration_in_months).

duration_in_months

Только если duration=repeating

Целое число ≥1. Сколько месяцев действует скидка для repeating. Для once / forever — поле запрещено.

max_redemptions

Нет

Максимальное число использований промокода всеми клиентами суммарно. Целое ≥1. Не передавайте — без лимита.

expires_at

Нет

Дата и время в ISO8601, когда Stripe перестанет принимать промокод на чекауте. Должно быть минимум 60 секунд в будущем. Не передавайте — без срока.

idempotency_key

Да

Партнёрский уникальный ключ запроса (1..128 символов). Один и тот же ключ + то же тело = безопасный replay. НЕ должен начинаться с ingest: — этот префикс зарезервирован под внутренние записи EasyPay.

source

Нет

Идентификатор источника для аналитики ("api" или "mcp"). По умолчанию "api". MCP-агент проставит "mcp" автоматически.

Что наследуется от продуктов (нельзя переопределить): is_test — все продукты должны быть одного окружения, оно и определяет, будет ли промокод создан в Stripe test mode или live mode.

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

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

{
  "success": true,
  "stripe_coupon_id": "AB12CD34",
  "stripe_promotion_code_id": "promo_1ABCDEF...",
  "code": "BLACKFRIDAY",
  "percent_off": 25,
  "duration": "once",
  "duration_in_months": null,
  "max_redemptions": 100,
  "expires_at": "2026-12-01T00:00:00Z",
  "stripe_product_ids": ["prod_XXXXXXXXXXXX", "prod_YYYYYYYYYYYY"],
  "is_test": false,
  "idempotency_key": "ваш-уникальный-ключ-этого-запроса",
  "created_at": "2026-05-13T16:41:20.096Z",
  "is_replay": false,
  "is_retry": false,
  "retry_count": 0
}

Поле

Описание

code

Финальный (нормализованный к верхнему регистру) текст промокода, который клиент вводит на чекауте. На случай разницы регистров отдавайте клиенту именно это значение.

stripe_coupon_id

ID Stripe Coupon — внутренний объект скидки в Stripe. Может пригодиться для сверки в Stripe Dashboard или будущей деактивации.

stripe_promotion_code_id

ID Stripe Promotion Code (promo_...) — это и есть «текст-обёртка» поверх Coupon. Сохраните его на своей стороне — он нужен для деактивации промокода через будущую ручку deactivate_partner_stripe_promotion_code.

is_test

true, если промокод создан в Stripe test mode (вы передавали prod_* тестовых продуктов). На реальных клиентах работать не будет — для production нужны live-продукты.

is_replay

true, если запрос с этим idempotency_key уже был обработан и вы получили закэшированный результат. Полезно для отладки.

is_retry

true, если EasyPay делал внутренние retry-попытки до Stripe (типично при сетевых сбоях). На корректность ответа не влияет.

retry_count

Сколько внутренних retry-попыток было сделано до успеха (0 для чистой первой попытки).

created_at

UTC-таймстамп первоначального создания row в EasyPay (не Stripe). На replay не меняется.

Ошибки

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

error_code

Когда срабатывает

invalid_input

Невалидное тело запроса: пропущенные поля, percent_off вне 1..100, code не соответствует ^[A-Za-z0-9_-]+$, expires_at в прошлом, duration_in_months без duration=repeating, idempotency_key начинается с ingest: и т.п.

unauthorized

Невалидный API-ключ или партнёр-аккаунт неактивен

forbidden

У партнёра нет разрешения product_create

product_not_found

Один из stripe_product_ids не найден в вашей Internal Tariffs (продукт не ваш или не существует). Сообщение содержит конкретный prod_…, который не прошёл.

product_not_approved

Один из продуктов ещё не прошёл модерацию (нет Stripe_Product_ID в Internal Tariffs).

product_data_corrupt

У продукта повреждены данные в Internal Tariffs — обратитесь в команду заботы.

mixed_environment

Переданы prod_* из разных окружений (часть test, часть live). Разделите на два запроса.

code_already_exists

Текст промокода уже занят на вашем Stripe-аккаунте. Возьмите более уникальный (например, с префиксом бренда).

idempotency_conflict

Тот же idempotency_key уже использовался с другим телом запроса. Сгенерируйте новый ключ для нового тела.

stripe_error

Сбой на стороне Stripe (4xx — детали в error_message; 5xx — EasyPay автоматически ретраит и не вернёт эту ошибку, если успели восстановиться).

internal_error

Сбой EasyPay. Безопасно повторить запрос с тем же idempotency_key — это либо вернёт уже созданный промокод, либо продолжит работу с той точки, где она прервалась.

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

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

  • Тот же **idempotency_key** + то же тело запроса. Возвращается тот же промокод. В ответе is_replay: true. Никаких дублей в Stripe. Лимит на 24 часа — после этого та же комбинация ключ+тело может создать новый промокод.

  • Тот же **idempotency_key** + другое тело. Возвращается idempotency_conflict. Никаких изменений в Stripe. Сгенерируйте новый ключ для нового тела.

  • Сетевой сбой посреди обработки (вы не получили ответ). Повторите с тем же ключом и телом — EasyPay подхватит работу с того места, где она прервалась.

  • Лимит ретраев. На один idempotency_key допустимо 5 неудачных попыток. После шестой ключ блокируется как failed_terminal — следующие запросы вернут закэшированную ошибку. Чтобы повторить — сгенерируйте новый idempotency_key.

Привязка к нескольким продуктам

Промокод применяется на чекауте только к тем продуктам, чьи prod_* перечислены в stripe_product_ids. Например, если у вас три продукта (Basic, Pro, Enterprise) и вы создали промокод с stripe_product_ids: ["prod_Pro", "prod_Enterprise"], то клиент введёт код только при покупке Pro или Enterprise — на Basic он не сработает.

Это удобно для маркетинговых акций «скидка на премиум-планы» без необходимости создавать отдельный промокод под каждый продукт.

Ограничения

  • Endpoint доступен только для уже одобренных продуктов партнёра. Для нового продукта сначала проведите его через мини-апп (модерация).

  • 1..50 продуктов в одном промокоде.

  • code должен быть уникальным в рамках вашего Stripe-аккаунта.

  • idempotency_key 1..128 символов, не начинается с ingest:.

  • Все привязываемые продукты — одного окружения (test ИЛИ live).

  • Сохраняйте code и stripe_promotion_code_id на своей стороне сразу после создания промокода.

  • Деактивация — через будущую ручку deactivate_partner_stripe_promotion_code (пока — через команду заботы EasyPay).

  • MVP: только percent_off. Фиксированной суммы (amount_off), бесплатной доставки, currency restrictions — пока нет.

Пример на Python

import urllib.request
import json
import uuid

url = "https://n8n.thenextgen.store/webhook/promotion-code-create"
payload = {
    "stripe_product_ids": ["prod_XXXXXXXXXXXX", "prod_YYYYYYYYYYYY"],
    "code": "BLACKFRIDAY",
    "percent_off": 25,
    "duration": "once",
    "max_redemptions": 100,
    "expires_at": "2026-12-01T00:00:00Z",
    "idempotency_key": str(uuid.uuid4())
}

req = urllib.request.Request(
    url,
    data=json.dumps(payload).encode("utf-8"),
    headers={
        "X-Partner-Api-Key": "ваш-api-ключ",
        "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"Promo code to share with customers: {result['code']}")
    print(f"Stripe promo id: {result['stripe_promotion_code_id']}")
    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/promotion-code-create \
  -H "X-Partner-Api-Key: ваш-api-ключ" \
  -H "Content-Type: application/json" \
  -d '{
    "stripe_product_ids": ["prod_XXXXXXXXXXXX"],
    "code": "SUMMER25",
    "percent_off": 25,
    "duration": "once",
    "idempotency_key": "summer-2026-promo-001"
  }'

Подписки: пример с длительной скидкой

Для продуктов-подписок промокод может действовать несколько биллинг-циклов:

{
  "stripe_product_ids": ["prod_SUBSCRIPTION_PRODUCT_ID"],
  "code": "FIRST3MONTHS",
  "percent_off": 50,
  "duration": "repeating",
  "duration_in_months": 3,
  "max_redemptions": 200,
  "idempotency_key": "summer-2026-3mo-discount"
}

С таким промокодом первые 3 ежемесячных списания будут со скидкой 50%; дальше — полная цена.

Через MCP-агента

Если у вас подключён EasyPay MCP-сервер (см. статью про MCP), можно создать промокод одной фразой в чате:

Создай промокод BLACKFRIDAY со скидкой 25% на продукты prod_XXX и prod_YYY,
лимит 100 использований, до 1 декабря.

Агент сам соберёт payload и вызовет create_partner_stripe_promotion_code. Ключ передаётся транспортно через MCP-config — модель его не видит.

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

  • **unauthorized** — обновите ключ через мини-апп («Show again» на первом шаге онбординга или у вашего менеджера).

  • **product_not_found** для продукта, который вы видите в мини-апп — проверьте, что копируете именно stripe_product_id (формат prod_…), а не внутренний номер из Internal Tariffs. И что окружение совпадает.

  • **mixed_environment** — разбейте запрос на два: отдельный для test-продуктов, отдельный для live.

  • **code_already_exists** — текст уже занят. Возьмите более уникальный (например, добавьте префикс с названием бренда: MYBRAND-BLACKFRIDAY).

  • **idempotency_conflict** — сгенерируйте новый ключ. Не пытайтесь «починить» существующий: каждое тело запроса должно иметь свой ключ.

  • **invalid_input** с сообщением про reserved prefixidempotency_key не должен начинаться с ingest:. Сгенерируйте новый ключ (например, uuid.uuid4()).

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