エラーハンドリング
Omise APIのエラーを理解し、信頼性の高い決済統合のための堅牢なエラーハンドリングを実装します。HTTPステータスコード、エラーレスポンス形式、デバッグのベストプラクティスについて学びます。
概要
Omise APIは、APIリクエストの成功または失敗を示すために従来のHTTPレスポンスコードを使用します。エラーは、デバッグと失敗の適切な処理に役立つ詳細情報を含むJSON形式のレスポンスを返します。
- 2xxコード → 成功
- 4xxコード → クライアントエラー(リクエストが無効)
- 5xxコード → サーバーエラー(Omise側で問題が発生)
- すべてのエラーは
object: "error"と説明フィールドを含むJSONを返します
エラーレスポンス形式
すべてのAPIエラーは一貫したJSON構造を返します。
{
"object": "error",
"location": "https://www.omise.co/api-errors#authentication-failure",
"code": "authentication_failure",
"message": "authentication failed"
}
エラーオブジェクトのフィールド
| フィールド | 型 | 説明 |
|---|---|---|
object | string | エラーレスポンスの場合は常に"error" |
location | string | このエラータイプのドキュメントへのURL |
code | string | 機械可読なエラーコード(アンダースコア付き小文字) |
message | string | 英語の人間可読なエラーメッセージ |
一部のエラーには、詳細なデバッグのためにcharge_id、customer_id、またはvalidation_errorsなどのコンテキスト固有の追加フィールドが含まれる場合があります。
HTTPステータスコード
2xx 成功
200 OK
意味: リクエストが成功しました
{
"object": "charge",
"id": "chrg_test_5xuy4w91xqz7d1w9u0t",
"amount": 100000,
"status": "successful"
}
発生する場合:
- ✅ GETリクエストがデータを正常に返しました
- ✅ POST/PATCHリクエストが正常に完了しました
- ✅ DELETEリクエストがリソースを正常に削除しました
201 Created
意味: リソースが正常に作成されました
発生する場合:
- ✅ POSTリクエストが新しいリソースを作成しました(まれで、通常は200を返します)
4xx クライアントエラー
クライアントエラーは、送信したリクエストに問題があることを示します。リクエストを修正して再試行してください。
400 Bad Request
意味: リクエストが不正な形式であるか、無効なパラメータが含まれています
{
"object": "error",
"location": "https://www.omise.co/api-errors#bad-request",
"code": "bad_request",
"message": "amount must be at least 2000 (in subunits)"
}
一般的な原因:
- ❌ 必須パラメータの欠落
- ❌ 無効なパラメータ値
- ❌ 最小しきい値を下回る金額
- ❌ サポートされていない通貨
- ❌ 不正な形式のJSONペイロード
- ❌ 無効なパラメータ型
シナリオ例:
# 必須パラメータの欠落
curl https://api.omise.co/charges \
-X POST \
-u skey_test_...: \
-d "currency=thb"
# Error: amount is required
# 金額が小さすぎる
curl https://api.omise.co/charges \
-X POST \
-u skey_test_...: \
-d "amount=100" \
-d "currency=thb"
# Error: amount must be at least 2000 (20 THB)
修正方法:
- ✅ 必須パラメータについてAPIドキュメントを確認してください
- ✅ 送信前にパラメータ値を検証してください
- ✅ 適切なデータ型を確保してください(数値は文字列ではなく数値として)
- ✅ 通貨固有の最小金額を確認してください
401 Unauthorized
意味: 認証に失敗しました - 無効または欠落したAPIキー
{
"object": "error",
"location": "https://www.omise.co/api-errors#authentication-failure",
"code": "authentication_failure",
"message": "authentication failed"
}
一般的な原因:
- ❌ APIキーが提供されていません
- ❌ 無効なAPIキー形式
- ❌ 取り消されたまたは期限切れのAPIキー
- ❌ 環境に対して間違ったキー(本番環境でテストキー)
- ❌ AuthorizationヘッダーでAPIキーが適切にエンコードされていません
シナリオ例:
# APIキーの欠落
curl https://api.omise.co/account
# Error: authentication failed
# 無効なAPIキー
curl https://api.omise.co/account \
-u invalid_key:
# Error: authentication failed
# 秘密鍵操作に公開鍵を使用
curl https://api.omise.co/charges \
-X POST \
-u pkey_test_...: \
-d "amount=100000" \
-d "currency=thb"
# Error: authentication failed
修正方法:
- ✅ APIキーが正しいことを確認してください(ダッシュボードからコピー)
- ✅ 余分なスペースや文字がないか確認してください
- ✅ サーバー側の操作には秘密鍵を使用してください
- ✅ トークン/ソースには公開鍵のみを使用してください
- ✅ キーが取り消されていないことを確認してください
- ✅ テストキーとライブキーが環境と一致することを確認してください
402 Payment Required
意味: カードまたは支払い方法が拒否されました
{
"object": "error",
"location": "https://www.omise.co/api-errors#payment-declined",
"code": "payment_declined",
"message": "Your card was declined",
"charge_id": "chrg_test_5xuy4w91xqz7d1w9u0t"
}
一般的な原因:
- ❌ 残高不足
- ❌ カードの有効期限切れ
- ❌ 誤ったカード詳細(CVV、有効期限)
- ❌ カード発行者が拒否(詐欺の疑い)
- ❌ オンライン決済が有効になっていないカード
- ❌ 取引限度額超過
支払い固有のエラーコード:
| コード | 意味 | アクション |
|---|---|---|
insufficient_fund | カードに十分な残高がありません | 顧客に別のカードを使用するよう依頼してください |
invalid_card | カード番号が無効です | カード番号を確認してください |
stolen_or_lost_card | カードが盗難/紛失として報告されています | 顧客に銀行に連絡するよう依頼してください |
failed_processing | 処理に失敗しました | 再試行するか、別のカードを使用してください |
payment_cancelled | 顧客が支払いをキャンセルしました | 顧客のアクション、修正は不要です |
payment_expired | 支払いウィンドウが期限切れになりました | 新しいchargeを作成してください |
シナリオ例:
# 残高不足でのcharge試行
curl https://api.omise.co/charges \
-X POST \
-u skey_test_...: \
-d "amount=1000000" \
-d "currency=thb" \
-d "card=tokn_test_..."
# Error: insufficient_fund
修正方法:
- ✅ 顧客に明確なエラーメッセージを表示してください
- ✅ 顧客に別の支払い方法を試すよう依頼してください
- ✅ 同じカードを複数回再試行しないでください(カードのブロックを回避)
- ✅ 顧客に銀行に連絡するよう提案してください
- ✅ 分析のために拒否理由を記録してください
拒否されたカードを繰り返し試行すると、詐欺アラートがトリガーされ、カードがブロックされる可能性があります。代わりに、顧客に支払い詳細を確認するか、別の支払い方法を使用するよう依頼してください。
404 Not Found
意味: 要求されたリソースが存在しません
{
"object": "error",
"location": "https://www.omise.co/api-errors#not-found",
"code": "not_found",
"message": "charge chrg_test_invalid was not found"
}
一般的な原因:
- ❌ 無効なリソースID
- ❌ リソースが削除されました
- ❌ 間違ったAPIエンドポイントURL
- ❌ リソースIDのタイプミス
- ❌ ライブモードでテストIDを使用(またはその逆)
シナリオ例:
# 無効なcharge ID
curl https://api.omise.co/charges/chrg_invalid \
-u skey_test_...:
# Error: not found
# 間違ったエンドポイント
curl https://api.omise.co/charge/chrg_test_... \
-u skey_test_...:
# Error: not found (should be /charges not /charge)
修正方法:
- ✅ リソースIDが正しいことを確認してください
- ✅ リソースが削除されていないか確認してください
- ✅ テスト/ライブモードがキーと一致することを確認してください
- ✅ エンドポイントURLのスペルを確認してください
- ✅ リストエンドポイントを使用してリソースを検索してください
422 Unprocessable Entity
意味: リクエストは適切な形式ですが、セマンティックエラーが含まれています
{
"object": "error",
"location": "https://www.omise.co/api-errors#invalid-charge",
"code": "invalid_charge",
"message": "charge has already been captured"
}
一般的な原因:
- ❌ 無効な状態遷移の試行
- ❌ ビジネスロジック違反
- ❌ リソースがすでに処理されています
- ❌ 無効なパラメータの組み合わせ
シナリオ例:
# すでにキャプチャされたchargeのキャプチャ
curl https://api.omise.co/charges/chrg_test_.../capture \
-X POST \
-u skey_test_...:
# Error: charge has already been captured
# charge金額を超える払い戻し
curl https://api.omise.co/charges/chrg_test_.../refunds \
-X POST \
-u skey_test_...: \
-d "amount=200000"
# Error: refund amount exceeds available amount
修正方法:
- ✅ 操作前にリソースの状態を確認してください
- ✅ ビジネスロジックの制約を確認してください
- ✅ 同じ操作を再試行しないでください(成功している可能性があります)
- ✅ リソースを取得して現在の状態を確認してください
429 Too Many Requests
意味: レート制限を超えました
{
"object": "error",
"location": "https://www.omise.co/api-errors#rate-limit-exceeded",
"code": "rate_limit_exceeded",
"message": "too many requests, please try again later"
}
レスポンスヘッダー:
X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1612137600
Retry-After: 60
修正方法:
- ✅ 指数バックオフを実装してください
- ✅
Retry-Afterヘッダーを確認してください - ✅ リクエスト頻度を減らしてください
- ✅ 可能な場合は操作をバッチ処理してください
- ✅ 適切な場合はレスポンスをキャッシュしてください
5xx サーバーエラー
サーバーエラーは、Omise側の問題を示します。これらはまれですが、適切に処理する必要があります。
500 Internal Server Error
意味: Omiseのサーバーで問題が発生しました
{
"object": "error",
"location": "https://www.omise.co/api-errors#internal-server-error",
"code": "internal_server_error",
"message": "An internal server error occurred"
}
発生する場合:
- ⚠️ 予期しないサーバー側エラー
- ⚠️ 一時的なサービス中断
- ⚠️ データベース接続の問題
処理方法:
- ✅ 指数バックオフでリクエストを再試行してください
- ✅ インシデントについてstatus.omise.coを確認してください
- ✅ 持続する場合はサポートに連絡してください
- ✅ デバッグのためにエラーの詳細を記録してください
503 Service Unavailable
意味: サービスが一時的に利用できません
{
"object": "error",
"location": "https://www.omise.co/api-errors#service-unavailable",
"code": "service_unavailable",
"message": "Service temporarily unavailable"
}
発生する場合:
- ⚠️ スケジュールされたメンテナンス
- ⚠️ サービスの過負荷
- ⚠️ アップストリームプロバイダーの問題
処理方法:
- ✅ 遅延後に再試行してください(
Retry-Afterヘッダーを確認) - ✅ ユーザーにメンテナンスメッセージを表示してください
- ✅ 後で処理するためにリクエストをキューに入れてください
- ✅ ステータスページを確認してください
完全なエラーコードリファレンス
400 Bad Request
| コード | メッセージ | 説明 |
|---|---|---|
backend_error | "backend error" | 決済プロバイダーとの通信に失敗しました |
bad_request | "bad request" | 無効なリクエストパラメータ |
brand_not_supported | "brand not supported" | カードブランドがアカウントで受け入れられていません |
documents_locked | "documents list cannot be modified" | 紛争がファイルアップロードに対して閉じられています |
expired_charge | "charge expired" | 認証期間が超過したか、支払いコードが期限切れになりました |
failed_capture | "capture failed" | chargeのキャプチャに失敗しました |
failed_expire | "expire failed" | chargeの期限切れを設定できませんでした |
failed_fraud_check | "fraud check failed" | カードが不正としてフラグが立てられました |
failed_multi_currency | "the currency conversion could not be completed" | 多通貨変換エラー |
failed_refund | "refund failed" | 返金処理エラー |
failed_reverse | "reverse failed" | chargeの取り消しに失敗しました |
failed_void | "void failed" | chargeの無効化操作に失敗しました |
feature_not_supported | "feature not supported" | アカウントに機能アクセスがありません |
invalid_amount | "invalid amount" | 整数以外の金額が送信されました |
invalid_bank_account | "invalid bank account" | 銀行口座の詳細が無効です |
invalid_card | "invalid card" | カードトークンの作成に失敗しました |
invalid_card_token | "invalid card token" | 文字列以外のトークンオブジェクトが提供されました |
invalid_charge | "invalid charge" | chargeが最小要件を満たしていません |
invalid_dispute | "invalid dispute" | 紛争の変更がパラメータ外です |
invalid_domain | "request must be made with the vault.omise.co domain" | トークン生成に間違ったドメインが使用されました |
invalid_file_type | "invalid content-type" | サポートされていないファイルタイプがアップロードされました |
invalid_link | "invalid link" | リンクに通貨または説明がありません |
invalid_recipient | "invalid recipient" | 受取人が無効または存在しません |
invalid_transfer | "invalid transfer" | 送金の作成/更新エラー |
missing_card | "request contains no card parameters" | リクエストにカードパラメータがありません |
missing_file | "missing file or filename" | ファイルコンテンツなしでファイルがアップロードされました |
used_token | "token was already used" | 使い捨てトークンが再利用されました |
401 Unauthorized
| コード | メッセージ | 説明 |
|---|---|---|
authentication_failure | "authentication failed" | 無効または欠落したAPIキー |
402 Payment Required
| コード | メッセージ | 説明 |
|---|---|---|
insufficient_fund | "insufficient funds" | カードの残高不足 |
invalid_card | "invalid card" | カード番号が無効です |
stolen_or_lost_card | "stolen or lost card" | カードが盗難/紛失として報告されています |
failed_processing | "processing failed" | 処理に失敗しました |
payment_cancelled | "payment cancelled" | 顧客が支払いをキャンセルしました |
payment_expired | "payment expired" | 支払い期間が期限切れになりました |
payment_rejected | "payment rejected" | 支払いが拒否されました |
403 Forbidden
| コード | メッセージ | 説明 |
|---|---|---|
key_expired_error | "expired key" | APIキーの有効期限が切れています |
locked_account_error | "account locked" | アカウントがロックされています |
not_authorized | "not authorized" | このキーでは操作が許可されていません |
404 Not Found
| コード | メッセージ | 説明 |
|---|---|---|
not_found | "the requested object was not found" | リソースが存在しません |
serializer_not_found | "your current API version does not support this action" | APIバージョンの互換性がありません |
service_not_found | "you are using an api version that does not support this operation" | APIバージョンで操作がサポートされていません |
422 Unprocessable Entity
| コード | メッセージ | 説明 |
|---|---|---|
failed_deletion | "this object could not be deleted" | 削除操作に失敗しました |
invalid_filter | "invalid filters" | 検索/リストフィルターが無効です |
invalid_page | "invalid page" | ページ番号が無効です |
invalid_per_page | "invalid per page" | ページあたりの項目数が無効です |
invalid_scope | "invalid scope" | 検索スコープが無効です |
429 Too Many Requests
| コード | メッセージ | 説明 |
|---|---|---|
too_many_requests | "you have sent too many requests in too little time" | レート制限を超えました |
500 Internal Server Error
| コード | メッセージ | 説明 |
|---|---|---|
internal_error | "request could not be completed due to an internal error" | サーバー側エラー |
503 Service Unavailable
| コード | メッセージ | 説明 |
|---|---|---|
search_unavailable | "search is temporarily unavailable" | 検索サービスが一時的にダウンしています |
カテゴリ別エラーコード
認証エラー
| コード | HTTPステータス | 説明 | 解決策 |
|---|---|---|---|
authentication_failure | 401 | 無効または欠落したAPIキー | APIキーが正しいことを確認してください |
key_expired_error | 403 | APIキーの有効期限切れ | ダッシュボードから新しいAPIキーを生成してください |
locked_account_error | 403 | アカウントがロックされています | サポートに連絡してください |
not_authorized | 403 | 操作が許可されていません | 正しいキータイプ(public/secret)を使用してください |
リクエストエラー
| コード | HTTPステータス | 説明 | 解決策 |
|---|---|---|---|
bad_request | 400 | 無効なリクエストパラメータ | 必須パラメータと形式を確認してください |
invalid_amount | 400 | 整数以外の金額 | 最小単位の整数金額を使用してください |
invalid_card_token | 400 | 無効なトークン形式 | トークンが文字列であることを確認してください |
invalid_domain | 400 | リクエストに間違ったドメイン | トークンにはvault.omise.coを使用してください |
missing_card | 400 | カードパラメータが欠落 | リクエストにカードまたはトークンを含めてください |
used_token | 400 | トークンが既に使用されています | トークンは使い 捨てです。新しいものを作成してください |
invalid_charge | 422 | chargeの状態が操作を許可しません | 最初にchargeのステータスを確認してください |
not_found | 404 | リソースが存在しません | リソースIDを確認してください |
支払いエラー
| コード | HTTPステータス | 説明 | 解決策 |
|---|---|---|---|
insufficient_fund | 402 | 残高不足 | 別の支払い方法をリクエストしてください |
invalid_card | 400/402 | カードの詳細が無効です | カード番号とCVVを確認してください |
stolen_or_lost_card | 402 | カードが盗難/紛失として報告されています | 顧客は銀行に連絡する必要があります |
failed_processing | 402 | 支払い処理に失敗しました | 再試行するか、別のカードを使用してください |
failed_fraud_check | 400 | カードが不正としてフラグが立てられました | 顧客は銀行に連絡する必要があります |
payment_cancelled | 402 | 顧客が支払いをキャンセルしました | 顧客のアクション |
payment_expired | 402 | 支払い期間が期限切れになりました | 新しいchargeを作成してください |
expired_charge | 400 | 認証が期限切れになりました | 新しいchargeを作成してください |
brand_not_supported | 400 | カードブランドがサポートされていません | サポートされているカードブランドを使用してください |
操作エラー
| コード | HTTPステータス | 説明 | 解決策 |
|---|---|---|---|
failed_capture | 400 | キャプチャに失敗しました | chargeの状態を確認してください。すでにキャプチャされている可能性があります |
failed_refund | 400 | 返金に失敗しました | chargeの状態と返金可能な金額を確認してください |
failed_reverse | 400 | 取り消しに失敗しました | chargeの状態を確認してください |
failed_void | 400 | 無効化に失敗しました | chargeの状態を確認してください |
failed_expire | 400 | 期限切れに失敗しました | chargeの状態を確認してください |
failed_deletion | 422 | 削除に失敗しました | リソースに依存関係がある可能性があります |
送金/受取人エラー
| コード | HTTPステータス | 説明 | 解決策 |
|---|---|---|---|
invalid_bank_account | 400 | 無効な銀行口座 | 銀行口座の詳細を確認してください |
invalid_recipient | 400 | 無効な受取人 | 受取人が存在し、アクティブであることを確認してください |
invalid_transfer | 400 | 無効な送金 | 送金パラメータを確認してください |
紛争エラー
| コード | HTTPステータス | 説明 | 解決策 |
|---|---|---|---|
invalid_dispute | 400 | 無効な紛争操作 | 紛争の状態を確認してください |
documents_locked | 400 | ドキュメントを変更できません | 紛争はクローズされています |
invalid_file_type | 400 | 無効なファイルタイプ | サポートされているファイル形式を使用してください |
missing_file | 400 | ファイルが欠落しています | アップロードにファイルを含めてください |
システムエラー
| コード | HTTPステータス | 説明 | 解決策 |
|---|---|---|---|
internal_error | 500 | サーバー側エラー | バックオフで再試行してください |
backend_error | 400 | 決済プロバイダーエラー | 再試行するか、サポートに連絡してください |
search_unavailable | 503 | 検索サービスがダウンしています | 待機して再試行してください |
too_many_requests | 429 | レート制限を超えました | レート制限を実装してください |
feature_not_supported | 400 | 機能が有効になっていません | サポートに連絡して有効にしてください |
failed_multi_currency | 400 | 通貨変換に失敗しました | 通貨のサポートを確認してください |
エラーハンドリングのベストプラクティス
1. 常に最初にHTTPステータスを確認する
# ✅ 良い - ステータスコードを確認
require 'omise'
begin
charge = Omise::Charge.create({
amount: 100000,
currency: 'thb',
card: params[:token]
})
# 成功
render json: charge
rescue Omise::Error => e
# タイプに基づいてエラーを処理
case e.http_status
when 400
render json: { error: 'Invalid request' }, status: 400
when 401
render json: { error: 'Authentication failed' }, status: 401
when 402
render json: { error: 'Payment declined', code: e.code }, status: 402
when 404
render json: { error: 'Resource not found' }, status: 404
when 422
render json: { error: 'Invalid operation', message: e.message }, status: 422
when 429
render json: { error: 'Rate limit exceeded' }, status: 429
when 500, 503
render json: { error: 'Service temporarily unavailable' }, status: 503
else
render json: { error: 'An error occurred' }, status: 500
end
end
2. エラーの詳細を解析する
# ✅ 良い - エラーの詳細を抽出
import omise
try:
charge = omise.Charge.create(
amount=100000,
currency='thb',
card=token
)
return charge
except omise.errors.BaseError as e:
error_code = e.code
error_message = e.message
# デバッグのためにログを記録
logger.error(f"Charge failed: {error_code} - {error_message}")
# ユーザーフレンドリーなメッセージを返す
if error_code == 'insufficient_fund':
return {'error': 'Insufficient funds. Please try a different card.'}
elif error_code == 'invalid_card':
return {'error': 'Invalid card details. Please check and try again.'}
else:
return {'error': 'Payment failed. Please try again.'}
3. サーバーエラーの再試行ロジックを実装する
// ✅ 良い - 指数 バックオフで再試行
const omise = require('omise')({ secretKey: process.env.OMISE_SECRET_KEY });
async function createChargeWithRetry(chargeData, maxRetries = 3) {
for (let attempt = 0; attempt < maxRetries; attempt++) {
try {
const charge = await omise.charges.create(chargeData);
return { success: true, charge };
} catch (error) {
const isServerError = error.statusCode >= 500;
const isLastAttempt = attempt === maxRetries - 1;
if (isServerError && !isLastAttempt) {
// 指数バックオフ: 1s, 2s, 4s
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
continue;
}
// クライアントエラーまたは最後の試行は再試行しない
return {
success: false,
error: {
code: error.code,
message: error.message,
statusCode: error.statusCode
}
};
}
}
}
4. リクエストを送信する前に検証する
<?php
// ✅ 良い - API呼び出し前に検証
function createCharge($amount, $currency, $token) {
// 金額を検証
$minAmounts = [
'thb' => 2000, // 20 THB
'jpy' => 50, // 50 JPY
'sgd' => 100, // 1 SGD
];
if ($amount < $minAmounts[$currency]) {
return [
'error' => 'Amount below minimum for ' . strtoupper($currency),
'min_amount' => $minAmounts[$currency]
];
}
// 通貨を検証
$supportedCurrencies = ['thb', 'jpy', 'sgd', 'myr', 'usd'];
if (!in_array($currency, $supportedCurrencies)) {
return [
'error' => 'Unsupported currency',
'supported' => $supportedCurrencies
];
}
// トークン形式を検証
if (!preg_match('/^tokn_test_[a-z0-9]+$/', $token) &&
!preg_match('/^tokn_[a-z0-9]+$/', $token)) {
return ['error' => 'Invalid token format'];
}
try {
$charge = OmiseCharge::create([
'amount' => $amount,
'currency' => $currency,
'card' => $token
]);
return ['success' => true, 'charge' => $charge];
} catch (Exception $e) {
return [
'error' => $e->getMessage(),
'code' => $e->getCode()
];
}
}
5. ユーザーフレンドリーなメッセージを表示する
// ✅ 良い - エラーコードをユーザーメッセージに変換
package main
import (
"github.com/omise/omise-go"
)
func getUserMessage(err error) string {
if omiseErr, ok := err.(*omise.Error); ok {
switch omiseErr.Code {
case "authentication_failure":
return "A system error occurred. Please try again later."
case "insufficient_fund":
return "Your card has insufficient funds. Please use a different card."
case "invalid_card":
return "The card details are invalid. Please check and try again."
case "stolen_or_lost_card":
return "This card cannot be used. Please contact your bank."
case "payment_cancelled":
return "Payment was cancelled. You can try again when ready."
case "payment_expired":
return "Payment session expired. Please start a new payment."
case "rate_limit_exceeded":
return "Too many attempts. Please wait a moment and try again."
case "service_unavailable":
return "Payment service is temporarily unavailable. Please try again later."
default:
return "Payment failed. Please try again or use a different payment method."
}
}
return "An unexpected error occurred. Please try again."
}
func createCharge(amount int64, currency string, token string) (string, error) {
client, _ := omise.NewClient(
os.Getenv("OMISE_PUBLIC_KEY"),
os.Getenv("OMISE_SECRET_KEY"),
)
charge, err := client.CreateCharge(&omise.ChargeParams{
Amount: amount,
Currency: currency,
Card: token,
})
if err != nil {
userMessage := getUserMessage(err)
return userMessage, err
}
return "Payment successful!", nil
}
6. デバッグのためにエラーをログに記録する
# ✅ 良い - 包括的なエラーログ
require 'logger'
logger = Logger.new('omise_errors.log')
begin
charge = Omise::Charge.create({
amount: 100000,
currency: 'thb',
card: token
})
rescue Omise::Error => e
# 詳細なエラー情報をログに記録
logger.error({
timestamp: Time.now.iso8601,
error_type: 'omise_api_error',
http_status: e.http_status,
error_code: e.code,
error_message: e.message,
request_id: e.request_id,
charge_params: {
amount: 100000,
currency: 'thb',
token: token[0..10] + '...' # セキュリティのための部分的なトークン
},
backtrace: e.backtrace.first(5)
}.to_json)
# 再発生または処理
raise
end
7. 機密性の高いエラーの詳細を公開しない
// ❌ 悪い - 内部の詳細を公開
app.post('/charge', async (req, res) => {
try {
const charge = await omise.charges.create(req.body);
res.json(charge);
} catch (error) {
// これをしないでください - APIキー、内部パスなどを公開します
res.status(500).json({ error: error.toString() });
}
});
// ✅ 良い - 安全なエラーメッセージ
app.post('/charge', async (req, res) => {
try {
const charge = await omise.charges.create({
amount: req.body.amount,
currency: req.body.currency,
card: req.body.token
});
res.json({ success: true, charge_id: charge.id });
} catch (error) {
// サーバー側で完全なエラーをログに記録
console.error('Charge failed:', error);
// クライアントに安全なメッセージを返す
const safeMessage = {
success: false,
error: {
code: error.code || 'unknown',
message: getUserFriendlyMessage(error.code)
}
};
res.status(error.statusCode || 500).json(safeMessage);
}
});
エラーのデバッグ
1. 開発にテストモードを使用する
開 発中は常にテストAPIキーを使用してください。
# ✅ テストモード - デバッグに安全
export OMISE_SECRET_KEY=skey_test_5xuy4w91xqz7d1w9u0t
# テストモードのエラーはライブモードと同じです
curl https://api.omise.co/charges \
-X POST \
-u $OMISE_SECRET_KEY: \
-d "amount=100000" \
-d "currency=thb" \
-d "card=tokn_test_no1t4tnemucod0e51mo"
2. ダッシュボードでリクエスト/レスポン スを確認する
- ダッシュボード → ログに移動します
- すべてのAPIリクエストとレスポンスを表示します
- 正確なエラーの詳細を確認します
- リクエストパラメータを確認します
3. デバッグログを有効にする
# Ruby - デバッグログを有効にする
require 'omise'
Omise.api_key = ENV['OMISE_SECRET_KEY']
Omise.debug = true # HTTPリクエスト/レスポンスを出力
charge = Omise::Charge.create({
amount: 100000,
currency: 'thb',
card: token
})
# Python - デバッグログを有効にする
import omise
import logging
# HTTPデバッグを有効にする
logging.basicConfig(level=logging.DEBUG)
omise.api_secret = os.environ['OMISE_SECRET_KEY']
// Node.js - リクエストインターセプションを使用
const omise = require('omise')({
secretKey: process.env.OMISE_SECRET_KEY
});
// すべてのリクエスト/レスポンスをログに記録
omise.interceptor = {
request: (config) => {
console.log('Request:', config);
return config;
},
response: (response) => {
console.log('Response:', response);
return response;
},
responseError: (error) => {
console.error('Error:', error);
throw error;
}
};
4. Curlでテストする
# 詳細出力は完全なHTTP交換を表示します
curl -v https://api.omise.co/charges \
-X POST \
-u skey_test_...: \
-d "amount=100000" \
-d "currency=thb" \
-d "card=tokn_test_..."
# 表示内容:
# > POST /charges HTTP/1.1
# > Authorization: Basic ...
# > Content-Type: application/x-www-form-urlencoded
# < HTTP/1.1 200 OK
# < Content-Type: application/json
5. 一般的なデバッグ手順
認証エラーの場合:
- ✅ ダッシュボードからAPIキーをコピーしてください(手動 で入力しないでください)
- ✅ 余分なスペースや改行がないか確認してください
- ✅ テストキーとライブキーが環境と一致することを確認してください
- ✅ Authorizationヘッダー形式を確認してください:
Basic base64(key:)
支払いエラーの場合:
- ✅ 最初にテストカード番号で試してください
- ✅ カードの詳細が正しいことを確認してください
- ✅ カードがオンライン決済をサポートしているか確認してください
- ✅ 問題を特定するために異なるカードでテストしてください
検証エラーの場合:
- ✅ エラーメッセージを注意深く読んでください
- ✅ パラメータ要件についてAPIドキュメントを確認してください
- ✅ データ型を確認してください(数値 vs 文字列)
- ✅ 最小値/最大値を確認してください
サーバーエラーの場合:
- ✅ status.omise.coを確認してください
- ✅ 遅延後に再試行してください
- ✅ 持続する場合はサポートに連絡してください
- ✅ デバッグのためにリクエスト/レスポンスを保存してください
エラーハンドリングのチェックリスト
本番環 境に移行する前に、エラーハンドリングが以下を満たしていることを確認してください。
- すべてのAPI例外/エラーをキャッチする
- HTTPステータスコードを確認する
- 特定の処理のためにエラーコードを解析する
- ユーザーフレンドリーなエラーメッセージを表示する
- 完全なコンテキストでエラーをログに記録する(サーバー側のみ)
- サーバーエラーの再試行ロジックを実装する
- 再試行に指数バックオフを使用する
- 支払い失敗を再試行しない
- 送信前にリクエストを検証する
- 機密性の高いエラーの詳細をユーザーに公開しない
- ネットワークタイムアウトを処理する
- サービス利用不可の場合のフォールバックを用意する
- エラー率を監視する
- 異常なエラーパターンにアラートを出す
- 開発環境でエラーシナリオをテストする
エラーシナリオのテスト
認証エラーのテスト
# 無効なAPIキー
curl https://api.omise.co/account \
-u invalid_key:
# Returns 401 authentication_failure
# 間違ったキータイプ
curl https://api.omise.co/charges \
-X POST \
-u pkey_test_...: \
-d "amount=100000"
# Returns 401 authentication_failure
検証エラーのテスト
# 金額が小さすぎる
curl https://api.omise.co/charges \
-X POST \
-u skey_test_...: \
-d "amount=100" \
-d "currency=thb"
# Returns 400 bad_request
# 無効な通貨
curl https://api.omise.co/charges \
-X POST \
-u skey_test_...: \
-d "amount=100000" \
-d "currency=invalid"
# Returns 400 bad_request
支払いエラーのテスト
特定のエラーをトリガーする特別なテストカードを使用します。
# 残高不足
curl https://vault.omise.co/tokens \
-X POST \
-u pkey_test_...: \
-d "card[number]=4111111111111111" \
-d "card[name]=Test User" \
-d "card[expiration_month]=12" \
-d "card[expiration_year]=2025" \
-d "card[security_code]=123"
# chargeでトークンを使用 - insufficient_fundで拒否されます
# 処理失敗
curl https://vault.omise.co/tokens \
-X POST \
-u pkey_test_...: \
-d "card[number]=4000000000000002" \
-d "card[name]=Test User" \
-d "card[expiration_month]=12" \
-d "card[expiration_year]=2025" \
-d "card[security_code]=123"
# chargeでトークンを使用 - failed_processingで拒否されます
FAQ
失敗した支払いを自動的に再試行する必要がありますか?
いいえ、支払い失敗(4xxエラー)を自動的に再試行しないでください。 拒否されたカードを繰り返し試行すると、以下の問題が発生する可能性があります。
- 詐欺アラートをトリガーする
- 発行者によってカードがブロックされる
- 顧客を苛立たせる
代わりに:
- 明確なエラーメッセージを表示する
- 顧客に支払い詳細を確認するよう依頼する
- 別の支払い方法を提案する
- 顧客が準備できたときに手動で再試行できるようにする
**サーバーエラー(5xx)**は指数バックオフで再試行できます。
chargeが失敗したかどうかを確認するにはどうすればよいですか?
HTTPステータスコードとchargeのステータスを確認してください。
成功:
- HTTP 200 OK
charge.status = "successful"(キャプチャ済み)charge.status = "pending"(承認済み、まだキャプチャされていません)
失敗:
- HTTP 402 Payment Required
payment_declinedのようなコードを含むエラーレスポンスcharge.status = "failed"または"expired"
4xxと5xxエラーの違いは何ですか?
4xx = クライアントエラー(あなたの過失)
- リクエストが無効でした
- リクエストを修正して再試行してください
- 例: 不正なパラメータ、拒否された支払い、無効な認証
5xx = サーバーエラー(私たちの過失)
- Omise側で問題が発生しました
- リクエストは有効でしたが、処理できませんでした
- バックオフで再試行してください
- 例: サービスダウン、内部エラー
再試行する前にどのくらい待つ必要がありますか?
指数バックオフを使用してください。
- 最初の再試行: 1秒待機
- 2回目の再試行: 2秒待機
- 3回目の再試行: 4秒待機
- 4回目の再試行: 8秒待機
- 3〜5回の試行後にあきらめる
async function retryWithBackoff(fn, maxAttempts = 5) {
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
return await fn();
} catch (error) {
if (error.statusCode < 500 || attempt === maxAttempts - 1) {
throw error;
}
const delay = Math.pow(2, attempt) * 1000;
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}