Peptide-Pay API
카드 및 암호화폐 결제를 처리하고, 여러분이 제어하는 USDC 월렛으로 정산하는 REST API. 한 번의 POST로 체크아웃을 생성합니다. 한 개의 웹훅이 결제 완료를 알려줍니다. 30분 이내에 출시하세요.
시작하기
Peptide-Pay는 네 가지 연동 모드를 지원합니다. 제약 조건에 맞는 것을 선택하세요. 하단의 API는 동일합니다.
드롭인 버튼, 가입 불필요. 요청 본문에 USDC 월렛 주소를 전달하세요. 백엔드 상태 제로. 공개되며 DevTools에 보입니다.
서버 대 서버. Bearer sk_live_ 뒤에 월렛을 숨기세요. 대시보드에서 브랜딩, 웹훅, 대량 페이아웃을 설정하세요.
기성 플러그인. ZIP 업로드, API 키 + 웹훅 시크릿 붙여넣기, 결제 시 주문 자동 완료. HPOS 준비 완료, WC 7.0+.
Custom App + Manual Payment Method. 기존 스토어에 약 30분 설치. Shopify Admin API를 통해 주문을 결제 완료로 표시합니다.
퀵스타트 (5분)
세 단계: 세션 생성, 고객 리다이렉트, 웹훅 처리. 아래 샘플은 프로덕션 준비 완료된 Node.js 체크아웃 라우트입니다.
// 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);그게 해피 패스의 전부입니다. 고객은 의 호스티드 체크아웃에 도착해 카드 또는 암호화폐를 선택하고, 여러분의 웹훅이 결제 후 30초 이내에 발사됩니다.
인증
연동 모드에 따라 두 가지 스킴:
| 스킴 | 방법 | 사용 시점 |
|---|---|---|
| Bearer 토큰 | Authorization: Bearer sk_live_… | 서버 사이드. 월렛을 비공개로 유지합니다. |
| 본문의 월렛 | { "wallet": "0x…", … } | 백엔드가 없는 정적 사이트 / 위젯. |
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 시 객체를 반환합니다.
#체크아웃 세션 생성
호스티드 체크아웃 URL을 생성합니다. 고객이 열어 카드 또는 암호화폐로 결제하고, Peptide-Pay가 여러분의 월렛에 USDC로 정산하며, 웹훅이 발사됩니다.
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| amount_cents | integer | 필수 | 통화의 최소 단위 금액 (센트). 범위 100 – 10 000 000. |
| currency | string | 필수 | ISO 4217 코드. 지원: EUR, USD, GBP, CAD, AUD, CHF. |
| wallet | string | one-of | Polygon의 USDC 월렛 (0x + 40 hex). Bearer 키 인증 시에는 선택. |
| customer_email | string | 선택 | 체크아웃 UI에 표시되고 KYC 재사용을 위해 온램프로 전달됩니다. |
| success_url | url | 선택 | 결제 성공 후 리다이렉트. http/https만 허용. |
| cancel_url | url | 선택 | 고객이 체크아웃을 포기할 경우 리다이렉트. |
| webhook_url | url | 선택 | order.paid 이벤트용 POST 타겟. 대시보드 기본값을 덮어씁니다. |
| provider | string | 선택 | 기본 'gateway' (스마트 피커 — 권장). 또는 GET /providers에서 특정 온램프 id를 고정 (예: moonpay, revolut, banxa, transak). |
| product_name | string | 선택 | 체크아웃 페이지에 표시되는 라벨 (최대 80자). |
| metadata | object | 선택 | 최대 10개의 문자열 키/값 쌍, 웹훅에서 그대로 반환됩니다. 예약된 키: order_id. |
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| id | string | 선택 | 세션 id, cs_로 시작합니다. |
| url | string | 선택 | 고객을 리다이렉트할 호스티드 체크아웃 URL. |
| status | string | 선택 | 생성 시 항상 "pending". |
| amount | integer | 선택 | amount_cents의 에코. |
| currency | string | 선택 | currency의 에코. |
| provider | string | 선택 | provider의 에코 (기본값 'gateway'). |
| expires_at | string | 선택 | ISO 8601 만료 시각 (생성 후 24시간). |
| tracking_number | string | 선택 | Polygon 정산 주소 — 웹훅 페이로드의 address_in과 일치하며 라이브 모니터링을 위해 /track에서 사용 가능. |
예제
// 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);#세션 조회 (폴링)
웹훅 폴백으로 사용하거나, 리다이렉트 후 성공 페이지를 하이드레이션할 때 사용하세요. 서버는 매 호출마다 정산 레이어를 재확인합니다 — 저렴하므로 (<200ms) 3-5초마다 폴링해도 괜찮습니다.
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| id | string | 선택 | 세션 id. |
| status | string | 선택 | pending | paid | expired | failed. |
| amount | integer | 선택 | 원래 금액 (센트). |
| currency | string | 선택 | 원래 통화. |
| paid_at | string|null | 선택 | 온체인 정산 완료 시각 (ISO 8601). |
| paid_provider | string|null | 선택 | 실제로 결제를 처리한 프로바이더 (요청된 것과 다를 수 있음). |
| txid | string|null | 선택 | Polygon 정산 txid. 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');
}#실시간 프로바이더 매트릭스
현재 트래픽을 수락 중인 온램프를 프로바이더별 최소 금액과 함께 나열합니다. 엣지에서 5분 캐시 — 요청마다가 아닌 앱 부트 시 한 번 폴링하세요.
| 필드 | 타입 | 필수 | 설명 |
|---|---|---|---|
| providers[].id | string | 선택 | 프로바이더 키 (/checkout/init에서 `provider`로 전달 가능). |
| 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.웹훅
세션이 종료 상태에 도달하면 설정된 (세션별 또는 대시보드)로 서명된 JSON 이벤트를 POST합니다. 시그니처 검증을 위해 항상 요청 본문을 파싱하세요 — 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 | 온체인 정산 확인됨. + + 이 반드시 존재합니다. 주문을 결제 완료로 표시하세요. |
현재 만 전송됩니다 — 만료되거나 실패한 세션은 를 통해 관찰 가능합니다 (24h TTL 이후 status는 가 되고, 종료 실패는 로 표시됨). 향후 릴리스에서 이들을 위한 푸시 이벤트를 추가할 수 있습니다.
시그니처 검증
가입 계정이 있는 가맹점은 시크릿을 받으며, 모든 전달에는 형식의 헤더가 포함됩니다. 을 계산하고 상수-시간 방식으로 과 비교하세요. 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');
}재시도 정책
2xx가 아닌 응답(및 5초 초과 타임아웃)은 지수 백오프로 재시도합니다. ~42시간에 걸쳐 총 6회:
- 시도 1 — 확인 즉시.
- 시도 2 — +5분.
- 시도 3 — +15분.
- 시도 4 — +1시간.
- 시도 5 — +4시간.
- 시도 6 — +12시간, 그 후 +24시간 (최종).
6회 실패 후 이벤트는 데드레터됩니다. 를 통해 언제든지 현재 상태를 재요청할 수 있습니다.
일반적인 이슈
- 모든 전달에서 'invalid signature'가 표시됩니다
- 프레임워크가 해시 전에 본문을 JSON으로 파싱했습니다. RAW 바이트를 읽으세요 (Express: express.raw({type:'*/*'}); Next.js: req.text(); Laravel: request()->getContent(); Rails: request.raw_post). 해싱 전에 재직렬화하지 마세요.
- IP 화이트리스트 — 어떤 IP에서 보내시나요?
- 전달은 현재 Vercel Edge(동적 IP)에서 발송됩니다. 정적 범위를 공개하지 않습니다. 반드시 화이트리스트가 필요하다면, 시그니처 헤더를 인증 게이트로 사용하고 모든 소스 IP를 허용하세요 — HMAC이 진짜 신원 확인입니다.
- HTTPS 필수?
- 네. http:// 엔드포인트로는 POST를 거부합니다 (혼동된 대리인 / 평문 재전송 위험). ngrok의 무료 https URL은 로컬 테스트에서 잘 작동합니다.
- 엔드포인트가 느립니다 — 5초 타임아웃을 연장할 수 있나요?
- 아니요. 즉시 2xx를 응답한 다음 비동기로 처리하세요 (작업 큐, setImmediate, 고루틴). 긴 블로킹 핸들러는 항상 타임아웃됩니다.
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전체 타입, 웹훅 헬퍼, 자동 재시도.
모든 언어한 번의 POST, 한 번의 GET. 라이브러리 불필요.
수수료
고정 — Peptide-Pay의 전체 커미션. 구독 없음, 월 정액 없음, 차지백 수수료 없음. 카드 온램프 수수료(업스트림 카드 프로세서가 부과하는 ~4.5%)는 패스스루입니다 — 고객이 지불하며, 여러분의 페이아웃에는 절대 영향을 주지 않습니다.
| 결제 수단 | 여러분이 지불 | 고객이 지불 |
|---|---|---|
| 카드 / Apple Pay / Google Pay | 3% | ~4.5% (온램프, 패스스루) |
| 암호화폐 직접 (USDC → USDC) | 3% | 가스비만 (~$0.01 on Polygon) |
상세 계산 예시: /fees.
테스트
모든 신규 가맹점 계정은 을 무료로 받습니다 — 3% 수수료 전액이 24시간 이내에 월렛으로 환불됩니다. 라이브 전환 전에 전체 플로우를 엔드투엔드(실제 카드, 실제 USDC, 실제 웹훅)로 리허설할 때 사용하세요.
- 샌드박스 모드는 자동입니다: 가맹점당 첫 3개의 유료 세션이 로 표시되어 자동 환불 대상이 됩니다.
- MoonPay 개발용 카드: , 아무 미래 유효기간, 아무 CVV, ZIP 10001.
- 로컬 웹훅 테스트: ngrok으로 localhost를 노출하고, URL을 세션별 필드에 붙여넣으세요.
전체 로컬 루프 (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.에러 & 레이트 리밋
모든 에러는 구조를 공유합니다. 상태 코드는 표준 REST입니다.
400401403404429502트러블슈팅
- 대시보드에서 세션이 'paid'이지만 웹훅이 발사되지 않았어요
- webhook_url이 공개 HTTPS로 접근 가능한지 확인하세요 (LAN 외부에서 curl해보세요). 올바르다면 GET /sessions/{id}로 폴링해 상태를 확인하세요 — 대시보드 /app이 웹훅 전달 통계(성공률, 카운트)를 보여줍니다. 데드레터 전까지 42시간에 걸쳐 6회 시도; 폴링으로 언제든지 재동기화할 수 있습니다.
- HMAC 불일치 — 시그니처가 항상 무효입니다
- 99%의 경우: 재직렬화된 본문을 해싱하고 있습니다. raw 바이트를 해시해야 합니다. 프레임워크가 핸들러 실행 전에 JSON을 자동 파싱합니다; raw 버퍼가 필요합니다. Next.js: .json() 전에 req.text(). Express: app.use('/webhooks', express.raw({ type: '*/*' }), …). Rails: request.raw_post. 또한 `HMAC(whsec_secret, t + '.' + rawBody)`를 계산하고 있는지 확인하세요 — `HMAC(whsec_secret, rawBody)`만이 아닙니다. 타임스탬프 접두사가 필수입니다.
- MoonPay가 '해당 국가에서 서비스를 사용할 수 없음'이라고 말합니다
- MoonPay는 ~20개국(이란, 북한, 쿠바, 자세한 목록은 그들의 사이트)을 제한합니다. 기본 프로바이더는 'gateway'입니다 — 스마트 피커가 다른 지역을 커버하는 Revolut, Transak, 또는 Banxa로 자동 폴백합니다. provider: 'moonpay'로 특정 프로바이더를 고정한 경우, 제거하고 라우터가 선택하도록 하세요.
- 'paid' 이벤트 이후 월렛에 USDC를 받지 못했어요
- polygonscan.com/address/<your-wallet>에서 USDC (Polygon POS) 전송을 확인하세요. 정산은 97%가 여러분에게, 3%가 Peptide-Pay로 도착합니다 - 97% 인바운드가 보이지 않으면 init 호출에 잘못된 월렛을 붙여넣었을 수 있습니다. GET /sessions/{id}로 재확인하세요 - txid 필드가 실제 온체인 전송을 가리킵니다.
- 고객이 두 번 청구되었어요
- 일어나면 안 됩니다. 각 세션은 하나의 정산 addressIn을 가집니다; 동일 주소로의 두 번째 결제는 저희 측에서 별도 세션이 되며 첫 번째만 주문에 크레딧됩니다. 발생하면 두 polygonscan txid + 세션 id 스크린샷을 hi@peptide-pay.com으로 보내주세요 - 트레저리에서 중복분을 환불합니다.
- 502 'Payment infrastructure temporarily unavailable'가 표시됩니다
- 저희 정산 업스트림이 저하됨 (요청의 < 0.5%). 동일한 Idempotency-Key로 30초 후 재시도하세요 - 월렛이 성공적으로 생성되는 즉시 캐시가 원래 응답을 반환합니다. 라이브 인시던트는 /status에서 추적하세요.
연동 준비되셨나요?
대부분의 가맹점은 가입부터 첫 유료 거래까지 30분 이내에 도달합니다.