php 定期課金システムを実装しよう
» STARTOUT詳細はこちら

WEBサービスづくり学習「STARTOUT」はじめました!

自分の「WEBサービス」を作りながら「制作技術」を習得しよう!

STARTOUTとは、WEB制作×サービス作りのオンライン実践学習サービスです。ゼロからWEBサービスを作り、収益化しながら技術を習得してみましょう!

 詳しくはこちら
ここでは「WEB制作×事業づくり実践学習サービス STARTOUT」の学習コンテンツの一部を公開しています。もし1,000以上あるすべてのコンテンツを学び尽くしたい場合、ぜひこちらからご登録ください!

定期課金システムを実装しよう

php|2019年01月15日

2019年01月15日
  • このエントリーをはてなブックマークに追加

では、今回はWEBサービスに課金システムの実装方法をお伝えしていきます。
特に、定期課金のサブスクリプションが作れるのが魅力です。
もちろん、クレジットカード決済可能。
noteなどのプラットフォームを使うより圧倒的に手数料が低いです。
課金システムさえ実装できれば、すぐにでも収益化の仕組みを作ることができるでしょう。

一昔前まで、定期課金システムの実装は、一部の高度な技術者だけの特権でした。
しかし、近年になってあるサービスが生まれたことで、それが一変したのです。

そのサービスがStripeです。

Stripeはソースコードを少し書ければ課金システムを実装できる優れものでした。
高度なスキルを持たなくても、安全かつ簡単に課金出来るようになったのです。
課金システムのイノベーションと言っても良いでしょう。

その後、同じ形式のサービスが多数生まれました。
もちろん日本でも同様のサービスが誕生し、多くの決済システムに使われています。

日本版のStripe、それこそがpay.jpです。

pay.jpは簡単にECショップを立ち上げられるサービスBASEから派生しました。
簡単な審査はありますが、pay.jpも簡単に決済システムを導入できます。
もちろん月額課金できますし、年額課金も可能です。

それでは、具体的な実装方法について、お伝えしていきましょう。

今回のミッション

今回はいよいよ、課金システムを作っていきましょう。
特に今回は月額/ 年額課金のシステムについてご説明いたします。

再現するファイルは下記になります。

ソースコードの解説を参考に、上記を再現してみてください。

一通りソースコードを読み、理解してから取り組むと良いでしょう。
新しい書き方も出てくるので、いきなりだと難しいかもしれません。

どうしても動かなければ、下記からファイルをダウンロードしてください。
細かい箇所のチェックはエラーなど、見つけてみましょう。

なお、毎回ご提供するこちらはあくまでも、参考ファイルです。
より良い書き方を見つけたら、ご自身で改良してみてください。

pay.jpにユーザー登録しよう

まずはpay.jpにアクセスし、会員登録を行ってください。
会員登録自体は、特に難しいことはありません。
他のサービスに登録するように、気軽に登録できます。

そして、ログインすると、管理画面に移動します。
下記のような画面が表示されるのではないでしょうか。

管理画面にはテストモードとライブモードがあります。
右上に表示されている部分ですね。

テストモードだと、課金システムを作ってテストしても、課金はされません。
また、テストカード番号も用意されていて、クレジットカードが無くてもテスト可能です。
テストカードの番号はこちらから確認してみてください。

また、最初にこちらから、pay.jpのライブラリをダウンロードしてください。
簡単に課金システムを使えるように準備されたプログラムです。
後から利用しますので「お支払いページと同じディレクトリ」にアップロードしておきましょう。

プランを作る

pay.jpの仕組みは、大きく考えて下記のような流れになります。

  1. 管理画面でプランを作る
  2. WEBサービスに、支払いフォームを実装
  3. 支払いフォームからデータがpay.jpに送られる
  4. 作成しておいたプランに紐付けられて課金

最初に管理画面から、プランを作る必要があります。
まず、左サイドバーからプランを選択してください。

そして、プラン作成を押します。

上記のような画面が表示されるはずですので、項目を入力しましょう。

  • 金額 – 課金する金額
  • 課金間隔 – 年次か月次か選べます
  • ID – 適当な英数字を入れましょう
  • プラン名 – 適当なプラン名を入れましょう
  • 課金日 – 課金する日 (未入力で課金日から月および年で支払い)
  • トライアル日数 – 無料お試し期間を設定できます

上記をすべて入力したら、保存を押してください。
プロダクトに関しても項目が異なるだけで同じように作成可能です。

これで、プランが完成しました。
次は、いよいよ実装に入っていきましょう。

お支払いフォームを作る

ご自身のサービスサイトに移動しましょう。
クレジットカードを入力してもらうフォームを作っていきます。

まず、フォームを作る前に、お支払いページのheadに下記のコードを入れましょう。

<script type="text/javascript" src="https://js.pay.jp/"></script>

このコードを入れることで、pay.jpにデータを送る機能を使えるようになります。
もちろん、上記のコードが入っていなければ動きません。

入力フォーム自体は、通常のHTMLとCSSで作りましょう。
ひとまず形を作ってから、システムを導入していきます。
フォームとして必要なのは、下記4つの項目です。

  • カード番号
  • 有効期限
  • カード確認コード(CVC)
  • お名前

では、さっそく実際のソースコードを見ていきましょう。

index.php

決済フォームの画面になります。
こちらにカード情報を入力していきます。

<!--エラーがあった時はこちらに表示-->
<div class="missArea cardmiss"></div>
 
<!--以下、クレジットカード入力フォーム-->
<dl>
    <dt>カード番号</dt>
    <dd><input type="text" name="card_number" placeholder="カード番号)4242424242424242"></dd>
    <dt>有効期限</dt>
    <dd><input type="number" name="card_exp_month" placeholder="月)12">/<input type="number" name="card_exp_year" placeholder="年)21"></dd>
    <dt>カード確認コード(CVC)</dt>
    <dd><input type="text" name="card_cvc" placeholder="CVC)123"></dd>
    <dt>お名前</dt>
    <dd><input type="text" name="card_name" placeholder="名前)TARO SASAKI"></dd>
</dl>
<button class="publishBtn" >登録する</button>
<div style="padding-top:40px;">
<div>テスト用カード番号は<a href="https://pay.jp/docs/testcard" target="_blank">こちら</a>から<br>
カード番号以外の情報は、グレーの文字通りご入力ください。<br>
実際に決済はされません。</div>
</div>
<script>
 
//公開鍵を設定
Payjp.setPublicKey("こちらに公開鍵を貼り付け");
 
//publishBtn(buttonを押した時に以下の処理スタート)
$(".publishBtn").click(function(){
     
    //一旦エラーエリアの内容をリセット(何も無い場合は変化なし)。
    $(".missArea").empty();
     
    //有効期限の年を取得
    //年を4桁すべて入力してもらうと手間がかります。
    //よって、2019の場合、19だけ入力してもらい、20はここで付け足す。
    var cvcdata = "20" + document.querySelector('input[name="card_exp_year"]').value;
     
    //他の項目もすべて取得
    var card = {
        number: document.querySelector('input[name="card_number"]').value,
        cvc: document.querySelector('input[name="card_cvc"]').value,
        exp_month: document.querySelector('input[name="card_exp_month"]').value,
        exp_year: cvcdata,
        name: document.querySelector('input[name="card_name"]').value
    };
     
    //pay.jpに送るため情報を暗号化(トークン化)
    Payjp.createToken(card, function(status, response) {
         
        //statusが200の場合はトークン化に問題無し。決済へ進む
        if (status == 200) {
             
            //トークンを取得
            var card_token = response.id;
             
            //ajaxで決済処理用のphpにトークンを送信
            $.ajax({
                type: 'POST',
                dataType:'json',
                url:'functions/pay.php',
                data:{
                    card_token:card_token,
                },
                success:function(data) {
                    if(data){
                        //決済処理に問題があった場合、エラーを表示。
                        $(".cardmiss").append(""+data+"");
                    }else{
                        //決済処理が成功した場合、決済顔料画面へ。
                        location.href = "success.php";
                    };
                },
                error:function(XMLHttpRequest, textStatus, errorThrown) {
                    alert(errorThrown);
                }
            });
             
        } else {
             
            //フォームの時点で入力内容にエラーがあった場合。
            var error_messege = response.error.message;
            $(".cardmiss").append("入力内容にエラーがあるようです。再度、ご確認の上、ご登録をお願い致します。");
             
        };
    });
});
</script>

では、それぞれ細かくみていきましょう。

まず、フォームはHTMLで普段使っているものとほぼ同じです。
もちろん今回の例に限ったことではなく、フォームの作り方は自由です。

装飾も、CSSで自由にしてみてください。

<!--エラーがあった時はこちらに表示-->
<div class="missArea cardmiss"></div>
 
<!--以下、クレジットカード入力フォーム-->
<dl>
    <dt>カード番号</dt>
    <dd><input type="text" name="card_number" placeholder="カード番号)4242424242424242"></dd>
    <dt>有効期限</dt>
    <dd><input type="number" name="card_exp_month" placeholder="月)12">/<input type="number" name="card_exp_year" placeholder="年)21"></dd>
    <dt>カード確認コード(CVC)</dt>
    <dd><input type="text" name="card_cvc" placeholder="CVC)123"></dd>
    <dt>お名前</dt>
    <dd><input type="text" name="card_name" placeholder="名前)TARO SASAKI"></dd>
</dl>
<button class="publishBtn" >登録する</button>
<div style="padding-top:40px;">
<div>テスト用カード番号は<a href="https://pay.jp/docs/testcard" target="_blank">こちら</a>から<br>
カード番号以外の情報は、グレーの文字通りご入力ください。<br>
実際に決済はされません。</div>
</div>

これでフォームの準備は完了です。

次は一度、pay.jpの管理画面に戻り、サイドバーのAPIタブを押してください。
すると、下記のようなページが表示されると思います。

大事なのはAPIキーの情報です。
公開鍵と秘密鍵、という項目があるかとおもいます。

公開鍵と秘密鍵は、pay.jpにデータを送るための鍵です。
2つなければpay.jpにアクセスすることはできません。

HTMLのソースコード上は公開鍵を使いますので、公開鍵を控えておいてください。
テストモードの場合はテスト用を使い、本番では本番用を使います。

次はbuttonを押した後の処理をJavaScriptで書いていきます。

<script>
 
//公開鍵を設定
Payjp.setPublicKey("pk_test_db09216e46f8eae174cc817c");
 
//publishBtn(buttonを押した時に以下の処理スタート)
$(".publishBtn").click(function(){
     
    //一旦エラーエリアの内容をリセット(何も無い場合は変化なし)。
    $(".missArea").empty();
     
    //有効期限の年を取得
    //年を4桁すべて入力してもらうと手間がかります。
    //よって、2019の場合、19だけ入力してもらい、20はここで付け足す。
    var cvcdata = "20" + document.querySelector('input[name="card_exp_year"]').value;
     
    //他の項目もすべて取得
    var card = {
        number: document.querySelector('input[name="card_number"]').value,
        cvc: document.querySelector('input[name="card_cvc"]').value,
        exp_month: document.querySelector('input[name="card_exp_month"]').value,
        exp_year: cvcdata,
        name: document.querySelector('input[name="card_name"]').value
    };
     
    //pay.jpに送るため情報を暗号化(トークン化)
    Payjp.createToken(card, function(status, response) {
         
        //statusが200の場合はトークン化に問題無し。決済へ進む
        if (status == 200) {
             
            //トークンを取得
            var card_token = response.id;
             
            //ajaxで決済処理用のphpにトークンを送信
            $.ajax({
                type: 'POST',
                dataType:'json',
                url:'functions/pay.php',
                data:{
                    card_token:card_token,
                },
                success:function(data) {
                    if(data){
                        //決済処理に問題があった場合、エラーを表示。
                        $(".cardmiss").append(""+data+"");
                    }else{
                        //決済処理が成功した場合、決済顔料画面へ。
                        location.href = "success.php";
                    };
                },
                error:function(XMLHttpRequest, textStatus, errorThrown) {
                    alert(errorThrown);
                }
            });
             
        } else {
             
            //フォームの時点で入力内容にエラーがあった場合。
            var error_messege = response.error.message;
            $(".cardmiss").append("入力内容にエラーがあるようです。再度、ご確認の上、ご登録をお願い致します。");
             
        };
    });
});
</script>

上記のような形で処理を書いていきます。

まず、カード情報をすべて、トークンという暗号に変えて扱っています。

トークンに変えることで、素のカード情報を扱う必要がなくなります。
よって、セキュリティとして安全に処理できるわけです。

カード情報をトークンに変更せずに、決済することは出来ません。

pay.php

次にphpにおける決済処理を見ていきましょう。

<?php
     
    //データベースと接続するファイルを読み込みます。
    require_once "db.php";
     
    //秘密鍵を設定するファイルを読み込みます。
    require_once 'secret.php';
     
    //アップロードしたpay.jpのライブラリから必要なファイル(init.php)を読み込みます。
    require_once 'payjp-php-master/init.php';
     
    //JavaScriptからポストされたトークンを受け取ります。
    $card_token = htmlspecialchars($_POST['card_token'], ENT_QUOTES);
     
    //管理画面で決めた、今回課金するプランのidを指定します。
    $plan_data = "s0001";
     
    //ユーザーのメールアドレスを取得します。
    //今回はユーザーのメールアドレスをセッションで保持、セッションから取得。
    $mail = $_SESSION['mail'];
     
    try {
         
        //pay.jpの管理画面に顧客データを作成します。
        Payjp\Payjp::setApiKey($secret);
        $result = Payjp\Customer::create(
            array(
                "email" => $mail,
                "card" => $card_token,
            )
        );
         
        //作成された顧客idを取得します。
        $resultid = $result['id'];
             
        //ユーザーをプランに加入する処理を実行。
        Payjp\Payjp::setApiKey($secret);
        $resultsub = Payjp\Subscription::create(
            array(
                "customer" => $resultid,
                "plan" => $plan_data
            )
        );
         
        //プランとユーザーを紐付けるidを取得
        $resultsubid = $resultsub['id'];
         
        //データベースにあるユーザーデータに、決済関連のidをまとめて保存
        $userplandata = mysqli_select_db($mysqli,'userdata');
        //今回はplanrank=プラン(s0001) planid=プラン決済id payid=顧客id registrationtime=決済時間を登録しています。
        //データベースにも予めカラムを作っておいてください。
        $userplandata = mysqli_query($mysqli,"UPDATE userdata SET planrank='$plan_data',planid='$resultsubid',payid='$resultid',registrationtime='$today' where mail = '$mail'");
         
    } catch (Exception $e) {
         
        //もしエラーがあった場合はエラーメッセージを返します。
        $miss = $getmessage = $e->getMessage();
     
    };
     
    //最後にjsonでエラーがあった場合のエラーメッセージを返します。
    header('Content-type: application/json');
    echo json_encode( $miss );
?>

冒頭で、秘密鍵を格納した別ファイルを呼び出しています。
決済画面には、必ず必要なファイルになります。
別ファイル化して、いつでも呼び出せるようにしておきましょう。

secret.php

中身は非常にシンプルで、秘密鍵が置いてあるだけです。

<?php
    //秘密鍵を入れます。
    $secret = 'ここに秘密鍵を貼り付けます。';
?>

秘密鍵は決済の要となる鍵です。
まさに家の鍵そのものと言っても良いでしょう。
人には見せないよう、厳重に管理しておいてください。

success.php

決済完了画面は、普通のhtmlです。
特にこれと言って、追記すべき点はありません。

決済が完了しました。<br>
<a href="index.php">決済画面へ戻る</a>

通常、このあとはログインしてダッシュボードなどに移動します。
ご自身のサービスでどのようなフローをたどるか、考えてみましょう。

さて、これで、一通り課金までの処理は完了です。

最初は上手く動かないかもしれませんが、ゆっくり順番にやってみてください。
テストモードで試せば、無駄に課金されるリスクもありません。

定額課金を解除する場合は?

頑張って作ったサービスですが、残念ながら登録を解除されることもあります。

その時は、データベースのデータと一緒に、pay.jpのデータも削除しなければなりません。
どうやって、pay.jpのデータを消すのか、下記のソースコードでご説明いたします。

<?php
    //$_SESSION["payid"]の中にpay.jpの顧客番号を入れておく
    //顧客idは登録の時にユーザーデータに保存済。
    $planiddata = $_SESSION["payid"];
 
    //もし顧客番号があった場合pay.jpにアクセス。
    if(!empty($_SESSION["payid"])){
 
        try {
            //秘密鍵を呼び出し
            Payjp\Payjp::setApiKey("ここに秘密鍵が入ります。");
             
            //顧客データをpay.jpから呼び出し。
            $result2 = Payjp\Customer::retrieve($planiddata);
             
            //プランと顧客を紐付けるidを取得。
            $user_pay_id = $result2->subscriptions -> data[0]->id;
 
            //上記idに紐付けられた課金をキャンセル
            $fin = Payjp\Subscription::retrieve($user_pay_id);
            $fin->cancel();  
 
        } catch (Exception $e) {
            //エラーがある場合はこちらに処理を書く。
        };
 
    };
?>

上記で、定額課金がキャンセルとなります。
あとは、自分のデータベースからユーザーを削除すれば解除完了です。

ただ、上記の場合、pay.jp内に顧客データが残っています。
もし顧客データを消す場合はpay.jpのリファレンスを参考に削除してみてください。

以上でpay.jpの基本的な使い方は完了となります。

WEBサービスづくり学習「STARTOUT」はじめました!

自分の「WEBサービス」を作りながら「制作技術」を習得しよう!

STARTOUTとは、WEB制作×サービス作りのオンライン実践学習サービスです。ゼロからWEBサービスを作り、収益化しながら技術を習得してみましょう!

 詳細はこちら
  • このエントリーをはてなブックマークに追加

ウエヤマ ショウタ

BASE.91とWORKROOM@セブ立ちあげました。誰もが「独学」でプロになれる仕組みを作る。現役WEBデザイナーでエンジニア。起業好き。IT留学事業を売却後、シリコンバレーで起業を学び、起業家&クリエイター育成活動開始。経験をシェアしてます。独学で収益化したスキル : デザイン、プログラミング、起業、マーケ、英語

ウエヤマ ショウタ

BASE.91とWORKROOM@セブ立ちあげました。誰もが「独学」でプロになれる仕組みを作る。現役WEBデザイナーでエンジニア。起業好き。IT留学事業を売却後、シリコンバレーで起業を学び、起業家&クリエイター育成活動開始。経験をシェアしてます。独学で収益化したスキル : デザイン、プログラミング、起業、マーケ、英語

WEBサービスづくり学習「STARTOUT」はじめました!

自分の「WEBサービス」を作りながら「制作技術」を習得しよう!

STARTOUTとは、WEB制作×サービス作りのオンライン実践学習サービスです。ゼロからWEBサービスを作り、収益化しながら技術を習得してみましょう!

 詳しくはこちら