勉強したことのメモ

Webエンジニア / プログラマが勉強したことのメモ。

【2025年版】PHPとStripeを用いてサイト内にクレカ決済及びキャンセル機能を実装する方法

   2025/06/15  PHP

5年ほど前にPHPとStripeを用いたクレカ決済機能をテスト実装したが最近試したところ動作しなかった。調べてみるとStripe Checkoutのバージョンが新しくなったらしく、そちらが原因っぽい。そのため現行のバージョンでのクレカ決済及びキャンセル機能の実装方法をメモ。尚、2025年6月時点で動作確認済み。

 

やりたいこと

やりたいことは以下の二点になる。

  • サブスクは無しで1回限りのクレジットカード決済の実装
  • クレジットカード決済後のキャンセル(返金)処理の実装

 

下準備

Stripeのアカウント作成

Stripe公式サイトでアカウントを作成しておく。

登録するとテスト用の「公開可能キー」「シークレットキー」が発行されるのでメモっておく。

ライブラリのインストール

サーバにSSH接続し適当なディレクトリに移動後、以下でライブラリをインストールする。

composer require stripe/stripe-php

ライブラリの要件はPHP5.6.0以降とのこと。尚、当方の環境はPHP8.3.3になる。

テスト用クレジットカード

テスト用のクレジットカードはこちらのページより確認できる。有効期限は「12 / 34」等の未来日付、セキュリティコードは「123」等の任意の3桁の数値となる。

 

クレジットカード決済機能の実装方法

基本的には公式サイトのドキュメントと同じソースコードになる。

secrets.php(シークレットキーの保存場所)

シークレットキーはメモったものに書き換えること。

<?php
$stripeSecretKey = 'sk_test_xxxxx';

checkout.php(決済処理)

「$domain(サイトのURL)」「name(商品名)」「unit_amount(金額)」部分は適宜変更すること。

決済成功時、MySQLにデータを登録したいような場合はsuccess_urlに遷移後、行う必要あり。そのため何らかのパラメータを送る場合はsession_idの後ろにGETパラメータを付与する形になりそう。その際はセキュリティ面を考慮し、トークン等を引き回した方が良さそう。

<?php
require_once './vendor/autoload.php';
require_once 'secrets.php';

\Stripe\Stripe::setApiKey($stripeSecretKey);
header('Content-Type: application/json');

$domain = 'https://test.com/checkout';

$checkout_session = \Stripe\Checkout\Session::create([
    'payment_method_types' => ['card'],
    'line_items' => [[
        'price_data' => [
            'currency' => 'JPY',
            'product_data' => [
                'name' => '商品名',
            ],
            'unit_amount' => 200,
        ],
        'quantity' => 1,
    ]],
    'mode' => 'payment',
    'success_url' => $domain . '/success.php?session_id={CHECKOUT_SESSION_ID}',
    'cancel_url' => $domain . '/error.php?session_id={CHECKOUT_SESSION_ID}',
]);

header("HTTP/1.1 303 See Other");
header("Location: " . $checkout_session->url);

index.php(決済用フォームの設置ページ)

form内に「input type="hidden"」を設置し、送信後に上記のcheckout.phpで受け取ることも可能。

<!DOCTYPE html>
<html lang="ja">
<html>
    <head>
        <script src="https://js.stripe.com/v3/"></script>
    </head>
    <body>
        <section>
            <form action="./checkout.php" method="POST">
                <button type="submit" id="checkout-button">購入</button>
            </form>
        </section>
    </body>
</html>

error.php(決済失敗時に表示されるページ)

こちらは以下のようなHTMLべた書きでも問題ない。

<!DOCTYPE html>
<html lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
</head>
<body>
エラー
</body>
</html>

success.php(決済成功時に表示されるページ)

<?php
require_once './vendor/autoload.php';
require_once 'secrets.php';

$stripe = new \Stripe\StripeClient($stripeSecretKey);

$session = $stripe->checkout->sessions->retrieve($_GET['session_id'], []);
var_dump($session->payment_intent);

var_dumpでは以下のような値が出力される筈。

こちらpayment_intentとなり決済キャンセル時に必要なIDになるのでMySQL等に保存した方が良い

string(27) "pi_xxxxxxxxxx"

 

決済キャンセル(返金)処理の実装方法

こちらも基本的には公式サイトのドキュメントと同じソースコードになる。

cancel.php

payment_intent(pi_xxxxxxxxxx部分)の部分は前述のsuccess.phpで取得したIDに変更すること。

<?php
require_once './vendor/autoload.php';
require_once 'secrets.php';

$stripe = new \Stripe\StripeClient($stripeSecretKey);

$refund = $stripe->refunds->create(['payment_intent' => 'pi_xxxxxxxxxx']);

var_dump($refund);

var_dumpでは以下のような値が出力される筈。そのため正常に処理されたかのチェックは「$refund->status」が「succeeded」になっているかどうかを見るのが良さそう。

object(Stripe\Refund)#16 (15){
    ["id"]=> string(27) "re_xxxxxxxxxx" 
    ["object"]=> string(6) "refund" 
    ["amount"]=> int(200) 
    ["balance_transaction"]=> string(28) "txn_xxxxxxxxxx" 
    ["charge"]=> string(27) "ch_xxxxxxxxxx" 
    ["created"]=> int(1749486797) 
    ["currency"]=> string(3) "jpy" 
    ["destination_details"]=> object(Stripe\StripeObject)#21 (2) { 
        ["card"]=> object(Stripe\StripeObject)#26 (3) { 
            ["reference_status"]=> string(7) "pending" 
            ["reference_type"]=> string(25) "acquirer_reference_number" 
            ["type"]=> string(6) "refund" 
        } 
        ["type"]=> string(4) "card" 
    } 
    ["metadata"]=> object(Stripe\StripeObject)#22 (0) { } 
    ["payment_intent"]=> string(27) "pi_xxxxxxxxxx" 
    ["reason"]=> NULL 
    ["receipt_number"] => NULL 
    ["source_transfer_reversal"]=> NULL 
    ["status"]=> string(9) "succeeded" 
    ["transfer_reversal"]=> NULL 
}

 

その他

JCBカードが使えない

テスト用JCBクレジットカードで試したところ決済が通らなかった。公式にサイトによると本番環境の利用が可能になるとJCBも受付可能になるとのこと。そのためテスト環境では利用不可。

 

所感

決済完了後、別ページに遷移してからデータベースに登録する形になるのがセキュリティ的にやや不安。

payment_intent(pi_xxxxxxxxxx部分)はStripe側が発行するsession_idを元に呼び出せるので問題無さそうだが、それ以外のデータを登録する際にGETパラメータで渡すことになり、その辺りがちょっと怖いところ。

ちょっと面倒だが復号可能な形で暗号化して渡す方が良いかも。

 

参考サイト

https://qiita.com/p_s_m_t/items/e665a3492b80ea7fc7df

https://docs.stripe.com/checkout/quickstart?client=html&lang=php&locale=ja-JP

https://docs.stripe.com/refunds?locale=ja-JP&dashboard-or-api=api

 - PHP

  関連記事

PHPとStripeで定額課金(サブスクリプション)実装する方法

PHPとStripeで1ヶ月ごとに500円でサイト内のサービスを使い放題、といっ ...

Stripeでキャンセル処理を行う際に「$config must be a string ~」エラーが出る場合の対応方法

Stripeにて先日メモした内容でキャンセル(返金)処理をしようとしたところ1件 ...

PHPとStripeを使ってサイト内でクレジットカード決済処理の実装方法

PHPとStripeの組み合わせでWebサービスにクレジットカード決済処理を導入 ...