本插件用于无缝对接**支付系统**(如彩虹支付等),实现发起支付请求、同步/异步通知验签、订单状态验证等功能。采用标准 MD5 签名算法,支持支付宝、微信等多种支付方式。 ## 1. 概述 **命名空间:** `admin\pay` **核心类:** `epay` **主要功能:** 生成支付表单并自动提交,验签同步/异步回调数据,验证订单真实性。 **签名方式:** MD5(参数排序拼接 + 密钥)。 ## 2. 类与配置 插件内置元数据配置,可通过 `config()` 方法获取。 ``` private $config = [ 'id' => 1, 'class' => 'epay', 'type' => 0, 'name' => '支付', 'Version' => 1.0, 'description' => '对接支付系统的插件', 'author' => '小蕾Gg' ]; ``` `config()` 返回上述配置的 JSON 字符串,用于系统识别支付插件信息。 ## 3. 支付流程 支付标准流程如下: 1. 用户选择商品,后台调用 `pay()` 方法。 2. 插件构造支付参数(商户ID、订单号、金额、支付方式等),生成 MD5 签名。 3. 自动输出一个带有隐藏表单的 HTML 页面,并立即提交到支付网关( `submit.php` )。 4. 用户跳转至支付收银台完成付款。 5. 支付成功后,支付会异步通知 `notify_url` (后台)和同步跳转 `return_url` (前端)。 6. 商户需在对应回调地址中调用 `verifyNotify()` 或 `checkSign()` 验签并更新订单状态。 ✅ **注意:** 插件使用了 `submitForm()` 输出自动提交表单,调用 `pay()` 后会直接 `die()` ,请不要在之后继续输出内容。 ## 4. 签名机制 签名算法(与支付官方一致): - 对所有请求参数(除 `sign` 和 `sign_type` 外)按参数名升序排序。 - 拼接成 `key1=value1&key2=value2` 格式字符串。 - 末尾拼接商户密钥( `app_key` / `secret_key` )。 - 计算 MD5 值作为 `sign` 。 **回调验签时:** 过滤掉回调参数中的 `sign` 、 `sign_type` 以及空值,再按相同规则计算签名,与接收到的 `sign` 对比。 ## 5. 方法详解 ### 5.1 pay($param, $info) **功能:** 发起支付请求,自动输出表单并跳转至支付网关。 **参数:** - `$param` :订单参数数组,包含: - `pay_type` :支付方式(如 alipay, wxpay, qqpay) - `out_trade_no` :商户订单号(唯一) - `gm_type` :游戏/商品类型标识(用于回调路由) - `subject` :商品名称 - `money` :订单金额(元) - `$info` :支付配置信息,包含: - `appid` :支付商户ID(pid) - `app_key` :商户密钥(用于签名) - `url` :支付网关地址(如 `http://pay.example.com/` ) - `id` :支付插件记录ID(用于回调识别) **返回值:** 无(直接输出 HTML 并终止脚本)。 **构造的支付参数:** ``` [ "pid" => $info['appid'], "type" => $param['pay_type'], "out_trade_no" => $param['out_trade_no'], "notify_url" => "{$host}/notify/syn/gm_type/{$param['gm_type']}/cid/{$info['id']}", "return_url" => "{$host}/notify/index/gm_type/{$param['gm_type']}/cid/{$info['id']}", "name" => $param['subject'], "money" => sprintf('%.2f', $param['money']), "sign" => $this->getSign($data, $info['app_key']), "sign_type" => "MD5" ] ``` ⚠️ 注意:异步通知地址包含 `gm_type` 和 `cid` ,便于商户区分业务类型。请确保路由可正确解析。 ### 5.2 checkSign($params, $info) **功能:** 验签回调参数(通常用于同步返回或异步通知的快速验证)。 **参数:** `$params` 为回调参数数组, `$info` 包含商户配置(至少包含 `secret_key` )。 **返回:** 验签成功返回 `['out_trade_no' => 商户订单号, 'trade_no' => 支付平台订单号]` ,失败返回 `false` 。 **注意:** 该方法直接调用 `getSign()` 比对签名,适用于简单的验签场景。 ### 5.3 verifyNotify($params, $info) **功能:** 完整的异步通知验签(符合支付标准流程)。 **流程:** 首先判断 `$params` 非空,然后调用 `getSignVeryfy()` 进行参数过滤、排序、拼接、MD5 比对。 **返回:** 布尔值 `true` 表示验签通过, `false` 表示失败。 📌 推荐在异步通知脚本中使用该方法,因为它严格按照支付签名规则过滤了空值、 `sign` 、 `sign_type` 、 `gm_type` 、 `cid` 等额外字段。 ### 5.4 getSign($data, $secret_key) **功能:** 生成签名(内部方法)。 **规则:** 移除 `sign` 和 `sign_type` 键,排序,拼接字符串,最后加上密钥计算 MD5。 ## 6. 异步与同步通知 支付在用户支付成功后,会同时发起: - **异步通知(notify_url)**:服务器后台 POST 请求,商户需在此处更新订单状态并输出 `success` 字符串(支付会重试直到收到 success)。 - **同步跳转(return_url)**:用户浏览器跳转,可展示支付结果页面,一般只需展示信息,不需要修改订单状态(以防异步未及时到达)。 **回调参数示例:** ``` { "pid": 1001, "trade_no": "202312010001", "out_trade_no": "ORDER20231201001", "type": "alipay", "name": "商品名称", "money": "10.00", "trade_status": "TRADE_SUCCESS", "sign": "md5签名", "sign_type": "MD5" } ``` **验签处理示例(异步回调脚本):** ``` $postData = $_POST; $epay = new \admin\pay\epay(); $info = [ 'appid' => '1001', // 商户ID 'app_key' => 'your_secret_key' // 商户密钥 ]; if ($epay->verifyNotify($postData, $info)) { // 签名验证成功,更新订单状态 $out_trade_no = $postData['out_trade_no']; // 更新数据库 ... echo 'success'; // 告知支付不再重发 } else { echo 'fail'; } ``` ## 7. 调用示例 ``` <?php require_once 'epay.php'; use admin\pay\epay; $epay = new epay(); // 支付配置(通常从数据库获取) $paymentInfo = [ 'id' => 1, // 插件记录ID 'appid' => '1001', // 商户PID 'app_key' => 'abcdef123456', // 商户密钥 'url' => 'https://pay.example.com/', // 网关地址 ]; // 订单参数 $orderParams = [ 'pay_type' => 'alipay', // 支付方式 'out_trade_no' => '20231201001',// 商户订单号 'gm_type' => 'game_gold', // 业务类型标识 'subject' => '游戏点卡100元', 'money' => 100.00, ]; // 发起支付(将自动跳转) $epay->pay($orderParams, $paymentInfo); // 以下代码不会执行 ?> ``` 异步通知脚本示例 ( `/notify/syn/gm_type/.../cid/...` ): ``` <?php $post = $_POST; $epay = new \admin\pay\epay(); // 根据路由参数获取对应支付配置 $paymentInfo $paymentInfo = ...; // 包含 app_key 等 if ($epay->verifyNotify($post, $paymentInfo)) { // 验签通过,处理业务逻辑 // 修改订单状态为已支付 echo 'success'; } else { echo 'fail'; } ?> ``` ## 8. 集成注意事项 - **回调 URL 构造:** 插件自动生成 `notify_url` 和 `return_url` ,其中包含 `gm_type` 和 `cid` 参数,请确保您的框架路由能正确解析这些参数,并获取到对应的支付配置信息(尤其是 `app_key` )。 - **密钥安全:** `app_key` 是签名核心,务必妥善保存在服务端,不可泄露给客户端。 - **订单号唯一性:** `out_trade_no` 必须保证唯一,避免重复支付。 - **异步通知幂等性:** 支付可能多次通知,请确保您的业务逻辑可以处理重复通知(例如判断订单状态是否已支付)。 - **同步通知验签:** 同样建议调用 `verifyNotify()` 或 `checkSign()` 验证签名,防止伪造的返回跳转。 - **输出 'success':** 异步通知处理完毕后,必须输出字符串 `success` ,否则支付会重复发送通知。 - **HTTPS 支持:** `getHostDomain()` 会自动识别当前请求协议,确保回调地址使用正确的 HTTPS/HTTP。 - **支付方式对照:** 常见的 `pay_type` 值: `alipay` (支付宝)、 `wxpay` (微信支付)、 `qqpay` (QQ钱包)、 `bank` (网银)等,具体请参考支付文档。 ## 9. 错误与返回说明 |场景 |返回值/行为 |处理建议 | |:--| |支付请求成功 |自动提交表单,跳转网关 |无需额外处理 | |验签成功(checkSign) |返回订单数据数组 |更新订单状态 | |验签失败(checkSign) |返回 false |记录日志,拒绝处理 | |异步通知验签通过 |verifyNotify 返回 true |输出 'success' | |异步通知验签失败 |verifyNotify 返回 false |输出 'fail',支付会重试 | |缺少必要参数 |表单自动输出,但支付可能报错 |检查 $param 完整性 | 📌 插件本身不直接返回错误码,而是通过验签方法的真假值及输出 HTML 来实现支付跳转。集成时请确保回调地址正确配置。 ## 附录:签名生成演示 ``` // 原始参数 $data = [ "pid" => 1001, "type" => "alipay", "out_trade_no" => "ORDER001", "money" => "10.00", "name" => "测试商品" ]; $secret = "abc123"; // 排序后拼接: money=10.00&name=测试商品&out_trade_no=ORDER001&pid=1001&type=alipay // 加上密钥: money=10.00&name=测试商品&out_trade_no=ORDER001&pid=1001&type=alipayabc123 // MD5 得到签名: 5d41402abc4b2a76b9719d911017c592 ``` 更新于 2026-09-28 13:58 阅读 38 ← 风格插件 相关问题 对接插件 风格插件