3Dセキュア実装サンプル・デモ

実際にお手元で動作するサンプルをご用意しております。

掲載箇所 リンク
GitHub https://github.com/payjp/payjp-php-tds-sample
ドキュメント 3Dセキュアフロー実装サンプル・デモ

使い方

詳細はリポジトリ内の README.md をご確認ください。
上記の GitHub リンクからソースコードをフォークやクローン、ダウンロードなどを利用してお手元に用意してください。

動作環境

動作環境は Docker を利用しています。
お手元に用意がない場合は、公式 HP の指示に従ってインストールを行ってください。

動作環境の構築

リポジトリをお手元に用意したら、以下のコマンドを実行してください。

$ docker compose build
$ docker compose run --rm app composer install

上記で必要なイメージの作成や、動作に不可欠なライブラリのインストールが行われます。

サーバーの起動

以下のコマンドを実行してください。1 2

$ PAYJP_PUBLIC_KEY=お手持ちの公開鍵 PAYJP_SECRET_KEY=お手持ちの秘密鍵 docker compose up -d

お手持ちの公開鍵と秘密鍵には PAY.JP の管理画面のAPI 設定ページに記載がありますので、そちらをご確認ください。
設定する鍵はテスト用のものを利用することを強く推奨いたします。

サンプルの動作確認

サーバーが起動したら、ブラウザで以下の URL にアクセスしてください。

http://localhost

この動作環境では、3Dセキュアの実装サンプルが動作し、指定した API キーに従って実際に決済を行うことができます。
テスト用の API キーを指定することで、実際の決済は行われませんので、安心してご利用ください。
作成された決済の状態を管理画面で確認することが可能です。

サブウィンドウ型 - トークン作成時の3Dセキュア

フォームの実装サンプル

https://github.com/payjp/payjp-php-tds-sample/tree/main/src/sub-window/index.php から一部抜粋。

<body>

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

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

<form action="/sub-window/create-charge.php" method="post">
    <p>おにぎり 100円</p>

    <div style="display: flex; gap: 1rem; align-items: center;">
        <div>
            <script
                type="text/javascript"
                src="https://checkout.pay.jp/prerelease"
                class="payjp-button"
                data-payjp-key="<?php echo htmlspecialchars($_ENV['PAYJP_PUBLIC_KEY'] ?? ''); // `pk_` から始まる公開鍵を設定してください。 ?>"
                data-payjp-three-d-secure="true"
                data-payjp-three-d-secure-workflow="subwindow"
                data-payjp-extra-attribute-email
                data-payjp-extra-attribute-phone
                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 htmlspecialchars($csrfToken); ?>" />
    <button type="submit">支払う</button>
</form>
</body>

この例では入力値やトークンが /sub-window/create-charge.php に POST されることを想定しています。

バックエンド ( PHP ) の実装サンプル

サブウィンドウ型で作成されたトークンはそのまま支払いや顧客カードとして登録しても構いません。

https://github.com/payjp/payjp-php-tds-sample/blob/main/src/sub-window/create-charge.php から一部抜粋。

<?php
declare(strict_types=1);

// 必要なパッケージを読み込んでください。実際の相対パスはお手元の環境によって変わります。
require_once __DIR__ . '/../vendor/autoload.php';

// method の形式や csrf token の検証などを行ってください。このサンプルでは本題と関係ないため省略します。

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

// トークンを使って支払いを行います。
Payjp\Payjp::$apiKey = $_ENV['PAYJP_SECRET_KEY'] ?? ''; // `sk_` から始まる秘密鍵を設定してください。
$charge = Payjp\Charge::create([
    'card' => $payjpToken,
    'amount' => 100,
    'currency' => 'jpy',
]);

// 支払い後に必要な処理を行ってください。

echo '支払いが完了しました。<br />';
echo $charge->id . '<br />';
echo $charge->amount . '<br />';

リダイレクト型 - トークン作成時の3Dセキュア

フォームの実装サンプル

https://github.com/payjp/payjp-php-tds-sample/tree/main/src/redirect/index.php から一部抜粋。

<body>

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

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

<form action="/redirect/redirect-to-tds-page.php" method="post">
    <p>おにぎり 100円</p>

    <div style="display: flex; gap: 1rem; align-items: center;">
        <div>
            <script
                    type="text/javascript"
                    src="https://checkout.pay.jp/prerelease"
                    class="payjp-button"
                    data-payjp-key="<?php echo htmlspecialchars($_ENV['PAYJP_PUBLIC_KEY'] ?? ''); // `pk_` から始まる公開鍵を設定してください。 ?>"
                    data-payjp-three-d-secure="true"
                    data-payjp-three-d-secure-workflow="redirect"
                    data-payjp-extra-attribute-email
                    data-payjp-extra-attribute-phone
                    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 htmlspecialchars($csrfToken); ?>" />
    <button type="submit">支払う</button>
</form>
<br />
<a href="/">戻る</a>
</body>

バックエンド ( PHP ) の実装サンプル

フォームでトークンを生成した後、リダイレクト型の 3D セキュアを開始します。
エンドユーザーを3Dセキュア完了エンドポイントへ誘導するために下記のような処理を行います。
このとき、生成したトークンはセッションなどに保存し、リダイレクト後に再度利用できるようにしておくと便利です。

https://github.com/payjp/payjp-php-tds-sample/blob/main/src/redirect/redirect-to-tds-page.php から一部抜粋。

<?php
declare(strict_types=1);

// 必要なパッケージを読み込んでください。実際の相対パスはお手元の環境によって変わります。
require_once __DIR__ . '/../vendor/autoload.php';

// method の形式や csrf token の検証などを行ってください。このサンプルでは本題と関係ないため省略します。

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

// 入力内容をセッションに保存しておきます。
$_SESSION['tds_input_data'] = [
    'payjp_token' => $payjpToken,
];

// 戻り先 URL ( `back_url` ) を設定します。ここでは firebase/php-jwt を使用しています。
// https://pay.jp/docs/api/#3d%E3%82%BB%E3%82%AD%E3%83%A5%E3%82%A2%E9%96%8B%E5%A7%8B
$jws = Firebase\JWT\JWT::encode(
    [
        'url' => 'http://localhost/redirect/callback.php',
    ],
    $_ENV['PAYJP_SECRET_KEY'] ?? '',
    'HS256'
);

header("Location: https://api.pay.jp/v1/tds/$payjpToken/start?publickey={$_ENV['PAYJP_PUBLIC_KEY']}&back_url=$jws");
exit;

カード会社の 3Dセキュア認証が完了した後、JWS で指定した URL にリダイレクトされます。
このリダイレクト先で、3Dセキュア認証結果のハンドリング・完了処理を行ってください。
具体的には下記のような実装を行います。

https://github.com/payjp/payjp-php-tds-sample/blob/main/src/redirect/callback.php から一部抜粋。

<?php
declare(strict_types=1);

// 必要なパッケージを読み込んでください。実際の相対パスはお手元の環境によって変わります。
require_once __DIR__ . '/../vendor/autoload.php';

// method の形式や csrf token の検証などを行ってください。このサンプルでは本題と関係ないため省略します。

// セッションから保持していたトークンを再取得します。
$payjpToken = $_SESSION['tds_input_data']['payjp_token'] ?? '';
unset($_SESSION['tds_input_data']);

// 3D セキュア認証が完了した後に、3Dセキュアフローを完了させます。
Payjp\Payjp::$apiKey = $_ENV['PAYJP_SECRET_KEY'] ?? ''; // `sk_` から始まる秘密鍵を設定してください。
$token = Payjp\Token::retrieve($payjpToken);

// この時点での `$token->card->three_d_secure_status` の状態を見たりして処理を判断することもできます。

// 3Dセキュアフロー完了します。忘れがちなので注意してください。
$token->tdsFinish();

// 3Dセキュアフローが完了したトークンを用いて、支払いを行います。
$charge = Payjp\Charge::create([
    'card' => $token->id,
    'amount' => 100,
    'currency' => 'jpy',
]);

// 支払い後に必要な処理を行ってください。

echo '支払いが完了しました。<br />';
echo $charge->id . '<br />';
echo $charge->amount . '<br />';

  1. ※環境変数については .env.example を .env にコピーしてご利用いただくことも可能です。 ↩︎

  2. ※動作環境で 80 番を利用しています。ポートに関する起動エラーが出た場合は他の処理で 80 番ポートが使われていないか確認してください。 ↩︎