エラーレスポンス形式
APIエラーは以下の形式で返されます。
{
"error": {
"type": "invalid_request_error",
"code": "validation_error",
"message": "エラーの説明",
"request_id": "req_abc123xyz789"
}
}
エラーの種類(authentication_error, invalid_request_error, payment_error, api_error)
エラーの原因となったパラメータ名(バリデーションエラー時)
認証エラー
| コード | HTTPステータス | 説明 |
|---|
unauthorized | 401 | 認証ヘッダーが不正または不足しています |
merchant_not_active | 403 | 加盟店アカウントが無効化されています |
バリデーションエラー
| コード | HTTPステータス | 説明 |
|---|
validation_error | 400 | リクエストパラメータが不正です。detailsフィールドに詳細が含まれます |
invalid_amount | 400 | 金額が不正です |
amount_below_minimum | 400 | 金額が最小値を下回っています |
amount_above_maximum | 400 | 金額が最大値を超えています |
unsupported_currency | 400 | サポートされていない通貨です |
決済エラー
| コード | HTTPステータス | 説明 |
|---|
payment_failed | 400 | 決済処理に失敗しました |
invalid_status | 400 | この操作は現在のステータスでは実行できません |
invalid_state | 400 | トランザクションの状態が不正です |
invalid_transaction | 400 | トランザクションが不正です |
not_supported | 400 | この操作はサポートされていません |
capture_failed | 400 | キャプチャに失敗しました |
refund_failed | 400 | 返金に失敗しました |
card_limit_exceeded | 400 | 月間カード決済上限を超過しました |
email_limit_exceeded | 400 | 月間メール決済上限を超過しました |
recurring_not_supported | 400 | コネクタが継続決済をサポートしていません |
Webhook決済エラーコード
決済が失敗した場合、payment.failed Webhookには、すべての決済プロバイダーで統一された形式の詳細なエラー情報が含まれます。
エラー構造
{
"event": "payment.failed",
"error": {
"code": "card_declined",
"category": "soft_decline",
"message": "カードが拒否されました",
"recommended_action": "use_different_card",
"psp_error": {
"code": "card_declined",
"decline_code": "generic_decline"
}
}
}
エラーカテゴリ
| カテゴリ | 説明 | 推奨アクション |
|---|
authentication | 3DS認証が必要 | 顧客を認証ページにリダイレクト |
soft_decline | 一時的な失敗 | リトライまたは別のカードを使用 |
hard_decline | 永続的な失敗 | 同じカードでリトライしない |
gateway_error | 決済ゲートウェイの問題 | 後でリトライ |
統一エラーコード
| コード | カテゴリ | 説明 | 推奨アクション |
|---|
authentication_required | authentication | 3DS認証が必要 | contact_customer |
authentication_failed | soft_decline | 3DS認証に失敗 | retry |
card_declined | soft_decline | カードが拒否されました | use_different_card |
insufficient_funds | soft_decline | 残高不足 | use_different_card |
expired_card | hard_decline | カードの有効期限切れ | use_different_card |
invalid_card | hard_decline | カード番号が無効 | use_different_card |
invalid_cvc | soft_decline | セキュリティコードが無効 | retry |
invalid_expiry | soft_decline | 有効期限が無効 | retry |
fraud_detected | hard_decline | 不正利用の疑い | none |
stolen_card | hard_decline | 盗難カード | none |
lost_card | hard_decline | 紛失カード | none |
processing_error | gateway_error | 処理エラー | retry |
gateway_timeout | gateway_error | ゲートウェイタイムアウト | retry |
gateway_unavailable | gateway_error | ゲートウェイ利用不可 | retry |
unknown_error | soft_decline | 不明なエラー | use_different_card |
ハンドリング例
// Webhookハンドラー
app.post('/webhooks/zafapay', (req, res) => {
const { event, error } = req.body;
if (event === 'payment.failed' && error) {
switch (error.category) {
case 'authentication':
// 顧客に認証リンク付きメールを送信
notifyCustomerForAuth(error.message);
break;
case 'soft_decline':
// リトライまたは別のカードを要求
if (error.recommended_action === 'retry') {
scheduleRetry();
} else {
requestNewCard();
}
break;
case 'hard_decline':
// リトライしない、顧客に通知
notifyCustomerCardInvalid(error.message);
break;
case 'gateway_error':
// 一時的な問題、リトライをスケジュール
scheduleRetryWithBackoff();
break;
}
}
res.json({ received: true });
});
psp_errorフィールドには、デバッグ用に元の決済プロバイダーのエラー詳細が含まれます。
返金エラー
| コード | HTTPステータス | 説明 |
|---|
invalid_amount | 400 | 返金額が決済額を超えています |
invalid_status | 400 | 返金可能なステータスではありません(completedのみ返金可能) |
リソースエラー
| コード | HTTPステータス | 説明 |
|---|
not_found | 404 | 指定されたリソースが見つかりません |
flow_not_found | 404 | 決済フローが見つかりません |
no_default_flow | 404 | デフォルトの決済フローが設定されていません |
flow_not_active | 400 | 決済フローが無効化されています |
connector_config_not_found | 404 | コネクタ設定が見つかりません |
冪等性エラー
| コード | HTTPステータス | 説明 |
|---|
idempotency_key_mismatch | 400 | 同じ冪等キーで異なるリクエストが送信されました |
idempotency_key_in_use | 409 | 冪等キーが現在処理中です |
システムエラー
| コード | HTTPステータス | 説明 |
|---|
internal_error | 500 | 内部エラーが発生しました。サポートにお問い合わせください |
エラーハンドリングのベストプラクティス
HTTPステータスコードを確認
4xx: クライアントエラー(リクエストの修正が必要)
5xx: サーバーエラー(リトライ可能)
エラーコードで処理を分岐
error.codeを使用して、エラータイプに応じた処理を実装してください
リトライ戦略
5xxエラーやidempotency_key_in_useの場合は、指数バックオフでリトライしてください
サンプルコード
async function createPayment(data) {
const response = await fetch('https://api.sandbox.zafapay.com/v1/payments', {
method: 'POST',
headers: {
'Authorization': 'Bearer YOUR_ACCESS_TOKEN',
'Content-Type': 'application/json',
'Idempotency-Key': generateIdempotencyKey()
},
body: JSON.stringify(data)
});
if (!response.ok) {
const { error } = await response.json();
// request_id をログに記録(サポートへの問い合わせ時に使用)
console.error(`API Error [${error.request_id}]:`, error.code, error.message);
switch (error.type) {
case 'authentication_error':
// トークンの再取得が必要
throw new AuthError(error.message);
case 'invalid_request_error':
// リクエストパラメータを確認
if (error.details) {
console.error('Validation errors:', error.details);
}
throw new ValidationError(error.message);
case 'payment_error':
// 決済失敗(別の決済方法を試す等)
throw new PaymentError(error.message);
case 'api_error':
// リトライ可能
throw new RetryableError(error.message);
default:
throw new Error(error.message);
}
}
return response.json();
}