SQSを用いたクレジットカード決済の非同期化

SQSを用いたクレジットカード決済の非同期化

こんにちは、カート決済部カート決済サービスブロックの林です。普段はZOZOTOWN内のカートや決済の機能開発、保守運用、リプレイスを担当しています。

弊社ではカートや決済機能のリプレイスを進めており、これまでにカート投入のキャパシティコントロールや在庫データのクラウドリフトを実現しています。

techblog.zozo.com

techblog.zozo.com

本記事では新たにクレジットカード決済処理を非同期化したリプレイス事例を紹介します。

はじめに

本章では、非同期化前のZOZOTOWNのクレジットカード決済を用いた注文処理の流れを説明します。

ZOZOTOWNの注文情報はオンプレミス環境で稼働しているSQL ServerのCartDBとFrontDBという2つのDBで管理しています。注文リクエストがくると、CartDBに注文データを作成します。この時に作成される注文データをZOZOTOWNでは仮注文と呼んでいます。仮注文の作成後、クレジットカード与信取得のためのリクエストを行います。与信取得後、仮注文の状態を与信完了に更新します。その後、CartDBからFrontDBへ注文データを移します。FrontDBの注文データを本注文と呼んでいます。

上記の処理の流れを以下の図に示します。

非同期化前の構成

背景・課題

ZOZOTOWNでは、セールの終了時刻となる日曜から月曜にかけての日跨ぎ時に注文のピークを迎えます。この時の注文数が通常時の十倍前後となります。

次のグラフはあるセールの日の日跨ぎ時の注文数の推移です。

注文数

注文がスパイクしたタイミングで、レイテンシも上がっています。レイテンシが上がることで、ユーザーの待ち時間が長くなりUXの低下が発生します。以下が注文リクエストのレイテンシの推移です。

注文リクエストのレイテンシ

最初は、このレイテンシの悪化の原因がDBにあると考え、日跨ぎ時のDBのCPU使用率を調査しました。以下が同じ時間帯のDBのCPU使用率の推移です。

CartDBのCPU使用率 FrontDBのCPU使用率

DB自体の負荷は余裕があり安定した状態となっていました。原因を深掘りしたところ、クレジットカードの与信リクエストの遅延でした。社外のAPIになるため、我々が直接レイテンシを改善することは困難でした。さらに、今後の成長を踏まえると、ユーザーの待ち時間がより長くなることで、UXの低下が予想されました。

そこで、UXの改善のための最初の対応としてクレジットカード決済処理の非同期化を進めることとしました。

非同期化のシステム構成

非同期化を行うにあたり以下の3パターンを検討しました。

  1. 与信処理から非同期化し、注文成立していない状態でも注文完了の画面を表示する
  2. 1のパターンを用意するが、同期処理にも切り替えられるようにする
  3. 与信処理から非同期化し、フロント側ではポーリングを行い注文成立したら注文完了の画面を表示する

それぞれの詳細とメリット・デメリットを以下に記載します。

パターン1 - 完全非同期化パターン

このパターンではユーザーとの注文完了コミュニケーションも非同期になります。処理フローは以下のとおりです。

  1. Webサーバが注文リクエストを受け付ける
  2. 仮注文の作成
  3. メッセージングサービスにエンキュー
  4. Webサーバは受付完了の画面を返す
  5. Workerがデキュー
  6. クレジットカードの与信確保のリクエスト
  7. CartDBに与信結果を保存
  8. FrontDBに本注文を作成

パターン1

5以降の処理が非同期になっています。ユーザーから見た場合、エンキューしたタイミングで画面が切り替わるため与信処理のレイテンシに影響されなくなります。

メリットとしては以下が挙げられます。

  • 与信処理のレイテンシに影響されることなく注文リクエストが完了する
  • キューとWorkerによるキャパシティコントロールができる

デメリットとしては以下が挙げられます。

  • ユーザーは受付完了の画面に遷移できた場合でも与信NGで注文成立しない可能性がある
  • 注文成立しない場合は支払い方法の変更通知などをメールで知らせる必要がある
  • 支払い方法変更の猶予を持たせるために、在庫を確保しておく必要がある
  • 注文数が少ない時間帯でも注文NGの場合に、ユーザーコミュニケーションが非同期になってしまう

パターン2 - 非同期・同期切り替えパターン

このパターンはパターン1の構成と既存の構成どちらも利用します。環境変数やアプリケーションロジックにより、既存パターンとパターン1の非同期化ロジックを切り替えられるようにします。

メリットとしては以下が挙げられます。

  • ピーク時はパターン1を利用できる
  • 注文数が少ない時は、既存ロジックに切り替えることでピーク時以外はパターン1のデメリットをカバーする

デメリットとしては以下が挙げられます。

  • 同期と非同期どちらも必要になるので決済フローの改修コストが増える

パターン3 - ポーリングパターン

パターン3

このパターンでは、パターン1と同様に与信処理は非同期化を行います。UXの低下は避けたいので、フロントからポーリングを行い、注文状態を確認するようにします。本注文が作成されたら、注文完了の画面に遷移します。与信等でエラーになった場合はカートトップに戻ります。処理フローは以下のとおりです。

  1. Webサーバが注文リクエストを受け付ける
  2. 仮注文の作成
  3. メッセージングサービスにエンキュー
  4. Webサーバはポーリング用のIDを返却
  5. フロントはIDを用いてポーリング開始
  6. Workerがデキュー
  7. クレジットカードの与信確保のリクエスト
  8. CartDBに与信結果を保存
  9. FrontDBに本注文を作成

6以降の処理が非同期になっています。非同期で本注文が作成されると5でポーリングしているところで検知し、注文完了の画面に遷移します。ユーザーから見た場合は既存のUI/UXと変わりませんが、与信確保のリクエストはキャパシティコントロールされています。

メリットとしては以下が挙げられます。

  • キューとWorkerによるキャパシティコントロールができる
  • 注文リクエスト自体は与信処理のレイテンシに影響されない
  • 注文完了のユーザーコミュニケーションを非同期にする場合でも非同期部分のフローは変更なしで実現できる

デメリットとしては以下が挙げられます。

  • ユーザーから見た時のUI/UXは変わらないので、注文リクエストをしてから注文完了の画面に行くまでに待ち時間がかかる

システム構成の決定

3つのパターンの中から弊社ではパターン3を選択しました。理由は以下のとおりです。

  • 現在のUXから低下させる部分を作りたくない
    • パターン1ではピーク時以外のUXが低下してしまう
  • 今後の改修コストの増加を極力避けたい
    • パターン2では同期と非同期の二重開発になり改修コストが増える
  • 注文完了のユーザーコミュニケーションを非同期にした場合、ビジネス要件の調整に時間がかかる
    • 与信失敗した場合にその注文はどうするのか
    • 支払い方法変更を受け付ける場合、どのくらいの期間受け付けるか
  • 今後ピーク時だけ注文完了のユーザーコミュニケーションを非同期にすることも比較的容易に可能である

パターン3でキャパシティコントロールを実現しつつ、並行してユーザーコミュニケーションの非同期化のためのビジネス要件の調整を進めることにしました。

メッセージングサービスの選定

データをキューイングするためのメッセージングサービスの検討もおこないました。今回候補に上がったAWSのサービスが以下の2つです。

  • Amazon Kinesis Data Streams (KDS)
  • Amazon Simple Queue Service (SQS)

それぞれの特徴やメリット、デメリットの詳細はカートリプレイスPhase1のテックブログで触れているのでご参照ください。

techblog.zozo.com

クレジットカード決済の非同期化では以下4点を主な検討事項としました。

  • 厳密な順序性はいらない
  • スパイクにも耐えられる
  • 1リクエストが詰まっても他に影響がでないようにしたい
  • なるべくマネージドなサービスにしたい

この中で採用の決め手となったのが、「1リクエストが詰まっても他に影響がでないようにしたい」でした。

与信処理では一部のリクエストだけが遅くなることがあります。そのため、特定のリクエストが遅延した場合でも他のリクエストへの影響を減らせる構成を検討していました。

KDSの場合シャード単位で読み出すため、特定のリクエストが遅延した場合の影響はシャード全体に渡ります。

SQSの場合は、特定のリクエストが遅延してもその他のWorkerがリクエストを処理できるので影響範囲を少なくできます。

上記の点から、本件ではSQSを採用することにしました。

効果

パターン3を実装した結果、仮注文作成までが注文リクエストの役割となったため、注文リクエスト自体のレイテンシは安定化し高速になりました。非同期化の実装前後での日跨ぎ時のレイテンシの比較は以下のとおりです。

非同期化前後のレイテンシ比較

また、SQSとWorkerでキャパシティコントロールができるようになった結果、与信リクエストの遅延がなくなりました。さらには、どれくらい与信キューに溜まっているかを可視化できるようになりました。以下は、日跨ぎ時に滞留しているキュー数の推移です。

滞留しているキュー数

滞留しているキュー数が明確になったことで、今後のZOZOTOWNの成長率との組み合わせでどれくらいキューに溜まり、注文完了までどれくらいかかるかの計算が可能になりました。

今後の展望

今回与信のキャパシティコントロールは実現したものの、UXの改善には至りませんでした。現状は注文完了の画面表示までのユーザーの待ち時間はリプレイス前と変わりません。今後ZOZOTOWNが成長していくと、必ず待ち時間は延びていきます。そのため、ユーザーコミュニケーションを非同期にしていく必要性があります。ユーザーコミュニケーションを非同期にした場合、以下の検討事項があります。

  • 与信失敗時の注文データの取り扱い
    • 既存では失敗時はカートに商品を戻している
    • 再度注文を行なってもらうより支払い方法変更できることが望ましい
  • 支払い方法を変更できる場合はどれくらいの期間在庫を確保するか
    • 在庫の確保期間が長ければ売り上げ損失になる可能性がある
    • 在庫の確保期間が短ければUXの低下を招く可能性がある

これらの検討事項はシステムだけではなく、ビジネス要件とも大きく関係してきます。現時点ではまだ検討中で、ビジネス側と密に連携し、今後の方針を決めていきたいと考えています。

まとめ

本記事ではクレジットカード決済の非同期化のシステム構成を紹介しました。非同期化したことにより、与信処理のキャパシティコントロールを行うことができました。非同期化を検討している方がいれば、ぜひ参考にしてみてください。今後はユーザーとのコミュケーション部分も非同期で行えるようにし、UXの向上を目指していきたいと考えています。

さいごに

ZOZOでは、一緒にサービスを作り上げてくれる方を募集中です。ご興味のある方は、以下のリンクからぜひご応募ください。

corp.zozo.com

カテゴリー