開発者リファレンス

Peptide-Pay API

カードと暗号資産で決済を行い、あなたが管理するUSDCウォレットに着金するためのREST API。1回のPOSTでチェックアウトを作成。1つのウェブフックで支払い通知。30分以内にリリース可能。

REST · JSONBearer認証HMAC-SHA256ウェブフックIdempotency-KeyCORS対応

はじめに

クイックスタート (5分)

3ステップ:セッション作成、お客様をリダイレクト、ウェブフックを処理。以下は本番利用可能なNode.jsチェックアウトルートのサンプルです。

app/checkout/route.ts
// 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秒以内にウェブフックが発火します。

認証

統合モードに応じた2つの方式:

方式方法タイミング
BearerトークンAuthorization: Bearer sk_live_…サーバーサイド。ウォレットを非公開に保ちます。
ボディ内ウォレット{ "wallet": "0x…", … }バックエンドのない静的サイト / ウィジェット。
注意
APIキーはマーチャントアカウントの全権限を持ちます — パスワードのように扱ってください。コミット禁止、ブラウザに送信禁止、漏洩時は /app/api-keys でローテーションしてください。

APIリファレンス

ベースURL: 。すべてのエンドポイントはJSONで通信し、成功時は単一オブジェクト、4xx/5xx時はオブジェクトを返します。

POSThttps://peptide-pay.com/api/v1/checkout/init

#チェックアウトセッションを作成

ホステッド・チェックアウトURLを発行します。お客様がそれを開き、カードまたは暗号資産で支払うと、Peptide-PayがあなたのウォレットにUSDCで決済し、ウェブフックが発火します。

リクエストボディ
フィールド必須説明
amount_centsinteger必須通貨の最小単位での金額(セント)。範囲 100 – 10,000,000。
currencystring必須ISO 4217コード。対応:EUR、USD、GBP、CAD、AUD、CHF。
walletstringいずれかPolygon上のUSDCウォレット(0x + 40桁のhex)。Bearerキーで認証されていない場合は必須。
customer_emailstring任意チェックアウトUIに表示され、KYC再利用のためオンランプに転送されます。
success_urlurl任意支払い成功後のリダイレクト先。http/httpsのみ。
cancel_urlurl任意お客様がチェックアウトを放棄した場合のリダイレクト先。
webhook_urlurl任意order.paidイベントのPOST先。ダッシュボードのデフォルトを上書きします。
providerstring任意デフォルトは 'gateway' (スマートピッカー — 推奨)。またはGET /providersから特定のオンランプIDを固定(例:moonpay、revolut、banxa、transak)。
product_namestring任意チェックアウトページに表示されるラベル(最大80文字)。
metadataobject任意最大10組の文字列キー/値ペア。ウェブフックでそのまま返却されます。予約キー:order_id。
レスポンス (200 OK)
フィールド必須説明
idstring任意セッションID、cs_で始まります。
urlstring任意お客様をリダイレクトするホステッド・チェックアウトURL。
statusstring任意作成時は常に "pending"。
amountinteger任意amount_centsのエコー。
currencystring任意currencyのエコー。
providerstring任意providerのエコー(デフォルト 'gateway')。
expires_atstring任意ISO 8601形式の有効期限(作成から24時間)。
tracking_numberstring任意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);
ヒント
を渡すとリトライ安全になります。同じキーに対し24時間キャッシュされたレスポンスを再生する代わりに新セッションを作成しません(不安定なネットワークでの二重課金を防止)。
GEThttps://peptide-pay.com/api/v1/sessions/{id}

#セッションを取得(ポーリング)

ウェブフックのフォールバックとして、またはリダイレクト後に成功ページをハイドレートするのに使用。サーバーサイドで毎回決済レイヤを再確認 — 安価(200ms未満)のため、3〜5秒ごとのポーリングで問題ありません。

レスポンス
フィールド必須説明
idstring任意セッションID。
statusstring任意pending | paid | expired | failed。
amountinteger任意元の金額(セント)。
currencystring任意元の通貨。
paid_atstring|null任意オンチェーン決済完了時のISO 8601タイムスタンプ。
paid_providerstring|null任意実際に支払いを処理したプロバイダ(リクエストされたものと異なる場合があります)。
txidstring|null任意Polygon決済のtxid。polygonscan.com/tx/{txid}でリンクしてください。
expires_atstring任意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');
}
GEThttps://peptide-pay.com/api/v1/providers

#ライブプロバイダーマトリクス

現在トラフィックを受け付けているオンランプの一覧と、プロバイダーごとの最低金額。エッジで5分キャッシュ — アプリ起動時に1回ポーリング、リクエストごとではありません。

レスポンス
フィールド必須説明
providers[].idstring任意プロバイダーキー(/checkout/initの`provider`として渡せます)。
providers[].provider_namestring任意ドロップダウン用の人間向けラベル。
providers[].statusstring任意'active'(このエンドポイントは常にアクティブでフィルタ済み)。
providers[].minimum_currencystring任意最低金額のISOコード。
providers[].minimum_amountnumber任意プロバイダーが受け付ける最低金額(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オンチェーン決済確認時。 + + は必ず存在。注文を支払い済みにマーク。

現在のみ配送 — 期限切れ・失敗セッションはで観測可能(24時間TTL後、終端の失敗は)。将来的にこれらへのプッシュイベントを追加する可能性があります。

署名検証

サインアップアカウントを持つマーチャントにはシークレットが付与され、すべての配送には形式のヘッダが含まれます。を計算し、と定数時間比較してください。5分以上古いものは拒否。

ヒント
ウォレットのみフロー(サインアップなし、なし)のウェブフックは未署名で配送されます — それでもがあなたが作成したセッションと一致することを検証してください。署名付き配送には /signup でシークレットを取得。
// 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');
}
注意
常に定数時間比較を使用してください 言語別:(Node)、(Python)、(PHP)、(Ruby)。単純なはタイミング攻撃者にHMACを1バイトずつ漏らします。

リトライポリシー

非2xxレスポンス(および5秒超タイムアウト)を指数バックオフでリトライ。約42時間で計6回:

  • 試行1 — 確認直後すぐ。
  • 試行2 — +5分。
  • 試行3 — +15分。
  • 試行4 — +1時間。
  • 試行5 — +4時間。
  • 試行6 — +12時間、その後+24時間(最終)。

6回の試行が失敗するとイベントはデッドレター化されます。で現状をいつでも再取得できます。

ヒント
ハンドラは冪等にしてください。で重複排除 — すでに処理した支払い済みイベントを再送する可能性があります。

よくある問題

すべての配送で「invalid signature」が出る
フレームワークがハッシュ前にボディをJSONパースしています。生のバイトを読んでください(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は、ローカルテストで問題なく動作します。
エンドポイントが遅い — 5秒タイムアウトを延長できる?
いいえ。即座に2xxで応答し、非同期で処理してください(ジョブキュー、setImmediate、goroutine)。長時間ブロックするハンドラは必ずタイムアウトします。

SDK

APIはで十分小さいです — しかしNode SDKは型、自動リトライ、署名検証を代行するヘルパーを提供します。

npm install github:kinerette/peptide-pay-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);
});
Node / TypeScript安定版
peptide-pay

完全な型、ウェブフックヘルパー、自動リトライ。

直接fetch()常に動作
任意の言語

1回のPOSTと1回のGET。ライブラリ不要。

ヒント
Python、PHP、Ruby、Go SDKはロードマップに含まれています。リリースまでは、上記の生//サンプルが正典リファレンス — 破壊的変更はしません。

手数料

定額 — Peptide-Payの全コミッション。サブスクリプションなし、月額なし、チャージバック手数料なし。カードオンランプ手数料(上流のカードプロセッサが課す約4.5%)はパススルー — お客様が支払い、あなたのペイアウトに触れません。

支払い方法あなたの負担お客様の負担
カード / Apple Pay / Google Pay3%約4.5%(オンランプ、パススルー)
暗号資産直接(USDC → USDC)3%ガス代のみ(Polygonで約$0.01)

詳細な内訳と計算例は /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に準拠。

400
無効なJSONまたは欠損フィールド
ボディの形式不正、金額が数値でない、ウォレットが0xアドレスでない、通貨が非対応。
401
無効または失効したAPIキー
Bearerトークンがマーチャントに解決されません。/app/api-keysでローテーション。
403
コールバック署名が不正
内部 — 当社の決済IPNが正しいセッションごとの署名なしでウェブフック受信機にヒット。通常運用ではマーチャント側には見えないエラーです。
404
セッションが見つかりません
IDが誤っているか、セッションが剪定された(終端状態から90日超)。
429
レート制限超過
initは60 req/分/IP、selectは30 req/分/IP。Retry-Afterヘッダを付与。上位ティアについてはサポートに連絡してください。
502
上流利用不可
決済ネットワークが一時的に劣化。同じIdempotency-Keyで30秒後にリトライ。SLA目標:99.5%以上。

トラブルシューティング

ダッシュボードでセッションが「paid」なのにウェブフックが発火しない
webhook_urlがパブリックHTTPSで到達可能か確認(LAN外からcurl)。正しければGET /sessions/{id}でステータスをポーリング確認 — ダッシュボード/appでウェブフック配送統計(成功率、回数)が表示されます。デッドレターまで42時間で6試行。ポーリングで常に再同期可能。
HMAC不一致 — 署名が常に無効
99%の原因:生バイトではなく再シリアライズ済みボディをハッシュしています。フレームワークはハンドラ実行前にJSONを自動パースするので、生バッファが必要です。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フィールドを確認 - オンチェーン転送の実体を指します。
お客様が二重請求された
起きないはずです。各セッションには1つの決済addressInがあり、同じアドレスへの2回目の支払いは当社側で別セッションとなり、最初のもののみ注文に計上されます。発生した場合、2つのpolygonscan txidとセッションIDのスクリーンショットをhi@peptide-pay.comにメール - トレジャリーから重複分を返金します。
「Payment infrastructure temporarily unavailable」の502が返る
決済上流が劣化(リクエストの0.5%未満)。同じIdempotency-Keyで30秒後にリトライ - ウォレット発行成功次第、キャッシュが元のレスポンスを返します。ライブインシデントは/statusで追跡。

導入準備はできましたか?

ほとんどのマーチャントはゼロから最初の支払い完了まで30分未満です。