pay.jp 3dセキュア導入

# ポップアップ型だとカゴ落ちしそう

"ポップアップをブロックしているブラウザだとエラーになる"
とのこと。まずいので 3Dセキュアを リダイレクト型で使う

まず、payjpのphpライブラリを最新にしないと`$token->tdsFinish();`が動かないので注意する。

# 流れ

kigengire2 で 決済
↓
tokenCheck で tokenのチェック
↓
comp で 3D認証を完了させる

# まずは、payjp-phpを最新版に


```vim composer.json
"require": {
        "payjp/payjp-php": "1.*",
    },
```

```
composer update payjp/payjp-php
```

1.7にアップデートされる

# 注意点

・サーバー側、view側のそれぞれAPIKEYが必要。テストと、本番用を揃えること

じゃないとtokenエラーになる


```kigengire2.ctp
<script type="text/javascript">
    function onCreatedToken(response) {
        console.log(response);
        document.querySelector('#created-token').textContent = response.id;
    }
</script>

<h1>支払いフォーム例</h1>

<p>
    payjp_test_key: <?=PAY_JP_TEST_KEY;?><br>
</p>

<form action="/payjp3ds/tokenCheck" method="post">
    <p>おにぎり 100円</p>

    <div style="display: flex; gap: 1rem; align-items: center;">
        <div>
            <script
                    type="text/javascript"
                    src="https://checkout.pay.jp"
                    class="payjp-button"
                    data-payjp-key="<?php echo htmlspecialchars(PAY_JP_TEST_KEY); // `pk_` から始まる公開鍵を設定してください。 ?>"
                    data-payjp-three-d-secure="true"
                    data-payjp-three-d-secure-workflow="redirect"
                    data-payjp-extra-attribute-email="<?=u('email');?>"
                    data-payjp-partial="true"
                    data-payjp-on-created="onCreatedToken"
            ></script>
        </div>
        <div>
            <small>※ <?php echo htmlspecialchars('<input type="hidden" name="payjp-token">'); ?> の value に token が自動的にセットされます。</small><br />
            <small>※ 生成されたトークン: <span id="created-token"></span></small>
        </div>
    </div>
    <br />
    <input type="hidden" name="csrf_token" value="<?php echo bin2hex(random_bytes(16)); ?>" />
    <button type="submit">支払う</button>
</form>
```

```tokenCheck.php

    public function tokenCheck()
    {
        $secretKey = PAY_JP_TEST_SECRET;
        $publicKey = PAY_JP_TEST_KEY;
        $back_url = 'https://your.com/payjp3ds/comp/';

// トークンを取得
        $payjpToken = $_POST['payjp-token'] ?? '';

// セッションに保存
        $_SESSION['tds_input_data'] = [
            'payjp_token' => $payjpToken,
        ];

        $header = [
            'alg' => 'HS256',
            'typ' => 'JWT'
        ];

        $payload = [
            'url' => $back_url,
        ];

        function base64url_encode($data) {
            // 通常のBase64エンコード
            $b64 = base64_encode($data);
            // '+' → '-' , '/' → '_' , '=' を削除
            return rtrim(strtr($b64, '+/', '-_'), '=');
        }

        $base64Header  = base64url_encode(json_encode($header));
        $base64Payload = base64url_encode(json_encode($payload));
        $signature = hash_hmac(
            'sha256',
            $base64Header . '.' . $base64Payload,
            $secretKey,
            true
        );
        $base64Signature = base64url_encode($signature);
        $jws = $base64Header . '.' . $base64Payload . '.' . $base64Signature;
        header("Location: https://api.pay.jp/v1/tds/{$payjpToken}/start?publickey={$publicKey}&back_url={$jws}");

        die();
    }

```

```comp.php

    public function comp()
    {
        //3Dセキュア認証
        \Payjp\Payjp::setApiKey(PAY_JP_TEST_SECRET);
        $token = \Payjp\Token::retrieve($_SESSION['tds_input_data']['payjp_token']);
        // 3Dセキュアフロー完了します。忘れがちなので注意してください。
        $token->tdsFinish();

        pr("3Dセキュア認証完了しました。");

        die();
    }

```

これで3D認証まではOK。
次は支払いとかやろう。


# 顧客化

```hoge.php
    public function _addCustomer()
    {
        //3Dセキュア認証
        \Payjp\Payjp::setApiKey($this->config['secret']);
        $token = \Payjp\Token::retrieve($_SESSION['tds_input_data']['payjp_token']);
        // 3Dセキュアフロー完了します。忘れがちなので注意してください。
        $token->tdsFinish();

        if($token->used == false){
            $customer = \Payjp\Customer::create(array(
                "card" => $token->id
            ));
        } else {
            echo "トークンは使用済みです";
            die();
        }

        return $customer->id;
    }

```