Stripe 3Dセキュアの導入方法

#はじめに
Stripeから日本での3Dsecureがリリースされたので早速導入してみました。
Stripeの公式ドキュメントはきちんとまとまってはいるのですが日本語ドキュメントがありません。
英語が苦手だーという方や、どっかに実装例転がってないかなーという方、実装手順を備忘録的にまとめてみますので、参考になれば幸いです。

#3Dセキュアとは
>3Dセキュア（すりーでぃーせきゅあ）とは、クレジットカードによるネットショッピングの決済時に利用される本人認証サービスである。3Dとは3つのドメインのことでイシュアドメインがカード会社を、アクワイアラドメインが加盟店を認証し、相互運用ドメインが取り引きを仲介する 

wikipediaより引用 [wikipedia](https://ja.wikipedia.org/wiki/3D%E3%82%BB%E3%82%AD%E3%83%A5%E3%82%A2)

イシュアやアクワイアラといった単語が出てきますが、これ自体はクレジットカードの機構のお話になるのでここでは触れません。3Dセキュアは購入時の本人認証の仕組みだと思ってください。
今回はこの本人認証サービスを導入したというお話になります。

#3Dセキュア実装時の決済までのステップ
決済システムを導入されたことのある方なら分かるかと思いますが、実際に決済が行われた際に限度額いっぱいなどのエラーが発生することの無いように、仮決済と本決済で分けて与信枠を確保するというものが一般的です。
Stripeの3Dセキュアの実装において、下図のように大きく4ステップに分かれますので、具体的に何をすればよいかを各ステップごとに書いていきます。
Stripe管理画面を用いての3Dセキュアは仮決済はフロントサイド、本決済はバックエンドで行われます。
<img width="889" alt="スクリーンショット 2019-03-14 17.14.05.png" src="https://qiita-image-store.s3.amazonaws.com/0/314384/0bc5624b-4bd1-ab84-6042-6331319b0dc5.png">

##出てくる単語の説明
###Element
Stripeが提供している決済における決済ボタンや入力フォームなどの既成UIコンポーネントのこと。
開発者側で入力フォームなどを作成しなくてよくなる便利な代物。
###PaymentIntent
Stripeが提供している支払いAPI群のこと、本書ではPaymentIntentのオブジェクトとしても記載している。
3Dセキュアを用いた決済を行う場合、このAPIを使用しなければならない。
###Stripe管理画面
Stripeが提供している管理画面。決済の履歴や、使用したクレジットブランドなど、決済記録が確認出来る。
また、その他機能も充実しており、本書では管理画面で設定できる3Dセキュアのルールを用いる際に出てくる。

##Step1:入力フォーム作成
###Elementの作成
3Dセキュアを使うためにはStripe.jsのバージョン3を使用し、Elementを作成する必要があります。
今回の3Dセキュア導入前まではStripe.jsのバージョン2を使用していたため、まずはバージョンアップを行いました。
大きく違った点として、導入前までは自分たちで入力フォームを作成しその値を使用していたものをstripe側が用意した入力フォームを用いる点です。

Stripe.jsのバージョン3を読み込み、公開可能キーと、betasの引数を渡しオブジェクトを作成後Elementを作成します。
ここでのbetasの引数ですが、後ほど出てくるpaymentIntentを使用する際には必要なパラメータなのでお忘れないよう気をつけてください。

```html

    <script src="https://js.stripe.com/v3/"></script>
    var stripe = Stripe(
        'stripeの公開可能キー',
        {betas: ['payment_intent_beta_3']}
    );
    var elements = stripe.elements();
```
###Elementのmount
html上で作成したidやclassに先程作成したelementをmountします。
今回はcreditCardHolderというidのタグにmountしてみます。

```javascript:payment.js
    var stripeCard = elements.create('card');
    stripeCard.mount('#creditCardHolder');
```
ここでのelements.create('hoge')はStripeが指定しているものしか入れることが出来ないので、下記ドキュメントを確認して入れてください。
https://stripe.com/docs/stripe-js/reference#elements-create
この様なクレジットカード入力フォームが出来上がりました。
<img width="632" alt="スクリーンショット 2019-03-13 19.07.35.png" src="https://qiita-image-store.s3.amazonaws.com/0/314384/9eee4c67-7b6c-56d0-dde1-be850d7179c2.png">
自分たちでフォームを作成しないでよいのは便利ですね。
https://stripe.dev/elements-examples/
こちらを参考にして入力フォームのデザインを変えることも可能です。今回は割愛します。

###入力フォームのリアルタイムバリデーション
先程のクレジットカード入力フォームにリアルタイムのバリデーションを入れることも出来ます。

```javascript:payment.js
    stripeCard.addEventListener('change', function(event) {
      var displayError = document.getElementById('card-errors');
      if (event.error) {
        displayError.textContent = event.error.message;
      } else {
        displayError.textContent = '';
      }
    });
```
mountしたelementを使用することで、入力フォームに入力している際にエラーが出てくれます。
<img width="606" alt="スクリーンショット 2019-03-14 12.11.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/314384/e611aeb9-a509-c696-c2e7-b6aeef62741f.png">
これもいいですね。初めからカード番号欄に英語や記号を入れることが出来ないようになっている点もありがたいです。

##Step2:customerオブジェクト、paymentIntentオブジェクト作成
次に実際の決済に必要なオブジェクトを作成していきます。
クレジットカード情報を入力し、submitしたタイミングで下記のようにajax通信を用いて作成します。
APIは予めサーバサイドに用意しておいてください。

```javascript:payment.js
    //customerオブジェクト作成
    function createCustomer() {
        return $.ajax({
            type: "POST",
            url: "/customer作成用URL",
        });
    }

    //paymentIntentオブジェクト作成
    function createPaymentIntent(customerId) {
        return $.ajax({
            type: "POST",
            url: "/paymentIntent作成用URL",
            data: {
                'totalCharge': 仮決済をしたい金額,
                'customer': customerId,
            }
        });
    }
```
まずcustomerオブジェクトを作成します。この際引数は無しで問題ありません。
そのcustomerIdと仮決済を行いたい金額を用いてpaymentIntentオブジェクトを作成します。
仮決済の金額と本決済の金額の差分があるとエラーになりますので、同一のpaymentIntentで決済をする場合は、同じ金額で仮決済を行うようにしてください。
こちらのPaymentIntent作成時はきちんと自分のサーバでのバリデーションや、CSRF対策を行うようにしてください。また、PaymentIntentはむやみに作成せず、必要な時にだけ作成するようにしましょう。

##Step3:仮決済処理
###仮決済処理
準備が整いましたので、仮決済処理に入っていきます。
3Dセキュアを管理画面上の設定ルールに沿って運用する場合、handleCardPayment()を使用します。
ここで行うことは下記の様な処理になります。

```javascript:payment.js
      function provisionalPayment(clientSecret) {
          stripe.handleCardPayment(
              clientSecret,
              stripeCard,
              {
                  source_data: {
                      owner: {
                          name: クレジットカード名義
                      }
                  },
              }
          ).then(function (result) {
              //成功またはエラーの処理を記載
          });
      }
```
handleCardPaymentの使い方はドキュメントにまとめられているので下から確認ください。
https://stripe.com/docs/stripe-js/reference#stripe-handle-card-payment
必要な引数として、clientSecret,cardElementがあります。
ownerオプションにはクレジットカード名義や、電話番号、emailアドレスが指定出来ます。

**※このhandleCardPayment実行時に仮決済が走るため、ここでエラーになった場合仮決済が通った状態で本決済が行われずにクレジット枠だけ押さえられてしまう場合があります。
こちらはきちんとクレジット枠の解放を行うようにしてください。解放方法についてはまた後日追記したいと思います。**


###Stripe管理画面のレーダーを用いた3Dセキュア
仮決済処理に関わってくるのでこのステップで説明しますが、
先程書いた管理画面の設定ルールを見て3Dセキュアの対象かどうかを判断する方法が、paymentIntentを用いたhandleCardPaymentです。
<img width="1027" alt="スクリーンショット 2019-03-14 16.51.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/314384/6e24b9c7-22b8-428d-0e3d-74e782cd98a3.png">
上の画像のように管理画面の中にルールを設定する場所があります。
handleCardPaymentを用いなくとも3Dセキュアの実装は出来るのですが、どのようなステータスの人に対して、3Dセキュアをかけるかというような設定を自分たちのリソース上に持っておかなければなりません。
https://stripe.com/docs/payments/3d-secure#three-ds-radar
上記のようにpaymentIntentとhandleCardPaymentを用いることで自分たちのリソース上に設定を持たないでよくなり、GUI上で設定されたルールを見て勝手に判断してくれ運用がかなり楽になるためこの組み合わせで3Dセキュアを運用するのが良いと思います。

##Step4:本決済処理
最後に本決済処理です。
ここからはフロントサイドではなくサーバサイドのお話となります。
サーバサイドの処理はものすごく単純なので簡単に説明していきます(人によっては端折ってもよいかも)
###paymentIntentIdを用いて本決済

```php:payment.php
    PaymentIntent::retrieve(paymentIntentId);
    $paymentIntent->capture();
```
サーバサイドに渡ってきたpaymentIntentオブジェクトのIdを引数に渡し、captureメソッドを用いて本決済を行います。
これで本決済が完了です。
他にもwebhookイベントを受け取って決済を行うことも可能です。
エラーハンドリングは各々でしっかりお願いします！

##どうなったら出来上がり？
テスト環境の設定になりますが、管理画面上でルールを設定しました。
<img width="1096" alt="スクリーンショット 2019-03-14 18.10.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/314384/11c7d082-5ac8-36d3-a9c5-6b1dd961e961.png">
上から2番目のルールをオンにしています。
この状態で、実装完了後下記のstripeが提供しているテストカードを使って決済をしてみます。
https://stripe.com/docs/testing#three-ds-cards
3Dセキュア対象のテストカードで決済を行い、このような認証画面が立ち上がれば成功となります。
<img width="801" alt="スクリーンショット 2019-03-14 18.09.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/314384/90883775-091c-7604-78f7-85912094068f.png">

##導入にあたり注意する点
###本番環境での本人認証確認画面
開発環境でのテストでは本人認証(stripeが用意したもの)が立ち上がっていたのに本番で動作確認をすると立ち上がらないといったことが起きました。
これに関しては、クレジットカード利用者が各クレジットカード会社(UC、セゾンなど)の本人認証サービスの登録をしていないと立ち上がりません。会社によっては登録をしていなくても登録を促す画面が立ち上がることもあるのですが、これはエンドユーザ次第なのでテストの際は本人認証サービスに登録してあるカードで確認するようにしましょう。
###対象のクレジットカードブランド
現状Stripeの3DセキュアはVisa,MasterCard,JCBの3つを対応しています。
Amexはまだ非対応なので導入の際にはご注意ください。



##まとめ
以上4ステップでStripeの3Dセキュアが実装出来ます。
Stripeを導入済みでまだ3Dセキュアを実装していない方がいらっしゃったら是非参考にしてみてください。