Peptide-Pay API
REST API для приёма карт и крипты с расчётом в USDC на ваш кошелёк. Один POST создаёт checkout. Один вебхук сообщает об оплате. Запуск меньше чем за 30 минут.
С чего начать
Peptide-Pay поддерживает четыре режима интеграции. Выбирайте под свои ограничения — API под капотом одинаковый.
Drop-in кнопка, без регистрации. Передавайте свой USDC-адрес в теле запроса. Ноль backend-состояния; публично, видно в DevTools.
Server-to-server. Прячьте кошелёк за Bearer sk_live_. Настраивайте брендинг, вебхуки, массовые payout'ы из дашборда.
Готовый плагин. Загружаете ZIP, вставляете API-ключ + webhook-секрет, заказы автоматически завершаются при оплате. HPOS-ready, WC 7.0+.
Custom App + Manual Payment Method. ~30 минут установки на существующий магазин. Отмечаем заказы как оплаченные через Shopify Admin API.
Быстрый старт (5 мин)
Три шага: создать сессию, редиректнуть клиента, обработать вебхук. Пример ниже — готовый к проду Node.js checkout-роут.
// Create a checkout session and redirect your customer.
// Authorization resolves the merchant wallet server-side — no wallet in the body.
const res = await fetch('https://peptide-pay.com/api/v1/checkout/init', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.PEPTIDEPAY_API_KEY}`, // sk_live_…
},
body: JSON.stringify({
amount_cents: 5000, // €50.00 — integer, in cents
currency: 'EUR', // EUR, USD, GBP, CAD, AUD, CHF
email: 'buyer@example.com', // optional, shown in checkout
success_url: 'https://mystore.com/success',
cancel_url: 'https://mystore.com/cart',
webhook_url: 'https://mystore.com/api/peptidepay-webhook',
metadata: { order_id: '1234' },
}),
});
const { id, url, tracking_number } = await res.json();
// => { id: 'cs_abc…', url: 'https://peptide-pay.com/session/cs_abc…',
// tracking_number: '0x…', provider: 'gateway', status: 'pending', … }
// Redirect your customer to the hosted checkout.
return Response.redirect(url, 303);Это весь happy path. Клиент попадает на hosted checkout на , выбирает карту или крипту, а ваш вебхук срабатывает в течение 30с после оплаты.
Аутентификация
Две схемы — в зависимости от режима интеграции:
| Схема | Как | Когда |
|---|---|---|
| Bearer-токен | Authorization: Bearer sk_live_… | Server-side. Кошелёк остаётся приватным. |
| Кошелёк в body | { "wallet": "0x…", … } | Статические сайты / виджеты без backend. |
sk_live_… and an sk_test_… key. Use sk_live_ as your canonical key — that is what every example here uses. The sk_test_ key is provided for the webhook-receiver simulator at /api/v1/test/fire-webhook. Peptide-Pay settles real on-chain USDC — there is no test network. To dry-run an integration, run a $1 real payment and refund yourself.API-референс
Base URL: . Все эндпоинты говорят JSON, возвращают один объект на успех и на 4xx/5xx.
#Создать checkout-сессию
Создаёт URL hosted checkout. Клиент открывает, платит картой или криптой, Peptide-Pay рассчитывается вам в USDC, срабатывает вебхук.
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| amount_cents | integer | обязательно | Сумма в наименьшей единице валюты (центы). Диапазон 100 – 10 000 000. |
| currency | string | обязательно | Код ISO 4217. Поддерживаются: EUR, USD, GBP, CAD, AUD, CHF. |
| wallet | string | one-of | USDC-кошелёк на Polygon (0x + 40 hex). Обязательно, если не аутентифицированы через Bearer-ключ. |
| customer_email | string | опционально | Показывается в UI checkout и прокидывается в on-ramp для переиспользования KYC. |
| success_url | url | опционально | Редирект после успешной оплаты. Только http/https. |
| cancel_url | url | опционально | Редирект, если клиент бросил checkout. |
| webhook_url | url | опционально | POST-таргет для событий order.paid. Переопределяет дефолт из дашборда. |
| provider | string | опционально | По умолчанию 'gateway' (умный пикер — рекомендуется). Или зафиксируйте конкретный on-ramp id из GET /providers (например moonpay, revolut, banxa, transak). |
| product_name | string | опционально | Лейбл на странице checkout (макс 80 символов). |
| metadata | object | опционально | До 10 key/value-пар строк, возвращаются в вебхуке. Зарезервированный ключ: order_id. |
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| id | string | опционально | ID сессии, начинается с cs_. |
| url | string | опционально | URL hosted checkout для редиректа клиента. |
| status | string | опционально | При создании всегда "pending". |
| amount | integer | опционально | Echo amount_cents. |
| currency | string | опционально | Echo currency. |
| provider | string | опционально | Echo provider (по умолчанию 'gateway'). |
| expires_at | string | опционально | ISO 8601 срок жизни (24ч с создания). |
| tracking_number | string | опционально | Polygon settlement-адрес — совпадает с address_in в payload вебхука, пригоден для /track для live-мониторинга. |
Примеры
// Node.js 18+
const res = await fetch('https://peptide-pay.com/api/v1/checkout/init', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${process.env.PEPTIDEPAY_API_KEY}`,
'Idempotency-Key': crypto.randomUUID(), // safe double-submit
},
body: JSON.stringify({
amount_cents: 5000,
currency: 'EUR',
email: 'buyer@example.com',
success_url: 'https://mystore.com/success',
cancel_url: 'https://mystore.com/cart',
metadata: { order_id: '1234' },
}),
});
if (!res.ok) throw new Error(`HTTP ${res.status}`);
const { id, url } = await res.json();
return Response.redirect(url, 303);#Получить сессию (polling)
Используйте как fallback для вебхука или для гидратации success-страницы после редиректа. Каждый вызов server-side пере-проверяет наш settlement-слой — дёшево (<200мс), так что polling каждые 3-5с ок.
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| id | string | опционально | ID сессии. |
| status | string | опционально | pending | paid | expired | failed. |
| amount | integer | опционально | Исходная сумма в центах. |
| currency | string | опционально | Исходная валюта. |
| paid_at | string|null | опционально | ISO 8601, когда on-chain settlement завершился. |
| paid_provider | string|null | опционально | Какой провайдер реально обработал платёж (может отличаться от запрошенного). |
| txid | string|null | опционально | Txid Polygon settlement. Линк через polygonscan.com/tx/{txid}. |
| expires_at | string | опционально | ISO 8601 срок жизни. |
// Poll every 3-5 seconds until terminal state. Use webhooks for push-
// delivery in production; polling is the fallback when webhooks are down.
async function waitForPayment(sessionId, { timeoutMs = 15 * 60 * 1000 } = {}) {
const deadline = Date.now() + timeoutMs;
while (Date.now() < deadline) {
const res = await fetch(`https://peptide-pay.com/api/v1/sessions/${sessionId}`);
const s = await res.json();
if (s.status === 'paid') return s; // terminal: success
if (s.status === 'expired') throw new Error('Session expired');
if (s.status === 'failed') throw new Error('Payment failed');
await new Promise(r => setTimeout(r, 4000));
}
throw new Error('Polling timeout');
}#Live-матрица провайдеров
Список on-ramp'ов, принимающих трафик, с per-provider минимальными суммами. Кешируется 5 мин на edge — опрашивайте один раз при старте приложения, не на каждый запрос.
| Поле | Тип | Обязательно | Описание |
|---|---|---|---|
| providers[].id | string | опционально | Ключ провайдера (можно передавать как `provider` в /checkout/init). |
| providers[].provider_name | string | опционально | Человекочитаемый лейбл для выпадашки. |
| providers[].status | string | опционально | 'active' (на этом эндпоинте всегда фильтр на active). |
| providers[].minimum_currency | string | опционально | ISO-код минимальной валюты. |
| providers[].minimum_amount | number | опционально | Минимальная сумма, которую принимает провайдер (в единицах minimum_currency). |
curl -sS 'https://peptide-pay.com/api/v1/providers' | jq '.providers[] | {id, provider_name, minimum_currency, minimum_amount}'
# [
# { "id": "gateway", "provider_name": "Smart (recommended)", "minimum_currency": "USD", "minimum_amount": 1 },
# { "id": "moonpay", "provider_name": "Moonpay", "minimum_currency": "EUR", "minimum_amount": 20 },
# { "id": "revolut", "provider_name": "Revolut Ramp", "minimum_currency": "EUR", "minimum_amount": 10 },
# { "id": "binance", "provider_name": "Binance Pay", "minimum_currency": "EUR", "minimum_amount": 15 },
# …
# ]
# Cache: 5 minutes at the edge. Call once per deploy, not per request.Вебхуки
Когда сессия достигает терминального состояния, мы шлём POST с подписанным JSON-событием на , который вы настроили (per-session или в дашборде). Всегда парсите -тело запроса для верификации подписи — пере-сериализация JSON переупорядочивает ключи и ломает HMAC.
POST /your-endpoint HTTP/1.1
Host: mystore.com
Content-Type: application/json
x-peptidepay-signature: t=1745300551,v1=3f9b5c1e8a7d… ← HMAC-SHA256, hex
{
"event": "order.paid",
"session_id": "cs_abc123",
"order_id": "1234",
"address_in": "0xAb12…",
"status": "paid",
"amount": 5000,
"currency": "EUR",
"txid": "0xfa89b2…",
"paid_at": "2026-04-23T10:02:31.000Z",
"attempt": 1
}Типы событий
| Событие | Когда |
|---|---|
| order.paid | Подтверждён on-chain settlement. + + гарантированно присутствуют. Помечайте заказ оплаченным. |
Сейчас доставляется только — expired и failed сессии наблюдаемы через (status уходит в после 24ч TTL; терминальные сбои — ). Возможно, добавим push-события для них в будущем.
Верификация подписи
Мерчанты со signup-аккаунтом получают секрет , и каждая доставка несёт заголовок вида . Считайте и сравнивайте с constant-time. Отбрасывайте всё старше 5 минут.
// Node.js — Express/Next.js route handler
import crypto from 'node:crypto';
const SECRET = process.env.PEPTIDEPAY_WEBHOOK_SECRET; // dashboard → Webhooks
export async function POST(req) {
const rawBody = await req.text(); // MUST be the raw bytes
const header = req.headers.get('x-peptidepay-signature') ?? '';
const [ tPart, v1Part ] = header.split(',');
const t = tPart?.split('=')[1];
const v1 = v1Part?.split('=')[1];
if (!t || !v1) return new Response('bad sig', { status: 400 });
// Reject replays older than 5 minutes.
if (Math.abs(Date.now() / 1000 - Number(t)) > 300)
return new Response('stale', { status: 400 });
const expected = crypto
.createHmac('sha256', SECRET)
.update(`${t}.${rawBody}`)
.digest('hex');
const ok =
v1.length === expected.length &&
crypto.timingSafeEqual(Buffer.from(v1, 'hex'), Buffer.from(expected, 'hex'));
if (!ok) return new Response('invalid sig', { status: 401 });
const event = JSON.parse(rawBody);
// Idempotency: dedupe by event.session_id in your DB — retries re-fire
// the same event (with an incrementing "attempt" field) until you 2xx.
if (event.event === 'order.paid') {
await markOrderPaid(event.order_id, event.txid);
}
return new Response('ok');
}Политика повторов
Ретраим non-2xx ответы (и таймауты > 5с) на экспоненциальном backoff. Шесть попыток за ~42 часа:
- Попытка 1 — сразу при подтверждении.
- Попытка 2 — +5 минут.
- Попытка 3 — +15 минут.
- Попытка 4 — +1 час.
- Попытка 5 — +4 часа.
- Попытка 6 — +12 часов, затем +24 часа (финал).
После 6 неудачных попыток событие уходит в dead-letter. Текущее состояние всегда можно перезапросить через .
Частые проблемы
- На каждой доставке вижу 'invalid signature'
- Ваш фреймворк распарсил тело как JSON до того, как вы его захешировали. Читайте RAW-байты (Express: express.raw({type:'*/*'}); Next.js: req.text(); Laravel: request()->getContent(); Rails: request.raw_post). Никогда не пере-сериализуйте перед хешированием.
- IP whitelist — с каких IP вы шлёте?
- Доставки сейчас идут с Vercel Edge (динамические IP). Мы не публикуем статический диапазон. Если ОБЯЗАНЫ заwhitelist'ить — используйте заголовок подписи как auth-gate и принимайте любой source IP; HMAC — это и есть настоящая проверка идентичности.
- HTTPS обязателен?
- Да. Отказываемся POST'ить на http:// эндпоинты (risk confusable deputy / plaintext replay). Бесплатный https URL от ngrok ок для локального тестирования.
- Мой эндпоинт медленный — можно увеличить таймаут 5с?
- Нет. Отвечайте 2xx сразу, а обрабатывайте асинхронно (очередь, setImmediate, goroutine). Долгие блокирующие хендлеры всегда заканчиваются таймаутом.
SDK
API достаточно маленький, чтобы хватило с головой — но Node SDK даёт типы, автоматические ретраи и хелпер , который делает верификацию подписи за вас.
// npm install github:kinerette/peptide-pay-sdk
import { PeptidePay } from 'peptide-pay';
const pp = new PeptidePay(process.env.PEPTIDEPAY_API_KEY);
// Create a session
const session = await pp.checkout.create({
amount_cents: 5000,
currency: 'EUR',
customer_email: 'buyer@example.com',
success_url: 'https://mystore.com/success',
cancel_url: 'https://mystore.com/cart',
metadata: { order_id: '1234' },
});
// Retrieve a session
const latest = await pp.sessions.retrieve(session.id);
// Verify + parse a webhook (throws on invalid signature)
app.post('/webhooks/peptidepay', express.raw({ type: '*/*' }), (req, res) => {
const event = pp.webhooks.constructEvent(
req.body,
req.headers['x-peptidepay-signature'],
process.env.PEPTIDEPAY_WEBHOOK_SECRET,
);
// event.event === 'order.paid' (currently the only event delivered)
res.sendStatus(200);
});peptide-payПолные типы, webhook-хелпер, автоматические ретраи.
любой языкОдин POST, один GET. Библиотеки не нужны.
Комиссии
Flat — полная комиссия Peptide-Pay. Без подписки, без абонплаты, без chargeback-комиссий. Комиссии карт on-ramp (~4.5%, берёт upstream card-процессор) — pass-through, их платит клиент, они не трогают вашу выплату.
| Способ оплаты | Вы платите | Платит клиент |
|---|---|---|
| Карта / Apple Pay / Google Pay | 3% | ~4.5% (on-ramp, pass-through) |
| Крипта напрямую (USDC → USDC) | 3% | только gas (~$0.01 на Polygon) |
Полный разбор с примерами на /fees.
Тестирование
Каждый новый мерчант-аккаунт получает бесплатно — полные 3% возвращаются вам на кошелёк в течение 24ч. Прогоняйте полный flow end-to-end (реальная карта, реальный USDC, реальный вебхук) до запуска в прод.
- Sandbox-режим автоматический: первые 3 оплаченных сессии на мерчанта помечаются и идут на auto-refund.
- Тестовая карта MoonPay: , любая будущая дата, любой CVV, ZIP 10001.
- Локальное тестирование вебхука: проброс localhost через ngrok, вставьте URL в поле на сессии.
Полный локальный loop (ngrok)
# 1. Expose your local webhook endpoint
ngrok http 3000
# 2. Copy the https://xxxx.ngrok-free.app URL and paste it into
# Dashboard → Webhooks → Endpoint URL, OR send it inline:
curl -X POST 'https://peptide-pay.com/api/v1/checkout/init' \
-H "Authorization: Bearer $PEPTIDEPAY_API_KEY" \
-H 'Content-Type: application/json' \
-d '{
"amount_cents": 100,
"currency": "EUR",
"customer_email": "test+sandbox@yours.com",
"success_url": "https://yours.com/success",
"cancel_url": "https://yours.com/cart",
"webhook_url": "https://xxxx.ngrok-free.app/webhooks/peptidepay"
}'
# 3. Open the returned `url`, hit MoonPay's dev test card
# 4242 4242 4242 4242 (any future exp, any CVV).
# 4. Your local endpoint receives the signed POST within ~30s of payment.Ошибки и rate limits
Все ошибки — единой формы . Статус-коды стандартные REST.
400401403404429502Troubleshooting
- Сессия 'paid' в дашборде, но мой вебхук так и не стрельнул
- Проверьте, что webhook_url доступен по публичному HTTPS (curl'ните из-за LAN). Если ок — опросите GET /sessions/{id} для подтверждения статуса; дашборд /app показывает статистику доставки (success rate, счётчики). Шесть попыток за 42ч до dead-letter; всегда можно ре-синкнуть через polling.
- HMAC mismatch — подпись всегда невалидна
- 99% случаев: вы хешируете пере-сериализованное тело вместо raw-байтов. Фреймворки автопарсят JSON до хендлера; нужен сырой буфер. Next.js: req.text() до любого .json(). Express: app.use('/webhooks', express.raw({ type: '*/*' }), …). Rails: request.raw_post. Также проверьте, что считаете `HMAC(whsec_secret, t + '.' + rawBody)` — НЕ просто `HMAC(whsec_secret, rawBody)`. Префикс таймстемпа обязателен.
- MoonPay говорит 'service unavailable in your country'
- MoonPay ограничивает ~20 стран (Иран, Северная Корея, Куба, полный список на их сайте). Дефолтный провайдер — 'gateway', умный пикер авто-откатывается на Revolut, Transak или Banxa с другой гео-покрытием. Если зафиксировали конкретного провайдера через provider: 'moonpay' — уберите и дайте роутеру выбирать.
- Кошелёк не получил USDC после 'paid'-события
- Смотрите polygonscan.com/address/<your-wallet> на USDC (Polygon POS) переводы. Settlement уходит 97% вам и 3% Peptide-Pay — если не видите входящие 97%, возможно, вставили не тот кошелёк в init-вызов. Подтвердите через GET /sessions/{id} — поле txid указывает на реальный on-chain перевод.
- Клиента списали дважды
- Не должно происходить. У каждой сессии один settlement addressIn; второй платёж на тот же адрес станет отдельной сессией у нас, и только первую мы зачислим в ваш заказ. Если случилось — скриньте оба polygonscan-txid + session id и пишите на hi@peptide-pay.com — вернём дубль из treasury.
- Получаю 502 'Payment infrastructure temporarily unavailable'
- Наш settlement-upstream деградировал (< 0.5% запросов). Ретрайте через 30с с тем же Idempotency-Key — кеш вернёт исходный ответ, как только кошелёк успешно создастся. Следите за /status за live-инцидентами.
Готовы интегрироваться?
Большинство мерчантов проходят путь от нуля до первой оплаченной транзакции меньше чем за 30 минут.