勉強したことのメモ

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

PHPにてサイトにPayPay決済システム及び決済キャンセル機能を実装する方法

  PHP

ここのところクレジットカード決済関連のメモを書いているが、他の決済方法についても試してみたかった。こちらのサイトによると「2024年度のよく利用する決済手段」はクレカが1位、PayPayが2位とのことで、この2つをあわせると90%以上のシェアに対応できる模様。そのためPayPay決済及びキャンセル機能の実装方法をメモ。

 

下準備

PayPayのDeveloper用アカウント作成

PayPayの公式サイトでDeveloper用アカウントを作成しておく。

アカウント作成後にAPIキー等が発行されるまでには時間がかかる点に注意。特に当方の環境だと4時間後ぐらいにページ更新しても発行されておらず、ダッシュボードからログアウト→ログインすることで反映された。詳細はこちらの公式案内を参照。

各種必要情報が発行されれば「APIキー」「シークレット」「加盟店ID」をメモっておくこと。

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

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

composer require paypayopa/php-sdk

ライブラリの要件は見受けられなかった。念のためGitHubページはこちらになる。

テストユーザについて

ダッシュボード内の「テストユーザー」タブを開くと表示される筈(ユーザーネーム部分に電話番号が表示される)。

尚、決済テスト時に認証コードを聞かれた場合は「1234」を入力すること。詳細は公式案内を参照。

 

決済機能の実装方法

index.php(決済ページへ遷移するためのプログラム)

コメントの「変更が必要な場所」内を適宜変更すること。

<?php
require __DIR__ . '/vendor/autoload.php';
use PayPay\OpenPaymentAPI\Client;
use PayPay\OpenPaymentAPI\Models\CreateQrCodePayload;
use PayPay\OpenPaymentAPI\Models\OrderItem;


/* 変更が必要な場所ここから */

$api_key = 'xxxxx'; //APIキー
$secret_key = 'xxxxx'; //シークレットキー
$merchant_id = 'xxxxx'; //加盟店ID

$item_name = 'テスト商品1'; //商品名
$item_price = 100; //商品価格
$item_quantity = 1; //商品個数

$merchant_payment_id = 'mpid_' . md5(uniqid(rand(), true)); //決済用ID
$redirect_url = 'https://test.com/complete.php?merchant_payment_id=' . $merchant_payment_id; //決済終了後のリダイレクト先

/* 変更が必要な場所ここまで */


$client = new Client([
    'API_KEY' => $api_key,
    'API_SECRET' => $secret_key,
    'MERCHANT_ID' => $merchant_id,
], false);

$items = (new OrderItem())
    ->setName($item_name)
    ->setQuantity($item_quantity)
    ->setUnitPrice(['amount' => $item_price, 'currency' => 'JPY']);

$payload = new CreateQrCodePayload();
$payload->setOrderItems($items);
$payload->setMerchantPaymentId($merchant_payment_id);
$payload->setCodeType("ORDER_QR");
$payload->setAmount(["amount" => $item_price, "currency" => "JPY"]);
$payload->setRedirectType('WEB_LINK');
$payload->setIsAuthorization(false);
$payload->setRedirectUrl($redirect_url);
$payload->setUserAgent($_SERVER['HTTP_USER_AGENT']);
$QRCodeResponse = $client->code->createQRCode($payload);
if( $QRCodeResponse['resultInfo']['code'] !== 'SUCCESS' || !$QRCodeResponse['data']['url'] ){
    echo("エラー");
    exit();
}

//決済ページに遷移
header('Location:' . $QRCodeResponse['data']['url']);
exit();

上記ページをブラウザから実行すると以下のようなページに遷移する筈。

上記は2回目の決済テストだったためテストユーザーでのログインや認証コード云々は聞かれなかったが、初回の場合は聞かれる筈なので前述の「テストユーザーについて」を参照すること。また、決済が完了した後のリダイレクトページは以下。

complete.php(決済完了後のリダイレクト先)

コメントの「変更が必要な場所」内を適宜変更すること。また、決済IDは後述の決済キャンセル時に必要になるため、MySQLへの保存を推奨。

<?php
require __DIR__ . '/vendor/autoload.php';
use PayPay\OpenPaymentAPI\Client;
use PayPay\OpenPaymentAPI\Models\CreateQrCodePayload;
use PayPay\OpenPaymentAPI\Models\OrderItem;

/* 変更が必要な場所ここから */

$api_key = 'xxxxx'; //APIキー
$secret_key = 'xxxxx'; //シークレットキー
$merchant_id = 'xxxxx'; //加盟店ID

/* 変更が必要な場所ここまで */

$merchant_payment_id = $_GET['merchant_payment_id']; //決済用IDはデータベースに保存推奨

$client = new Client([
    'API_KEY' => $api_key,
    'API_SECRET' => $secret_key,
    'MERCHANT_ID' => $merchant_id,
], false);

$QRCodeDetails = $client->payment->getPaymentDetails($merchant_payment_id);
if($QRCodeDetails['resultInfo']['code'] !== 'SUCCESS') {
    echo("決済情報取得エラー");
    return;
}
var_dump($QRCodeDetails);

出力例

上記ページの出力例は以下になる。なお、「data->status」が「COMPLETED」だと決済済み、「FAILED」だと失敗もしくは決済キャンセルになるっぽい。

array(2) {
  ["resultInfo"]=>
  array(3) {
    ["code"]=>
    string(7) "SUCCESS"
    ["message"]=>
    string(7) "Success"
    ["codeId"]=>
    string(8) "xxxxx"
  }
  ["data"]=>
  array(11) {
    ["paymentId"]=>
    string(20) "xxxxx"
    ["status"]=>
    string(9) "COMPLETED"
    ["acceptedAt"]=>
    int(1750322751)
    ["refunds"]=>
    array(1) {
      ["data"]=>
      array(0) {
      }
    }
    ["merchantPaymentId"]=>
    string(37) "xxxxx"
    ["amount"]=>
    array(2) {
      ["amount"]=>
      int(100)
      ["currency"]=>
      string(3) "JPY"
    }
    ["requestedAt"]=>
    int(1750322751)
    ["terminalId"]=>
    string(0) ""
    ["orderItems"]=>
    array(0) {
    }
    ["assumeMerchant"]=>
    string(18) "xxxxx"
    ["paymentMethods"]=>
    array(1) {
      [0]=>
      array(2) {
        ["amount"]=>
        array(2) {
          ["amount"]=>
          int(100)
          ["currency"]=>
          string(3) "JPY"
        }
        ["type"]=>
        string(6) "WALLET"
      }
    }
  }
}

 

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

cancel.php

コメントの「変更が必要な場所」内を適宜変更すること。決済IDは前述のものを記述する。

<?php
require __DIR__ . '/vendor/autoload.php';
use PayPay\OpenPaymentAPI\Client;
use PayPay\OpenPaymentAPI\Models\CreateQrCodePayload;
use PayPay\OpenPaymentAPI\Models\OrderItem;

/* 変更が必要な場所ここから */

$api_key = 'xxxxx'; //APIキー
$secret_key = 'xxxxx'; //シークレットキー
$merchant_id = 'xxxxx'; //加盟店ID

$merchant_payment_id = 'xxxxx'; //決済ID

/* 変更が必要な場所ここまで */

$client = new Client([
    'API_KEY' => $api_key,
    'API_SECRET' => $secret_key,
    'MERCHANT_ID' => $merchant_id,
], false);

$response =  $client->payment->cancelPayment($merchant_payment_id);
var_dump($response);

出力例

上記ページの出力例は以下になる。

array(2) {
  ["resultInfo"]=>
  array(3) {
    ["code"]=>
    string(16) "REQUEST_ACCEPTED"
    ["message"]=>
    string(16) "Request accepted"
    ["codeId"]=>
    string(8) "xxxxx"
  }
  ["data"]=>
  NULL
}

 

注意点等

キャッシュに注意する

当方の環境だとindex.php→PayPay側に遷移した後、エラーが発生するケースがあった。どうもindex.phpを開いた際にキャッシュが残っており、その辺りでエラーになっているっぽい。

実際にサイトに実装する際は商品ページ等にindex.phpへのリンクを埋め込むことになるかと思うが、その際にGETパラメータでタイムスタンプ等を付与してキャッシュ対策しておいた方が安全かと思われる。

キャンセル後のレスポンス

同じ決済IDに対して複数回キャンセル処理しても「REQUEST_ACCEPTED」が返ってきた。また、念のため決済IDを変えてキャンセル処理しても同様だったため、キャンセル処理後のレスポンスが正しいのかどうかちょっと不安かも。

そのため実際にサイトに実装する際はキャンセル処理後にcomplete.phpでやったように決済情報を取得し、「data->status」が「FAILED」になっているか確認した方が良いかも。

 

決済手数料について

公式ページによると1決済あたり1.60~1.98%とのこと。

 

所感

国産のためかリファレンスが日本語なのが助かる。ダッシュボードの動作も軽め。

ただ、1つのサイトにクレカとPayPayを両方実装するとなると、データベース側の制御がやや面倒くさそう。

 

参考サイト

https://developer.paypay.ne.jp/products/docs/webpayment

https://www.paypay.ne.jp/opa/doc/jp/v1.0/webcashier

https://github.com/paypay/paypayopa-sdk-php

https://qiita.com/rururu3/items/841e3b5a73da3447dc69

https://kawaidesu.hatenablog.com/entry/2020/07/31/135428

 - PHP

  関連記事

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

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

PHPとPAY.JPを用いてサイト内にクレカ決済及びキャンセル機能を実装する方法

先日PHPとStripeを用いてサイト内にクレカ決済及びキャンセル機能を実装する ...

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

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

Stripe Checkoutにてクレジットカード決済の際に3Dセキュア対応にする方法

先日Stripe Checkoutを用いたクレジットカード決済機能についてメモし ...

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

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