1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Stripe / JP_StripesAdvent Calendar 2024

Day 12

【Chargesから】Stripeで3Dセキュア2に対応するAPIへ移行した備忘【PaymentIntentsへ】

Posted at

アジェンダ

経済産業省のウェブサイトによると、3Dセキュア2認証の導入が義務化されており、事業者は2025年3月末までに対応する必要があるらしい。

EC取引の増加に伴い、不正利用や詐欺が増加しているため、より強固なセキュリティが求められているとのこと。

経済産業省: 「クレジットカード・セキュリティガイドライン」が改訂されました

運用している業務システムの決済は、Stripeを導入しており、義務化に向けて本対応が求められた。

え、既存のStripeの実装はどう改修したらええの?
ってか、3Dセキュアって何、1とか2とかなに?
認証画面ってどうやって出すの、プロダクト側で新規実装しなきゃあかん?

…という、最初の一歩からつまずきやすかったため、対応した基本的な内容を備忘録としてまとめます。

3Dセキュアってなに?

クレジットカード決済する際、各ECサイトの画面からカード会社の認証画面にリダイレクトした事はないだろうか。

それです。

3Dセキュア1

  • モバイル決済に対応していない。
  • 認証画面へのリダイレクトが発生し、各カード会社のページで認証操作を行う必要がある。
    • このリダイレクトにより、ECサイトでは 「カゴ落ち(決済前に離脱すること)」 のリスクが増加。
  • ユーザーはあらかじめ設定した固定パスワードを入力する必要があり、セキュリティが低い上に操作性も悪かった。

3Dセキュア2(EMV 3Dセキュア)

  • リダイレクトを必要とせず、webViewなどのインラインフローで認証が可能。
  • 認証内容は動的認証(ワンタイムパスワード、生体認証など)を採用。
    • 認証フローのスムーズさにより、ユーザー離脱を軽減。
    • 動的な認証により、セキュリティが大幅に向上。

ざっくりアプローチ

Stripeでの大まかな流れは下記。

  1. サーバー
    • 3Dセキュア2に対応したAPIで決済を作成。
    • 決済に3Dセキュア認証が要求される場合、クライアント側で認証フローを実行する必要がある。
  2. クライアント
    • StripeのSDKを使用し、3Dセキュアのチャレンジフローを表示。ユーザーに認証操作を行ってもらう。
  3. サーバー
    • 3Dセキュア認証が成功した場合、決済成功の後続処理を実行。失敗した場合は決済を中断。
    • 3Dセキュア認証が不要な場合、クライアント側の処理をスキップして後続処理を実行。
  4. Success!!

移行方法

課題

既存の決済システムはchargesAPIで実装されていた。このAPIは3Dセキュア1に対応しているものの、今回対応が求められる3Dセキュア2には対応していない。
そのため、chargesAPIを利用している場合は、paymentIntentsAPIへの移行が必要となる。

両APIの簡単な違いは下記。

chargesAPI

chargesAPIは、その名の通り支払い専用のAPIで、使用方法は非常にシンプル。メソッドを使ってリクエストを送信するだけで、Stripe上で決済処理が実行される。

.php
\Stripe\Charge::create([
    'amount' => 5000,
    'currency' => 'usd',
    'customer' => $customerId,
    // その他パラメータ
]);

返金処理も可能で、従来の決済システムにおいてはchargesAPIで十分対応できていた。しかし、3Dセキュア2認証には対応していないため、現在の要件には不適合。残念。

paymentIntentsAPI

chargesAPIがレガシー技術とされる一方、paymentIntentsAPIはStripeの最新機能を利用できるAPIである。3Dセキュア2認証にも対応しており、使い方はchargesAPIと大きく変わらない。

paymentIntentsAPIでは、生成されたpaymentIntentが各決済を管理するオブジェクトとなる。

.php
\Stripe\PaymentIntent::create([
    'amount' => 5000,
    'currency' => 'usd',
    'payment_method' => $paymentMethodId,
    'off_session' => false,
    // その他パラメータ
]);

paymentIntentは、3Dセキュア認証が必要な場合、clientSecretというパラメータを持ち、さらに決済処理の状態を示すstatusも保持する。
これらの情報はレスポンスに含まれているため、後続の処理を条件分岐で実装できる。

また、管理画面上でも該当の取引について、3Dセキュア認証待ちであることを示すステータスが確認可能である。

.json
{
    "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に少し強くなった気がする。

あと、アドベントカレンダー初投稿で少し気持ちが上がってる。
ハッピークリスマス🎄

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?