8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TypeScriptAdvent Calendar 2022

Day 20

TypeScriptでStripe SDKがthrowするエラーを型安全に処理する方法

Posted at

この記事は、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で入力補完が利用できます。

スクリーンショット 2022-11-15 14.37.00.png

特定のエラーケースのみハンドルする

「リクエストパラメータが不完全なケース」や「認証エラー」など、特定のケースのみ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やバグ改善のフローを効率化しましょう。

8
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?