エラー
すべての 4xx/5xx レスポンスは同じ JSON エンベロープを持ちます:
{
"code": 400,
"message": "invalid_input",
"details": [
{ "field": "items[0].unit_price", "message": "must be a positive decimal string" }
]
}code— 数値の HTTP ステータス (400、401、404、…)。汎用的な HTTP レイヤの処理には有用ですが、分岐ロジックにはmessageのほうを switch してください —codeだけでは、例えばinvalid_inputとpayment_method_not_supported(どちらも 400) を区別できません。message— lower-snake-case のセンチネル名で、内部のerrors.go定数から派生しています (例:INVALID_INPUT → "invalid_input")。リリース間で安定しています — こちらで switch してください。details— バリデーションエラー時に設定されます。{ field, message }の配列で、クライアントが入力にエラーをひも付けられるようになっています。それ以外では省略されます。
現在、ボディには trace_id や timestamp を返していません。本ページの以前のドラフトでは両方を約束していましたが、それは計画上のものでした。サーバーログとリクエストを突き合わせる必要がある場合は、レスポンスの Date ヘッダーとゲートウェイ側のレート制限ヘッダー (X-RateLimit-*) を控え、サポートチケットでそれらを引用してください。
ゲートウェイエッジでの拒否は別の形をしています。 上記のエンベロープはバックエンドサービスが返すものです。サービスに到達する 前 にゲートウェイで拒否されたリクエスト — /b2b/v1/* 呼び出しでの欠落 / 不正な X-Signature、未知の X-Client-ID、期限切れの X-Timestamp — は { "error": "...", "message": "..." } の形で返ります。ここで error は粗いスラグ (unauthorized / bad_request / service_unavailable)、message が詳細を運びます。数値の code も details もありません。したがって検証側はまず HTTP ステータスで分岐し、次に message を読み、code / details はリクエストがゲートウェイを通過した場合にのみ存在するものとして扱ってください。ゲートウェイのボディ例 (401):
{ "error": "unauthorized", "message": "invalid signature" }HTTP ステータス → 典型的なコード
| HTTP | 典型的な code 値 | 意味 |
|---|---|---|
| 400 | INVALID_INPUT、MISSING_REQUIRED、INVALID_FORMAT、INVALID_LENGTH、INVALID_VALUE、INVALID_USER_STATUS、INVALID_USER_ROLE、PAYMENT_METHOD_NOT_SUPPORTED、AMOUNT_BELOW_MINIMUM | 不正なリクエスト — details を確認 |
| 401 | INVALID_CREDENTIALS、INVALID_TOKEN、TOKEN_EXPIRED、INVALID_SIGNATURE (B2B); INVALID_OTP、OTP_EXPIRED、SESSION_EXPIRED (ダッシュボード / JWT のみ) | 認証失敗 — 不正なキー、期限切れタイムスタンプ、署名誤り。OTP/SESSION_EXPIRED コードはダッシュボード JWT ルート (/payment/v1/*) でのみ表面化し、純粋な B2B 統合では発生しません。 |
| 402 | INSUFFICIENT_CREDIT | マーチャントのプリペイド残高が尽きました — リトライ前にチャージしてください |
| 403 | FORBIDDEN、IP_BLOCKED | キーは有効ですが、この呼び出しに必要なスコープ / IP 許可がありません |
| 404 | NOT_FOUND、RECORD_NOT_FOUND、USER_NOT_FOUND、SESSION_NOT_FOUND | リソースが存在しない (またはこのマーチャントには存在しない) |
| 409 | ALREADY_EXISTS、USER_ALREADY_EXISTS、SESSION_ALREADY_EXISTS | 異なるボディでの冪等性リプレイ、またはステートマシンが遷移を拒否 |
| 429 | TOO_MANY_REQUESTS、TOO_MANY_ATTEMPTS | レート制限到達; バックオフしてリトライ |
| 500 | INTERNAL_SERVER_ERROR、DATABASE_CONNECTION_ERROR、DATABASE_QUERY_ERROR、DATABASE_TRANSACTION_ERROR、REDIS_CONNECTION_ERROR、REDIS_OPERATION_ERROR、EXTERNAL_SERVICE_ERROR | 当方の問題; バックオフ付きでリトライ可能。(TWILIO_SERVICE_ERROR / SENDGRID_SERVICE_ERROR のようなプロバイダ固有コードは内部に存在しますが、ダッシュボード側の通知フローのみで表面化し、B2B エンドポイントには現れません。) |
| 503 | SERVICE_UNAVAILABLE | 下流の依存が停止中。バックオフ付きでリトライ |
全コードリファレンス
目にする可能性のある code 値の全集合 (payment-service/pkg/errors/errors.go と一致):
認証 & セッション (401)
INVALID_CREDENTIALS— ユーザー名 / パスワードまたは API キーの組み合わせが拒否されたINVALID_TOKEN— JWT / セッショントークンがパース不能または改ざんされているTOKEN_EXPIRED— JWT のexpを過ぎたINVALID_OTP— OTP が一致しないOTP_EXPIRED— 発行から許容ウィンドウを超える時間が経過した OTPSESSION_EXPIRED— ダッシュボードセッションが期限切れINVALID_SIGNATURE— B2B / Webhook 呼び出しの HMAC 署名不一致
認可 (403)
FORBIDDEN— 認証は通ったがロール / スコープ / マーチャント境界がアクションを阻止IP_BLOCKED— IP が乱用リストに登録されている
未検出 (404)
NOT_FOUND— 汎用RECORD_NOT_FOUND— 指定された ID の行が存在しないUSER_NOT_FOUND— ユーザー検索失敗SESSION_NOT_FOUND— ダッシュボードセッション ID 未認識
競合 (409)
ALREADY_EXISTS— 汎用USER_ALREADY_EXISTS— サインアップが一意制約に当たったSESSION_ALREADY_EXISTS— セッションの重複挿入
バリデーション (400)
INVALID_INPUT— 汎用;detailsを確認MISSING_REQUIRED— 必須フィールドが欠落INVALID_FORMAT— 値が期待形式と一致しない (例: UUID、URL、メール)INVALID_LENGTH— 値が短すぎる、または長すぎるINVALID_VALUE— 値が許容される enum / 範囲外INVALID_USER_STATUS— ユーザーが当該アクションを許可しない状態にあるINVALID_USER_ROLE— ロールに当該アクションの権限がないPAYMENT_METHOD_NOT_SUPPORTED— プロバイダ / アセットの組み合わせがマーチャントに対して有効化されていないAMOUNT_BELOW_MINIMUM— 注文額がネットワーク別または環境レベルの下限を下回っている。レスポンスのdetails.floor_usdに設定された下限 (USD) が入っているので、そのまま表示できます;messageテキストにも同じ内容が記載されています。INSUFFICIENT_BALANCE— バイヤーのウォレットが送金分の支払いアセットを十分に保有していない。INSUFFICIENT_GAS— バイヤーのウォレットが送金をブロードキャストするためのネイティブガスを保有していない。
決済 / 課金 (402)
INSUFFICIENT_CREDIT— マーチャントのプリペイド残高がガス / プラットフォーム手数料を賄えない。ダッシュボードからチャージしてリトライしてください
レート制限 (429)
TOO_MANY_REQUESTS— ゲートウェイ IP のレート制限TOO_MANY_ATTEMPTS— 同じリソースに対する繰り返し失敗 (例: OTP) がスロットルを発動
ストレージ / インフラ (500)
DATABASE_CONNECTION_ERROR— DB に到達できなかったDATABASE_QUERY_ERROR— クエリプランがランタイムで失敗DATABASE_TRANSACTION_ERROR— commit/rollback 失敗REDIS_CONNECTION_ERROR— Redis に到達できなかったREDIS_OPERATION_ERROR— Redis コマンド失敗EXTERNAL_SERVICE_ERROR— 汎用のサードパーティ障害 (以下のプロバイダ別に分類されていないもの)TWILIO_SERVICE_ERROR— Twilio SMS / Verify 呼び出し失敗SENDGRID_SERVICE_ERROR— SendGrid メール送信失敗INTERNAL_SERVER_ERROR— 予期しない通り抜け; レスポンスのDateヘッダー +X-RateLimit-*を控えてご連絡ください
可用性 (503)
SERVICE_UNAVAILABLE— クリティカルな下流が unhealthy を報告; バックオフ + リトライ
バリデーションエラー (400)
リクエストボディの形式不正が原因の場合、details は配列なので、
エラーをフィールドに紐付けてマッピングできます:
{
"code": 400,
"message": "invalid_input",
"details": [
{ "field": "items[0].unit_price", "message": "must be a positive decimal string" },
{ "field": "success_url", "message": "must be a valid https URL" }
]
}認証エラー (401)
INVALID_SIGNATURE は 3 つの異なる失敗モードを包括しています — 区別する唯一の方法は二分探索です:
- 不正なシークレットキー (環境変数を再確認)
- タイムスタンプのずれが 5 分超 (NTP で同期)
- 不正な canonical 文字列 (最も多いのは:
\nセパレータ漏れ、または、送信したものと異なるパース後に再シリアライズしたボディに署名している)
正確な署名アルゴリズムは 認証 を参照してください。
レート制限 (429)
| サーフェス | 制限 |
|---|---|
ゲートウェイ全ルート (/b2b/v1/* 含む) | ゲートウェイの IP 単位 — 500 req/min、共通バケット。マーチャント単位ではありません。 |
公開チェックアウト (/checkout/*) | より厳しいサブバケット — IP あたり 20 req/min |
X-RateLimit-Limit と X-RateLimit-Remaining は、ゲートウェイのレート制限がかかった
すべての レスポンスで送出されます (成功時のみではありません)。
あなたの IP に対する現在のバジェットとして扱ってください。
レート制限のレスポンスには Retry-After ヘッダー (ウィンドウがリセットされる
までの秒数) が含まれます — これに従ってください。フォールバックとして、
ジッタ付きでバックオフ — ベース 1s から指数的に 30s まで。
レート制限は変更される可能性があります。正当な理由で当たっている場合 (例: 大規模な過去範囲の整合性チェック) はご連絡ください — バルク エンドポイントはロードマップ上にあります。
残高不足 (402)
INSUFFICIENT_CREDIT (HTTP 402 Payment Required) は、マーチャントの
プリペイド残高が、実行しようとしていた操作 (典型的には暗号資産決済の
精算、またはガススポンサーされたオンチェーン処理の実行) の次の
ガス + プラットフォーム手数料をカバーできないことを意味します。
マーチャントダッシュボードからチャージし (Billing → Add credit)、
操作をリトライしてください; 進行中の作業は待機し、残高不足が解消され次第
(チャージが完了し次第)自動的に再開されます。
サーバーエラー (5xx)
500 はリクエストを処理できなかったことを意味します。バックオフ付きで
リトライ — idempotency_key があれば、元のリクエストが部分的に成功
していても二重課金になりません。
リトライが 1 分以内に回復しない場合、バイヤーには「一時的に決済が
利用できません」と一般的なメッセージを表示し、失敗したエンドポイント、
マーチャント ID、レスポンスの Date ヘッダーと X-RateLimit-* の値、
おおよそのリクエスト時刻を添えてサポートにご連絡ください — それで
ログから該当リクエストを特定できます。
Webhook 配信エラー
Webhook 配信は別の失敗チャネルです — あなたのサーバーが呼び出し側 ではないため、API エラーとしては表面化しません。配信が non-2xx を 返す (またはタイムアウトする) と、0s、1min、5min、15min、1h、6h の 指数バックオフでリトライキューに入ります (合計 6 回 — Webhook → 概要 と同じスケジュール)。配信単位の完全な 履歴は、ダッシュボードの Developers → Webhooks → [endpoint] → Delivery log に表示されます。6 回目の試行後、配信はデッドレターに 入り — ダッシュボードに「Failed」バッジが表示され、サーバーが正常化 した後に手動で再生できます。