1
6

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

cakephp3 で pay.jp クレジットカード決済を導入

Last updated at Posted at 2017-05-23

cakephp3 で pay.jp クレジットカード決済を導入

webhookは
初回 subscription.created
2回目以降 subscription.renewed
が返ってくる

まずは composer で
https://github.com/payjp/payjp-php
インストール

改訂版

まずはテーブルを作成


CREATE TABLE `payjps` (
  `id` bigint(20) NOT NULL COMMENT 'ユーザーIDと同一',
  `customer_id` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT 'カスタマーID',
  `teiki_id` varchar(255) CHARACTER SET utf8 DEFAULT NULL COMMENT '定期課金ID',
  `created` datetime NOT NULL DEFAULT current_timestamp(),
  `plan_id` int(11) DEFAULT 0 COMMENT ''
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

下準備

u('id')//ユーザーのID

##管理画面より
・PAYJPのKEYとSECRETを取得し PayjpsController config に設定
https://yourdomain.com/payjps/webhook/ の webhook を作成
・継続課金の場合、10000,10001のIDのプランを先に作っておく

共通

・初回のみクレジットカード決済を入力させる
・次回以降は入力の必要なし
・処理は webhook を使わず遷移先で行う

・都度課金

・サイト特有の処理は _tudoSyori() にて行う

・定額課金

・サイト特有の処理は _keizokuSyori() にて行う
定額プランは どれか 1つしか入れないものとする。
継続決済の2回目以降の自動処理の場合のみwebhookを使う。

PayjpsController.php


<?php

namespace App\Controller;
use App\Controller\AppController;

use Cake\Controller\Controller;
use Cake\Event\Event;
use Cake\ORM\TableRegistry;
use Cake\Cache\Cache;


use Cake\Mailer\Email;

use Cake\Core\Configure;
use Cake\ORM\Query;

use Cake\Utility\Hash;

use PDO;
use Cake\Datasource\ConnectionManager;
use Cake\I18n\Time;

class PayjpsController extends AppController
{

    public $config = [
        'secret' => PAY_JP_SECRET,//your
        'key' => PAY_JP_KEY//your
    ];


// あなたのコントローラで下記のように呼び出す
    public function initialize()
    {
        parent::initialize();
    }

    public function reset()
    {

        $this->Payjps->deleteAll([
            'id' => u('id'),
        ]);

        return $this->redirect($this->referer());
    }


    public function test()
    {


        $res = $this->Payjps->find()
            ->where([
                'id' => u('id'),
            ])
            ->first();




        //そのまま送る
        
        $config = $this->config;


        $this->set(
            compact(
                'res',
                'config'
            )
        );
        
    }

//  都度課金があった場合どのような処理をするか
    public function _tudoSyori()
    {
//        $_POST['user_id']
//        pd($_POST);



        return number_format($_POST['amount'])." 円の支払い完了";


    }

    //  継続課金があった場合どのような処理をするか
    //  2回目以降の時は webhook からこれを動かす
    public function _keizokuSyori()
    {
        $customer_id = "";
        if(!empty($_POST['customer_id'])){
            $customer_id = $_POST['customer_id'];
        }

        if(!empty($_POST['data']['customer'])){
            $customer_id = $_POST['data']['customer'];
        }

        $res = true;
        if(!empty($_POST['plan_id'])){
            $res = "プラン : ".$_POST['plan_id']." に入りました";
        }

        return $res;

    }


    public function tudokakin()
    {

//        初回決済
        if (!empty($_POST['customer_id'])) {

            //            顧客化されている場合
            $customer_id = $_POST['customer_id'];

        } else {

            //顧客化
            $customer_id = $this->_addCustomer();

            $data = [
                'id' => $_POST['user_id'],
                'customer_id' => $customer_id
            ];

            $valid = $this->Payjps->newEntity($data);
            $this->Payjps->save($valid);

        }

        \Payjp\Payjp::setApiKey($this->config['secret']);


        try {
            $res = \Payjp\Charge::create(
                [
                    "customer" => $customer_id,
                    "amount" => $_POST['amount'],//支払額
                    "currency" => 'jpy'
                ]
            );
            if (isset($res['error'])) {
                throw new Exception();
            }
        } catch (Exception $e) {
            // カードが拒否された場合
            echo $res['error']['message'];
            exit;
        }


        $syori = $this->_tudoSyori();


        $this->Flash->set($syori);
        return $this->redirect($this->referer());

        $this->autoRender = false;
    }


    public function keizoku()
    {

        if (!empty($_POST['customer_id'])) {
            //            顧客化されている場合
            $customer_id = $_POST['customer_id'];
        } else {
            //        初回決済
            //顧客化
            $customer_id = $this->_addCustomer();

            $data = [
                'id' => $_POST['user_id'],
                'customer_id' => $customer_id
            ];

            $valid = $this->Payjps->newEntity($data);
            $this->Payjps->save($valid);

        }



        //トークンで支払う
        \Payjp\Payjp::setApiKey($this->config['secret']);

        try {
            $res = \Payjp\Subscription::create(
                [
                    "customer" => $customer_id,
                    "plan" => $_POST['plan_id']//管理画面で先にプランを作っておく
                ]
            );
            if (isset($res['error'])) {
                throw new Exception();
            }
        } catch (Exception $e) {
            // カードが拒否された場合
            echo $res['error']['message'];
            exit;
        }





//        ユーザー情報を一旦保存
        $data = [
            'id' => $_POST['user_id'],
            'customer_id' => $customer_id,
            'teiki_id' => $res->id,
            'plan_id' => $_POST['plan_id'],
        ];

        $valid = $this->Payjps->newEntity($data);
        $this->Payjps->save($valid);

        $syori = $this->_keizokuSyori();

        $this->Flash->set($syori);
        return $this->redirect($this->referer());

        $this->autoRender = false;
    }

    public function _addCustomer()
    {
//        フォームから送られてくる。
//         [payjp-token] => tok_ef86a1b8303ddbf08c42cc6cba06

        //トークンで支払う
        \Payjp\Payjp::setApiKey($this->config['secret']);

        $customer = \Payjp\Customer::create(array(
            "card" => $_POST['payjp-token']
        ));

        return $customer->id;
    }



    //    退会処理
    public function taikai()
    {
        \Payjp\Payjp::setApiKey($this->config['secret']);
        $su = \Payjp\Subscription::retrieve($_GET['teiki_id']);
        $su->delete();


        $_SESSION['Payjp']['plan_id'] = 0;


        $this->Payjps->updateAll(
            [
                'plan_id' => 0,
                'teiki_id' => ''
            ],//にする
            [
                'teiki_id' => $_GET['teiki_id']
            ]//これを
        ); // 条件


        $this->Flash->set("解約しました");
        return $this->redirect($this->referer());

        $this->autoRender = false;
    }

    public function webhook()
    {

        if(!empty($_POST['type'])){

//            初回課金時
            if($_POST['type'] == "subscription.created"){

            }

//            2回目以降課金時
            if($_POST['type'] == "subscription.renewed"){
                $this->_keizokuSyori();
            }



        }

        $this->autoRender = false;
    }






}


payjps/test.ctp



<?php

//同じページに複数フォームを設置できないので
//0 は 継続決済 , 1 は都度課金とする
$kakin_mode = 0;

?>

<h1>payjpのテスト</h1>


<? if ($kakin_mode): ?>

    <h2>都度決済</h2>


    <form action="/payjps/tudokakin/" method="post">

        <input type="radio" name="amount" value="1000"> 1,000 円の都度課金
        <input type="radio" name="amount" value="5000" checked> 5,000 円の都度課金
        <input type="radio" name="amount" value="10000"> 10,000 円の都度課金

        <input type="hidden" id="user_id" name="user_id" value="<?=u('id');?>">

        <? if (!empty($res->customer_id)): ?>
        <input type="hidden" id="customer_id" name="customer_id" value="<?=$res->customer_id;?>">
            <div>
                <button type="submit">2回目以降の決済</button>
            </div>
        <? else: ?>
        <input type="hidden" id="customer_id" name="customer_id" value="">
            <script src="https://checkout.pay.jp/" class="payjp-button" data-key="<?=$config['key'];?>" data-text="都度課金 (単発)"></script>
        <? endif; ?>

    </form>




<? else: ?>


    <h2>継続決済</h2>

    <? if (!empty($res->teiki_id)): ?>

        <p>解約フォームへ</p>
        <a href="/payjps/taikai/?teiki_id=<?=$res->teiki_id;?>">現在有料プランに入っています。退会しますか?</a><br>


    <? else: ?>

        <form action="/payjps/keizoku/" method="post">
            <input type="radio" name="plan_id" value="10000" checked> 30日掲載プラン
            <input type="radio" name="plan_id" value="10001"> 365日掲載プラン
            <input type="hidden" id="user_id" name="user_id" value="<?=u('id');?>">


            <? if (!empty($res->customer_id)): ?>
                <input type="hidden" id="customer_id" name="customer_id" value="<?=$res->customer_id;?>">
                <div>
                    <button type="submit">定額プラン申し込み (2回目以降の決済)</button>
                </div>
            <? else: ?>
            <input type="hidden" id="customer_id" name="customer_id" value="">
                <script src="https://checkout.pay.jp/" class="payjp-button" data-key="<?=$config['key'];?>" data-text="定期プラン申し込み"></script>

            <? endif; ?>

        </form>



    <? endif; ?>





<? endif; ?>



<? if (!empty($res->customer_id)): ?>
    <? if (!empty($res->teiki_id)): ?>
        <? else: ?>
        <a href="/payjps/reset/">カードの有効期限などが切れ場合リセットする</a>
    <? endif; ?>
<? endif; ?>




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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?