この記事は、TypeScript Advent Calendar 2022 20日目の記事です。
Stripeをシステムに組み込む場合、Stripe APIまたはSDKがThrowするさまざまなエラーを処理する必要があります。
そしてTypeScriptの場合、try ~ catch
でキャッチしたエラーの型がunknown
になるため、適切にハンドリングするか、as
などを利用して対応します。
この記事では、Stripe SDKからのエラーであるかを判別する方法などを紹介します。
Stripe SDKのエラーレスポンスは、StripeError
を拡張している
Stripe SDKがThrowするStripeに関するエラーはError
をextendしたStripeError
クラスを基にしています。
/**
* StripeError is the base error from which all other more specific Stripe errors derive.
* Specifically for errors returned from Stripe's REST API.
*/
class StripeError extends Error {
readonly message: string;
readonly type: string;
readonly raw: unknown;
readonly rawType?: RawErrorType;
readonly headers?: {[header: string]: string};
readonly requestId?: string;
このクラスをさらにextendして、カードに関するエラーやAPIキーの権限といった様々なエラーを表現しています。
e instanceof Stripe.errors.StripeError
でStripe SDK由来のエラーか否かを判別する
Node.jsのStripe SDKでは、StripeError
をベースにエラーオブジェクトが作成されています。
そのため、instanceof StripeError
でStripe APIに関するエラーかを判別できます。
try {
await stripe.customers.retrieve('cus_dummy')
} catch (e) {
if (e instanceof Stripe.errors.StripeError) {
console.log(`[Error: ${e.code}] ${e.message}`)
} else {
console.log(e)
}
}
このコードを実行すると、[Error: resource_missing] No such customer: 'cus_dummy'
が表示されます。
また、StripeError
に所属するプロパティであれば、VS CodeなどのIDEで入力補完が利用できます。
特定のエラーケースのみハンドルする
「リクエストパラメータが不完全なケース」や「認証エラー」など、特定のケースのみcatchしたいケースも存在します。
その場合は、処理したいエラーコードに対応するエラークラスが存在するかを確認して、instanceof
の分岐を設定することができます。
参考として、2022/11時点のエラークラス生成処理部分のコードを引用しました。
static generate(rawStripeError: StripeRawError): StripeError {
switch (rawStripeError.type) {
case 'card_error':
return new StripeCardError(rawStripeError);
case 'invalid_request_error':
return new StripeInvalidRequestError(rawStripeError);
case 'api_error':
return new StripeAPIError(rawStripeError);
case 'authentication_error':
return new StripeAuthenticationError(rawStripeError);
case 'rate_limit_error':
return new StripeRateLimitError(rawStripeError);
case 'idempotency_error':
return new StripeIdempotencyError(rawStripeError);
case 'invalid_grant':
return new StripeInvalidGrantError(rawStripeError);
default:
return new StripeUnknownError(rawStripeError);
}
}
以下の例では、InvalidRequest Error
を「ユーザーが入力したデータによるエラー」と仮定し、それ以外を内部エラーとしてレスポンスするAPIを作成しています。
try {
await stripe.customers.retrieve('cus_dummy')
} catch (e) {
// リクエスト内容が不完全なので、HTTP 400を返す
if (e instanceof Stripe.errors.StripeInvalidRequestError) {
return res.status(400).json({
code: e.code,
message: e.message,
})
// 想定外のエラーがStripeから来ている可能性が高いため、HTTP 500で内部エラーとする
} else if (e instanceof Stripe.errors.StripeError) {
return res.status(500).json({
code: e.code,
message: "Internal server error"
})
// Stripeが関係しないエラーが発生したため、これも内部エラーとする
} else {
return res.status(500).json({
message: "Internal server error"
})
}
}
Stripe APIエラーを型安全にハンドルし、レポーティングやユーザーへのフィードバックに活用しよう
StripeのAPIエラーレスポンスには、「関係性の高いと思われるドキュメントのURL」や「ダッシュボードでAPIリクエスト内容を確認できるページのURL」など、様々な追加情報が含まれています。
型安全にStripeのAPIエラーを処理することで、これらの追加情報をユーザーへのフィードバックやトラッキングツールに送信し、UXやバグ改善のフローを効率化しましょう。