決済プラグイン開発
決済プラグインは app/Pay/<名前>/ に置きます。ディレクトリ名が handle となり、システム全体でこの決済手段を識別するキーになります。
ディレクトリ構成
app/Pay/Demo/
Config/
Info.php プラグイン情報 + コールバック定義(必須)
Config.php 設定の既定値
Submit.js 「決済インターフェース」のフォーム(旧形式 Submit.php も可)
Impl/
Pay.php 注文作成の実装(必須)
Signature.php コールバックの署名検証(署名を使う場合は必須)
View/
1.html 決済ページをローカル描画する場合のテンプレート
Assets/ プラグイン独自の静的リソース
Vendor/
autoload.php プラグイン専用のサードパーティ依存(任意)Config/Info.php
通常のプラグインより 2 つ多く、options と callback があります。
<?php
declare(strict_types=1);
return [
'version' => '1.0.0',
'name' => 'デモ決済',
'author' => 'あなたの名前',
'website' => 'https://example.com',
'description' => '1 行の説明',
// このプラグインが提供する決済形態。運営者が 1 つ選びます
'options' => [
1 => 'QR コード',
2 => 'PC 決済',
3 => 'モバイル決済',
],
// コールバックの検証方法。コアがこの定義どおりに自動処理します
'callback' => [
\App\Consts\Pay::IS_SIGN => true, // 署名を検証するか
\App\Consts\Pay::IS_STATUS => true, // ステータス項目を確認するか
\App\Consts\Pay::FIELD_STATUS_KEY => 'trade_status', // ステータス項目名
\App\Consts\Pay::FIELD_STATUS_VALUE => 'TRADE_SUCCESS', // 成功を表す値
\App\Consts\Pay::FIELD_ORDER_KEY => 'out_trade_no', // 注文番号の項目名
\App\Consts\Pay::FIELD_AMOUNT_KEY => 'total_amount', // 金額の項目名
\App\Consts\Pay::FIELD_RESPONSE => 'success' // ゲートウェイに返す内容
]
];
callbackが無いとコールバックは即座に拒否されます。 理由は「プラグインに Config/Info.php の callback 定義がありません」となります。
Config/Submit.js — 設定フォーム
管理画面の 決済インターフェース で運営者が入力する項目は、このファイルで定義します。形式は通常のプラグインとまったく同じです(プラグイン開発 · 設定フォーム)。
決済プラグインは通常、基本設定と決済形態ごとのタブに分けます。
[
{
name: `${util.icon("/app/Pay/Demo/Assets/Icon/Setting.png")} 基本設定`,
form: [
{ title: "加盟店 ID", name: "mch_id", type: "input", placeholder: "決済事業者から発行された加盟店 ID", required: true },
{ title: "API キー", name: "key", type: "input", placeholder: "決済事業者から発行されたキー", required: true }
]
},
{
name: "QR コード決済",
form: [
{ title: "受取人", name: "payee", type: "input", placeholder: "決済画面に表示される受取人" }
]
}
]グループの
nameではutil.icon()が使えます。Font Awesome のクラス名でも、プラグイン自身のAssets/内の画像パスでも構いません。
旧形式の Config/Submit.php(フラットなフィールド配列)も引き続き使えます。両方存在する場合は Submit.js が優先されます。
Impl/Pay.php
App\Pay\Pay インターフェースを実装します。メソッドは trade() の 1 つだけです。
<?php
declare(strict_types=1);
namespace App\Pay\Demo\Impl;
use App\Entity\PayEntity;
use App\Pay\Base;
use Kernel\Exception\JSONException;
class Pay extends Base implements \App\Pay\Pay
{
public function trade(): PayEntity
{
// $this->code は運営者が選んだ options のキー
if ($this->code == 1) {
return $this->qrcode();
}
throw new JSONException("不正なリクエストです");
}
private function qrcode(): PayEntity
{
// $this->config の認証情報で決済事業者を呼び、決済 URL を取得する
$payUrl = '...';
$entity = new PayEntity();
$entity->setType(\App\Pay\Pay::TYPE_REDIRECT);
$entity->setUrl($payUrl);
return $entity;
}
}基底クラスが提供するもの
App\Pay\Base を継承すると、次がそのまま使えます。
| プロパティ | 内容 |
|---|---|
$this->amount | 注文金額(float) |
$this->tradeNo | 注文番号 |
$this->config | 運営者が入力した設定 |
$this->callbackUrl | 非同期コールバック URL。事業者に渡します |
$this->returnUrl | 決済後に購入者が戻ってくる URL |
$this->clientIp | 購入者の IP |
$this->code | 運営者が選んだ決済形態(options のキー) |
$this->handle | プラグインのディレクトリ名 |
決済ページの 3 つの提示方法
PayEntity::setType() で、購入者をどう決済ページへ送るかが決まります。
| 定数 | 値 | 動作 |
|---|---|---|
TYPE_REDIRECT | 2 | setUrl() のアドレスへそのまま遷移 |
TYPE_LOCAL_RENDER | 3 | プラグインの View/<code>.html で決済ページをローカル描画(QR コードを自前で描くなど) |
TYPE_SUBMIT | 4 | POST フォームで送信 |
Impl/Signature.php
IS_SIGN が true のときは必須です。verification() を実装します。
<?php
declare(strict_types=1);
namespace App\Pay\Demo\Impl;
class Signature
{
public function verification(array $map, array $config): bool
{
$sign = $map['sign'] ?? '';
unset($map['sign']);
ksort($map);
$expect = md5(urldecode(http_build_query($map) . '&key=' . $config['app_key']));
return hash_equals($expect, (string)$sign);
}
}false を返すとコアはコールバックを拒否し、SERVICE_PAY_CALLBACK_FAIL フックを発火します。
コア側のコールバック処理の流れ
コールバックが届くと、コアは次の順で処理します。すべて通過して初めて注文が発送されます。
Config/Info.phpのcallback定義を読む — 無ければ拒否(plugin)- プラグインに
Vendor/autoload.phpがあれば読み込む IS_SIGNが真なら、認証情報が設定済みか確認し(未設定ならcredentialで拒否)、Signature::verification()を呼ぶ(失敗ならsignで拒否)IS_STATUSが真なら、ステータス項目を照合する(不一致ならstatusで拒否)- 注文番号と金額を取り出し、注文サービスに渡す
FIELD_RESPONSEの内容をそのままゲートウェイに返す
いずれの失敗も $reason を伴って SERVICE_PAY_CALLBACK_FAIL(0x3010)フックを発火します(フック一覧参照)。決済アラートを作るならこれを購読してください。
金額と通貨
サイトの通貨が人民元以外の場合、ゲートウェイに送る金額はレートで換算する必要があります。レートの意味は**「サイト通貨 1 単位が人民元でいくらか」**です。
ストアフロントに金額を表示するときは、¥ や「元」をハードコードしないでください。次を使います。
\App\Util\Currency::symbol()通貨記号のハードコードは決済プラグインで最もよくある表示バグです。請求される金額は正しいのに、表示される通貨だけが違う状態になります。
専用の依存関係
サードパーティ SDK が必要な場合は、プラグインの Vendor/ に置いて autoload.php を用意してください。コアがコールバック処理の前に自動で読み込みます。グローバルな composer.json は変更しないでください。
テスト
管理画面の決済インターフェースにはコールバックのテスト機能(callbackTest)があり、実際に支払わずにコールバック経路を検証できます。
