Stripeの決済をChargeからPaymentIntentへ移行する

バックエンドで、主に販売管理などの経理業務向けアプリケーションを開発しているアズマです。 趣味はカピバラ鑑賞です。そろそろ実カピバラたちとのえさやりや握手を再開したいです。

はじめに

インターネットでのクレジットカード決済は今や当たり前に行うようになって久しいです。弊社でも電子チケット購入時にはクレジットカード決済が選べます。クレジットカードの決済は、決済代行会社であるStripeを利用しています。

Stripeを導入した当初の決済は ChargeAPI を利用し、クレジットカードの紛失や不正利用のリスクを軽減するための仕組みとして 3D Secure(3D Secure 1.0) を適用して、必要に応じてクレジットカード会社からの本人認証を挟む対応をしていました。 しかしこの3D Secure 1.0は急速に次バージョンである3D Secure 2.0への移行が推進されています。 その主だった理由は以下です。

  • 3D Secure 1.0はユーザー+パスワードによる認証方式で、2.0は1.0よりセキュアなワンタイムパスワード形式等に移行している
  • 3D Secure 1.0のまま利用していると、クレジットカード会社からのチャージバック*1補償が受けられなくなる可能性が発生する
  • 3D Secure 1.0はすべての決済に対して本人認証を実施してしまうため、利便性が明らかに低下する
  • 3D Secure 2.0は不正利用をリアルタイムで監視し、不正利用の疑いがある決済に対してのみ本人認証を要求するため、利用者の利便性を損いにくい

これを受けて弊社でも 3D Secure 2.0に対応した決済に移管することに決め、3D Secure 2.0に対応したStripeのAPIである、PaymentIntent (PaymentIntentAPI) を利用した決済へ移行しました。PaymentIntentに移行すると次のメリットも享受されます。

  • 自動認証処理
  • 二重支払いの防止用にべき等キーによる検証機構*2を提供

移行するにあたっての変更点

ChargeAPI(以降、Chargeと略します)からPaymentIntentAPI(以降、PaymentIntentと略します)へ移行するにあたっては、 https://stripe.com/docs/payments/payment-intents/migration:Stripe公式 Payment Intents API への移行 を参考にすれば概ね実施はできます。特にStripeが提供している標準UIをそのまま使っている場合は大きな変更が必要なく、StripeのUI内で決済完了まで行えます。

弊社ではStripeが提供するUIは利用せずに決済用のStripeクライアントスクリプトのみを利用し、購入した電子チケットや利用したクーポンなどを確認した後にサーバ側で決済を確定する、オーソリとキャプチャを分離したカスタム方式を採用しています。これは安全なトランザクション管理を第一としており、もし処理が失敗したときでも処理を正しくやり直せるためです。

PaymentIntentを使った決済を開始する

以下の図は弊社で採用したカスタム方式での大まかな決済フローです。

PaymentIntent移行後の発行から決済登録まで

サーバ側で決済を確定させるときの留意点としては、Chargeでは利用者が決済情報を入力した後にサーバ側でStripeへ決済を確認から決済の確定をしていたのに対し、PaymentIntentでは決済情報を入力する前にStripeからPaymentIntentを発行したあと、Webクライアント側で決済の確認をして、サーバ側でStripeへ決済を確定する流れに変更します。 ここが1つ目の大きな変更点です。

PaymentIntentを作成するコードは、以下のようになります。PaymentIntent作成時には決済フローの方法と決済金額を指定し、過去にStripeでクレジットカード決済を利用した場合は、StripeカスタマIDも指定します。 さらに、オーソリとキャプチャを分離するのと、選択した決済手段を再度利用できるよう、PaymentIntent作成時のパラメータに以下を設定します。

実装例: gist.github.com

パラメータ名 設定値※ 設定内容
FutureUsage OFF_SESSION 決済手段を今後も利用
CaptureMethod MANUAL オーソリとキャプチャを分離
Currency jpy 金額の単位:日本円
Amount 数値 金額
PaymentMethodType card クレジットカードを指定
Customer cus_から始まるID Stripe利用済みの場合はStripeが付与したカスタマ一意のID

※実際の設定値は、PaymentIntentCreateParamクラスに値を設定できるメソッドも用意されています。旧来からあるMap<String, Object>でも設定可能ですが、より型安全に扱えるクラスが提供されています。

発行したPaymentIntentからフロント側と共有するClientSecretの値を取り出した値を渡して、PaymentIntentの発行は完了します。

決済登録から完了まで

続いて、決済登録してから完了までの流れです。

PaymentIntent移行後の決済登録から決済完了まで

決済のオーソリ(確認)から決済確定までを、すべてStripeのクライアントスクリプトで実施する方法もありますが、弊社ではオーソリと決済を分離し、クライアント側でオーソリまで行ったあと、サーバ側では購入していただいたチケットや予約したアクティビティの情報をシステムへ確実に登録してから、決済の確定まで行う方式を選択しています。そのため、サーバ側で行うStripeに関する実装は、決済を確定するキャプチャのみになります。

以下がキャプチャする実装の一部です。オーソリまで完了したPaymentIntentのIDをあらためてStripeAPIから問い合わせ、決済が可能かをステータスで確認後にキャプチャを実施します。

なお、3D Secure 2.0の認証が発動したときには、Stripeクライアントスクリプトが自動的に制御します。3D Secure 2.0による認証が完了した後にサーバ側へ遷移しますので、3D Secureに伴うサーバ側の実装は不要になります。ここも大きな変更点です。

電子チケット以外の対応

新しいPaymentIntentへ移行する手順はこれで十分ですが、弊社では電子チケット以外にもアクティビティの予約も承っています。アクティビティに関しては電子チケットと異なり、申込み時はオーソリのみ実施して決済可能であるかを確認した後、予約していただいたアクティビティを実際に催行してから決済を確定しています。予約の申し込みから予約の催行まで30日以上開くことも珍しくなく、オーソリが確保できる期間は決済したクレジットカードにも依りますが、最短で7日までしか有効でないためこの手法をとっています。

アクティビティ予約の申込みと決済フロー

懸念点としては、PaymentIntent移行前に申し込みを受けたときはChargeで予約の催行をして、決済確定したときには移行後の仕組みで決済確定する期間が発生します。また、Chargeで決済IDは Charge専用の番号で、PaymentIntentで決済したIDはPaymentIntent専用であるため、新旧2つのAPIに対応が必要になり、どちらでも動作するロジックが必要となりました。

他にも、弊社経由で一度クレジットカード決済を使って電子チケット購入やアクティビティ予約をした際には、Stripeに登録したクレジットカード情報をもう一度使えるよう設定しています。つまりPaymentIntent移行前に登録した決済内容を、移行後も引き続き利用できるよう対応が必要です。 Stripeで決済した際の決済手段を扱うAPIもPaymentIntent移行前と移行後で別のAPIが用意されており、移行前(SourceAPI)と移行後(PaymentMethodAPI)とStripeのAPIが異なりますので、発行されるID体系も異なりますが、こちらに関してはPaymentMethodAPIで移行前であるSourceAPIのIDでも動作できましたので、別途データの付け替えなどは発生せずにそのまま利用可能です。

PaymentIntent移行後に取得方法や更新方法が変わる項目

Charges API と PaymentIntents API | Stripe のドキュメント にある比較表のとおりに沿って変更点を洗い出すことも可能ですが、ここに挙げた内容以外にも、決済のデフォルト手段として登録する方法が変わります。

PaymentIntent移行後に、決済手段(PaymentMethod)は決済したカスタマ(Customer)オブジェクトにて扱うよう変更していますが、決済手段のデフォルトとして設定するにはCustomerのフィールドを更新します。弊社サービスを長く利用されている方やクレジットカードの更新があった方の場合、1つのCustomerで複数の決済手段を持つこともありますので、前回使った決済手段をデフォルトに設定する方法として用意されています。

gist.github.com

まとめ

駆け足気味となりますが、PaymentIntentへ移行する際の手順や注意点をまとめました。実際に組み込む前には検証用の小さなアプリケーションを組んでPaymentIntent移行後の動作をStripeのテスト環境で試しながら進めていくと理解が深まります。

最後に

アソビューの開発についてもっと聞いてみたい!という方はこちらもぜひご覧ください!

www.asoview.com

*1:チャージバック:クレジットカードの会員、利用者が不正利用などの理由から利用代金の決済に同意しない場合、クレジットカード会社が加盟店様に対して支払いを拒否すること。

*2:1回の購入に対して一意のキーを発行しておくことで、多重決済していないかを検知します。特にこだわりがなければUUID v4で生成した値を用います