決済機能の実装(Stripe)

Webサービスを提供する際に欠かせないのがオンライン決済です。これを自分で作るのは大変過ぎるので、多くの場合は決済プロバイダを利用します。クレジットカード決済ができる決済プロバイダとして、PayPalやGMOなどいろいろとありますが、ここではStripeを使う方法を紹介します。

Stripeとは

Stripeは2011年に開始されたオンライン決済代行サービスです。米国サンフランシスコに本社があり、日本でも2016年からサービスが提供開始されました。

Stripeは決済プロバイダのサイトに遷移させることなく自サイトで決済処理を構築でき、支払いのコンバージョンを向上できるのが特徴です。

また、費用面は決済金額の3.6%が手数料として必要なだけで、それ以外はかかりません。審査等もないため、Stripeの公式ページで「いますぐ始める」をクリックして、メールアドレス等を登録すればすぐに始められます。

注意点としては、お客様への返金処理が発生した場合に手数料の3.6%は返ってこないという点だけです。返金があまり発生しなければあまり問題にはならないと思います。

開発用の公式ドキュメントも豊富で、主要な言語全てのサンプルコードが書かれているため、開発も容易な方だと思います。

テスト環境と本番環境が分けられており、テスト環境だけなら口座登録しなくても試せるので、試しに開発してみるということが気軽にできます。タイムマシーンという機能もあり、時間を未来に進ませてテストすることも可能です。

Stripeの仕組み

Stripeは、お客様のカード情報を自サイトに一切通さずに決済が可能なので、お客様の重要な情報を自サイトで保持することなく決済可能なサービスを提供できます。

Stripeとお客様、自サイトがどのような仕組みで連携しているのか、概要だけでも知っておくと自サイトの実装が理解しやすくなります。

Stripeの仕組み①

まず、Stripeアカウントを作成すると、シークレットキーと公開可能キーが付与されています。これらは、自サイトのWebサーバで使うのでサーバ内で使えるようにしておきます。

次に、ユーザがWebサーバにアクセスしてきて、クレジットカードを登録しようとした時に、カード登録用のStripe Elementと呼ばれるモジュールの入ったWebページと、公開可能キーを送ります。

シークレットキーは絶対にブラウザから見られないように気をつけましょう。(これがあると全てのお客様のほとんどの操作ができてしまいます)

Stripeの仕組み②

ユーザは、ブラウザ上でクレジットカード情報を入力して送信ボタンを押します。その情報はStripeに送られます。

この送信時に公開可能キーを使うので、対応するシークレットキーを持っているWebサイト関連の要求ということがStripeにわかり、Stripeはトークンを発行します。

Stripeの仕組み③

ユーザは、StripeからもらったトークンをWebサーバに送ります。

Webサーバは、ユーザからのトークンとシークレットキーを使い、Stripeにカスタマー生成要求をします。これには、シークレットキーが必要になります。(誰かになりすまされたら大変ですので、あなたのStripeアカウントのシークレットキーを持っている人だけがこれをできます)

Stripe側は、シークレットキーとトークンを検証し、問題なければカスタマーを生成して、そのカスタマーとクレジットカード情報を紐づけます。(Stripe側はトークンによってカード情報がわかります)

そして、Stripe側からWebサーバに生成したカスタマーのカスタマーIDなどが返却されます。

以降は、このカスタマーIDとシークレットキーを使えばいろいろな決済ができますので、このカスタマーIDは、自サイトのデータベースに保存しておくと便利です。

カード登録の実装

Stripeの実装のサンプルはこちらにかっこよく作られた5種類のサンプルがあります。これらのソースコードはgithubでも公開されていますので、これらを見ながらカスタマイズして使うのが良いと思います。

ですが、かっこよく作られているため装飾部分が多く、どこが重要なのかがわかりづらいかもしれないので、基本的な処理をシンプルに実装したサンプルをここで紹介します。(上記サイトのexample2をベースに簡素化しています)

フロント側

まずブラウザに表示する画面ですが、このようにカード番号、有効期限、セキュリティコードの最低限の3つだけを入力するフォームを作ります。

入力フィールドにプレースホルダーテキストがでていますが、これはStripeのSDKがやってくれています。また、入力がおかしい時に注意をだしてくれるのもSDKがやってくれます。

こちらのHTMLソースコード(register.html)です。

<html>
  <head>
    <title>Stripe Card Register</title>    
    <meta charset="utf-8">
    <link rel="stylesheet" href="example2.css">    
    <script src="https://js.stripe.com/v3/"></script>        
    <script src="stripe.js"></script>
  </head>
  <body>
    <form action="register.php" method="post" name="form1" id="payment-form">
      <label>カード番号</label><div id="example2-card-number"></div>
      <label>有効期限</label><div id="example2-card-expiry"></div>
      <label>セキュリティコード</label><div id="example2-card-cvc"></div>
      <br>
      <button type="submit">登録</button>
    </form>
  </body>
</html>

登録ボタンを押すと通常ならフォームに入力されたデータがsubmitされて、register.phpに渡されて実行されますが、ここではそうはせずに、その前にStripeサーバにカード情報を送り、トークンをもらってそのトークンをregister.phpに渡すという処理が stripe.jsに書かれています。

このstripe.jsを説明します。フォームのハンドラを実装し、フォームのデフォルトの動作をやめて独自にカード情報をStripeに情報を送り、Stripeからもらったトークンをフォームデータにしてregister.phpに渡します。

window.onload = () => {
    var stripe = Stripe('pk_...(Stripeアカウントの公開可能キー)');
    var elements = stripe.elements();

    var elementStyles = {
	  base: {
	    color: '#32325D'
	  },
	  invalid:{
	    color: '#E25950'
	  }
    };

    var elementClasses = {
	  focus: 'focused',
	  empty: 'empty',
	  invalid: 'invalid',
    };

    var cardNumber = elements.create('cardNumber', {
	  style: elementStyles,
	  classes: elementClasses,
    });
    cardNumber.mount('#example2-card-number');

    var cardExpiry = elements.create('cardExpiry', {
	  style: elementStyles,
	  classes: elementClasses,
    });
    cardExpiry.mount('#example2-card-expiry');

    var cardCvc = elements.create('cardCvc', {
	  style: elementStyles,
	  classes: elementClasses,
    });
    cardCvc.mount('#example2-card-cvc');

    // 入力変更時のリスナー。おかしい値が入ったらエラーを表示
    cardNumber.addEventListener('change', function(event) {
        var displayError = document.getElementById('card-errors');
        if (event.error) {
            displayError.textContent = event.error.message;
        } else {
            displayError.textContent = '';
        }
    });

    // submit時のリスナー
    var form = document.getElementById('payment-form');
    form.addEventListener('submit', function(event) {
        event.preventDefault();
        // Stripeサーバにクレジットカード情報を送信してクレジットカードトークンを取得する
        stripe.createToken(cardNumber).then(function(result) {	
            if (result.error) {
                var errorElement = document.getElementById('card-errors');
                errorElement.textContent = result.error.message;
            } else {
                // クレジットカードトークンを処理する。
                stripeTokenHandler(result.token);
            }
        });
    });

    function stripeTokenHandler(token) {
        // クレジットカードトークンをformデータにして、submit処理する。
        var form = document.getElementById('payment-form');
        var hiddenInput = document.createElement('input');
        hiddenInput.setAttribute('type', 'hidden');
        hiddenInput.setAttribute('name', 'stripeToken');
        hiddenInput.setAttribute('value', token.id);
        form.appendChild(hiddenInput);
        form.submit();
    }
}

前半、入力フォームのスタイル等を実装していますが、後半にsubmit時のリスナーがあります。ここで、preventDefault()で通常の処理をキャンセルし、入力されたフォームデータをcreateToken()でStripeに送ってトークンをもらいます。

(カード番号しか送ってないように見えますが、有効期限もセキュリティコードも送ってます。どれを送ってもこの3つは送られます。)

Stripeから正常にトークンを受け取ったら、改めてstripeTokenHandler()でフォームデータを作成し、そこにトークンをセットしてsumbit処理をします。すると、トークンだけがregister.phpに渡ります。

CSSファイルも記載します。(example2.css)

#example2-card-number {
    border: 1px solid #111111;
    padding: 2px 8px 2px 8px;
    width: 200px;
}

#example2-card-expiry {
    border: 1px solid #111111;
    padding: 2px 8px 2px 8px;
    width: 120px;
}

#example2-card-cvc {
    border: 1px solid #111111;
    padding: 2px 8px 2px 8px;
    width: 120px;
}

こちらは、3つの入力フォームの大きさなどを調整をしているだけです。

一応、この3つのファイルをコピーすればフロント側は完成します。

サーバ側

次にサーバ側の処理を説明します。サーバ側はPHPの書きます。ブラウザからフォームデータとしてトークンを受け取り、このトークンとシークレットキーを使ってStripeにカスタマー生成要求をし、ユーザとカード情報を紐づけてもらうという流れです。成功すると、ユーザのカスタマーIDが取得でき、以後のこのカスタマーIDを用いて決済ができるようになります。

それでは、PHPのコード(register.php)です。

<?php
  require __DIR__ . '/vendor/autoload.php';
  \Stripe\Stripe::setApiKey("sk_...(シークレットキー)");
  
  $token  = $_POST['stripeToken'];
  $email = "test@testdomain.com";
  $name = "test@testdomain.com";

  try {
    $customer = \Stripe\Customer::create([
      'source' => $token,
      'email'  => $email,
      'name'   => $name,
    ]);
    $customer_id = $customer->id;
  } catch (Exception $e) {
    echo "failed to register Card." . $e->getMessage() . "\n";
    exit();
  }
  echo "カードを登録しました。" . $customer_id;
?>

まず最初に、requireでPHPのライブラリをロードしてStripeのシークレットキーをセットします。

PHPのライブラリのインストールは、composerを使って可能です。インストール方法はこちらにも記載しております。

フォームデータの送信で受け取るトークンは、$_POSTの連想配列で受け取れます。実際には、トークンと合わせてユーザのIDとなるemailやnameの情報も同じ$_POSTでもらいますが、簡素化した例なので、test@testdomain.com を直書きしています。

これらの情報をStripeのライブラリを使って、Customer::createすればOKです。成功すればカスタマー情報が返ってくるので、customer->idをカスタマーIDとして、自サイトのデータベースにもユーザと紐づけて保存しておきましょう。

成功すると、こんな感じでカスタマーIDがブラウザに表示されます。(実際には見せない方が良いです)

テスト

 この4つのファイルをWebサーバでアクセスできる場所におき、シークレットキーと公開可能キーをそれぞれ設定すれば一応、カード登録までできます。テストアカウントであればテスト用のカードで試せますので、それで試してみます。

カード番号は、全て4242にします。有効期限は現在よりも未来ならOKです。セキュリティコードは数字3桁何でもOKです。

これで登録ボタンを押すと、register.phpに処理が渡り、成功すればブラウザに生成したカスタマーIDが表示されます。

それでは、Stripeアカウントのコンソールでも確かめてみましょう。

今、登録したユーザが顧客リストに入っていれば成功です。カード情報は下4桁しか表示されませんが、合ってることがわかります。

サブスクリプションの実装

次にいよいよ課金の実装方法ですが、これもほとんどStripeサイト側でやってくれます。

まず、Stripeのコンソール画面で、サブスクリプションの商品を作成しておきます。

商品カタログの商品作成から、作成できます。

商品の名前、金額、そしてタイプとして「継続」と「1回限り」が選べますが、「継続」を選べばサブスクリプションになります。

作成すると、商品カタログ画面にも下記のように表示されます。

ここのAPI IDの列に示されている price_xxxx という商品IDが必要になってきます。

自サイトのサーバ側では、シークレットキー、顧客のカスタマーID、この商品IDがあればサブスクリプション開始が処理できます。PHPのソースコードは以下のように実装できます。

<?php
  require __DIR__ . '/vendor/autoload.php';
  \Stripe\Stripe::setApiKey("sk_...(シークレットキー)");

  $customer_id = (ユーザのカスタマーID)
  $selected_price_id = (商品ID)

  try{
    $subscription = \Stripe\Subscription::create([
      'customer' => $customer_id,
      'items' => [
         [
           'plan' => $selected_price_id,
         ],
      ],  
    ]);
    $msg = 'Subscription ID: ' . $subscription->id;
    // subsctiprion IDは解約時に必要のため、自サイトのデータベースに登録
  } catch (Exception $e) {
    $msg = $e->getMessage();            
  }
  echo $msg;
?>

成功すると、返り値のsubscriptionオブジェクトからIDが取得できます。このsubscription IDが解約や変更時に必要になるので、これも自サイトのデータベースに登録しておくと良いです。

まとめ

ここまでで、クレジットカードの登録からサブスクリプションの開始までを紹介しました。なんとなくのイメージがわかれば、あとはStripeのドキュメントサンプルソースコード等を見れば自サイトへのカスタマイズが容易になると思います。大事なのは、何をやっているのを理解することです。

また、このソースコードの中でもクレジットカード情報を直接処理している部分はないです。(最初のフロント部分のjavascriptでStripeに渡しているだけです)自サイトのサーバは、トークンやカスタマーID、サブスクリプションIDなど、Stripeが発行したものだけを使っています。

それでいて、自サイトのページ内でカード登録から決済まで全てできてしまうので、よくある決済ページへの遷移などでコンバージョンが下がってしまうことも回避できます。

豊富なSDKやAPIに、サンプルやドキュメントも整備されていて、開発者にとっても実装しやすい決済プラットフォームだと思います。