アジェンダ
経済産業省のウェブサイトによると、3Dセキュア2認証の導入が義務化されており、事業者は2025年3月末までに対応する必要があるらしい。
EC取引の増加に伴い、不正利用や詐欺が増加しているため、より強固なセキュリティが求められているとのこと。
運用している業務システムの決済は、Stripeを導入しており、義務化に向けて本対応が求められた。
え、既存のStripeの実装はどう改修したらええの?
ってか、3Dセキュアって何、1とか2とかなに?
認証画面ってどうやって出すの、プロダクト側で新規実装しなきゃあかん?
…という、最初の一歩からつまずきやすかったため、対応した基本的な内容を備忘録としてまとめます。
3Dセキュアってなに?
クレジットカード決済する際、各ECサイトの画面からカード会社の認証画面にリダイレクトした事はないだろうか。
それです。
3Dセキュア1
- モバイル決済に対応していない。
- 認証画面へのリダイレクトが発生し、各カード会社のページで認証操作を行う必要がある。
- このリダイレクトにより、ECサイトでは 「カゴ落ち(決済前に離脱すること)」 のリスクが増加。
- ユーザーはあらかじめ設定した固定パスワードを入力する必要があり、セキュリティが低い上に操作性も悪かった。
3Dセキュア2(EMV 3Dセキュア)
- リダイレクトを必要とせず、webViewなどのインラインフローで認証が可能。
- 認証内容は動的認証(ワンタイムパスワード、生体認証など)を採用。
- 認証フローのスムーズさにより、ユーザー離脱を軽減。
- 動的な認証により、セキュリティが大幅に向上。
ざっくりアプローチ
Stripeでの大まかな流れは下記。
-
サーバー
- 3Dセキュア2に対応したAPIで決済を作成。
- 決済に3Dセキュア認証が要求される場合、クライアント側で認証フローを実行する必要がある。
-
クライアント
- StripeのSDKを使用し、3Dセキュアのチャレンジフローを表示。ユーザーに認証操作を行ってもらう。
-
サーバー
- 3Dセキュア認証が成功した場合、決済成功の後続処理を実行。失敗した場合は決済を中断。
- 3Dセキュア認証が不要な場合、クライアント側の処理をスキップして後続処理を実行。
- Success!!
移行方法
課題
既存の決済システムはchargesAPI
で実装されていた。このAPIは3Dセキュア1に対応しているものの、今回対応が求められる3Dセキュア2には対応していない。
そのため、chargesAPI
を利用している場合は、paymentIntentsAPI
への移行が必要となる。
両APIの簡単な違いは下記。
chargesAPI
chargesAPI
は、その名の通り支払い専用のAPIで、使用方法は非常にシンプル。メソッドを使ってリクエストを送信するだけで、Stripe上で決済処理が実行される。
\Stripe\Charge::create([
'amount' => 5000,
'currency' => 'usd',
'customer' => $customerId,
// その他パラメータ
]);
返金処理も可能で、従来の決済システムにおいてはchargesAPI
で十分対応できていた。しかし、3Dセキュア2認証には対応していないため、現在の要件には不適合。残念。
paymentIntentsAPI
chargesAPI
がレガシー技術とされる一方、paymentIntentsAPI
はStripeの最新機能を利用できるAPIである。3Dセキュア2認証にも対応しており、使い方はchargesAPI
と大きく変わらない。
paymentIntentsAPI
では、生成されたpaymentIntentが各決済を管理するオブジェクトとなる。
\Stripe\PaymentIntent::create([
'amount' => 5000,
'currency' => 'usd',
'payment_method' => $paymentMethodId,
'off_session' => false,
// その他パラメータ
]);
paymentIntentは、3Dセキュア認証が必要な場合、clientSecret
というパラメータを持ち、さらに決済処理の状態を示すstatusも保持する。
これらの情報はレスポンスに含まれているため、後続の処理を条件分岐で実装できる。
また、管理画面上でも該当の取引について、3Dセキュア認証待ちであることを示すステータスが確認可能である。
{
"client_secret": "pi_3tBLwu2tPa_secret_YicjGHhZloH",
"next_action": "use_stripe_sdk",
"status": "requires_action",
}
対応
実装
課題を踏まえ、以下の方針で対応した。
- バックエンドでは、既存の
chargesAPI
をすべてpaymentIntentsAPI
へ移行。- 既存の決済失敗フローを基に、3Dセキュアが要求される場合のみ別の分岐を実装し、デグレードリスクを回避。
- クライアント側ではStripeのSDKを導入し、3Dセキュア要求に応じたレスポンスを受け取った際に、認証画面(3Dセキュアチャレンジフロー)を表示できるように実装。
3Dセキュアの認証画面(チャレンジフロー)については、プロダクト側で新たに実装する必要は無かった(ありがたい🙌)。
clientSecret
をStripeの認証関連メソッドに渡すだけで、認証画面が自動的に表示される仕組みである。
なお、公式ドキュメントには、ウェブ、iOS、Android、React Nativeにおける対応方法が簡潔に記載されている。
その他
決済は比較的複雑で重要な機能のため、入念なテストを行った。
Stripeでは、通常の決済用テストカードに加えて、3Dセキュア認証に対応したテストカードも複数用意されている。これらを活用して認証フローを検証した。
終わりに
StripeのAPIリファレンスは非常に充実しており、実装を進めやすいと感じた。
しかし、特にchargesAPIからpaymentIntentsAPIへの移行手順や、3Dセキュアチャレンジの具体的な実装方法については、情報が少なく苦労した。。。
案外、同じような条件で実装された記事が出回っていなかった。
とはいえ、まだまだ入門レベルではあるが、Stripeに少し強くなった気がする。
あと、アドベントカレンダー初投稿で少し気持ちが上がってる。
ハッピークリスマス🎄