Search by

jiaoyukun / leshua-sdk-php

jiaoyukun125151

乐刷聚合支付 PHP SDK(支持统一下单、退款、查询、通知验签、对账等)

Package info

gitee.com/jiao_yu_chun/leshua-sdk-php.git

pkg:composer/jiaoyukun/leshua-sdk-php

Statistics

Installs: 9

Dependents: 0

Suggesters: 0

dev-master 2026-08-21 11:16 UTC

This package is not auto-updated.

Last update: 2026-09-19 03:29:29 UTC


README

PHP License

基于乐刷商户层级接入文档封装的 PHP 7.2+ SDK。覆盖统一下单、退款、查询、关闭订单等核心接口,支持 MD5 和国密 SM3 两种签名算法。

本 SDK 基于 乐刷商户层级接入文档 开发,已通过生产环境真实商户(xxxxxxxxxx)联调测试

📦 安装

composer require jiaoyukun/leshua-sdk-php

依赖:

🚀 快速上手

use Jiaoyukun\Leshua\Leshua;

$leshua = new Leshua([
    'merchant_no' => 'xxxxxxxxxx',                                  // 乐刷商户号
    'pay_key'     => 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',            // 商户密钥(加签用)
    'parse_key'   => '',                                              // 代理商解析密钥(回调验签用,可选)
    'appid'       => '',                                              // 微信 appid(jspay_flag=1/3 必填,可选)
    'sandbox'     => false,                                           // true=测试环境,false=生产环境
    'notify_url'  => 'https://your-domain.com/leshua/notify',        // 异步通知回调地址(可选)
]);

💳 核心接口

1️⃣ 统一下单(生成聚合码)

$result = $leshua->payment->createOrder([
    'third_order_id' => 'YOUR_ORDER_001',
    'amount'         => 100,        // 单位:分
    'jspay_flag'     => 2,          // 0=Native 1=JSAPI 2=收银台 3=小程序
    'body'           => '培训费',
    // 'notify_url' => 'https://...', // 可覆盖全局配置
]);

// jspay_flag=0/1/2 时返回 jspay_url(收银台/JSAPI)
// jspay_flag=0 时也返回 td_code(Native 二维码内容)
$payUrl    = $result['jspay_url'] ?? null;
$qrContent = $result['td_code'] ?? null;

// 重要字段
$leshuaOrderId = $result['leshua_order_id'];  // 用于后续查询/退款

2️⃣ 条码支付(被扫 - 商家扫用户付款码)

$result = $leshua->payment->barcodePay([
    'third_order_id' => 'YOUR_ORDER_002',
    'amount'         => 200,
    'jspay_flag'     => 0,
    'auth_code'      => '用户付款码',  // 必填
    'body'           => '商品',
]);

3️⃣ 订单状态查询

$result = $leshua->payment->queryOrder('YOUR_ORDER_001');

// 状态字段
$status      = $result['status'];             // 2=支付成功,详见下表
$payTime     = $result['pay_time'];           // 支付完成时间
$payWay      = $result['pay_way'];            // WXZF/ZFBSF/YLZF
$tradeType   = $result['trade_type'];         // JSAPI/NATIVE/MICROPAY/SmPgPay
$amount      = $result['amount'];             // 订单金额(分)
$refunded    = $result['refund_amount'];      // 已退款金额(分)
$openid      = $result['openid'];             // 用户 openid(JSAPI/小程序时返回)
$channelId   = $result['channel_order_id'];    // 微信/支付宝侧订单号

订单状态码

状态码含义
0未支付
1支付中
2支付成功
3已退款
4已关闭

4️⃣ 发起退款

$result = $leshua->payment->refund(
    'YOUR_ORDER_001',         // 商户订单号
    'YOUR_REFUND_001',        // 商户退款单号(业务系统唯一)
    100,                      // 退款金额(分,可部分退款)
    [
        // 'reason' => '学员申请退款',
        // 'notify_url' => 'https://...',
    ]
);

$leshuaRefundId = $result['leshua_refund_id'];  // 乐刷退款单号
$status         = $result['status'];            // 10=退款中 11=退款成功

限制:每笔订单最多 50 次退款,订单发起后 60 天内可退款(文档约定)。

5️⃣ 退款状态查询

// ⚠️ 必须传乐刷订单号(不是商户订单号或商户退款单号)
$result = $leshua->payment->queryRefund('1000626023126233');  // leshua_order_id

$status        = $result['status'];                // 10=退款中 11=退款成功
$refundAmount  = $result['refund_amount'];         // 退款金额
$refundTime    = $result['refund_time'];           // 退款完成时间
$payWay        = $result['pay_way'];               // 原支付方式
$channelOrder  = $result['channel_order_id'];      // 通道侧订单号

6️⃣ 关闭订单

$leshua->payment->closeOrder('YOUR_ORDER_001');

注意:已支付或收银台预下单的订单关闭可能返回「订单不存在」(这是乐刷业务规则,非 SDK 错误)。

🔐 签名算法切换

默认 MD5。如需切换到国密 SM3:

use Jiaoyukun\Leshua\Signer;

// 加签:MD5
$sign = Signer::signForTradeGateway($params, $key, Signer::ALGO_MD5);

// 加签:国密 SM3(乐刷商户层级接入指南要求)
$sign = Signer::signForTradeGateway($params, $key, Signer::ALGO_SM3);

SM3 实现使用 lizhichao/one-sm 包(HMAC-SM3 已自实现,符合国密标准 GB/T 32905-2016)。

🌐 接入指南类接口(暂未实现)

接口用途状态
listTradeDetail交易结算明细查询(代理商对账)⚠️ 暂未实现(单商户模式不常用)
商户进件 / 变更商户资料管理⚠️ 暂未实现(接入流程商务对接)
对账文件下载T+1 下载对账文件⚠️ 暂未实现(见 Plan)

业务侧常用接口已全部覆盖(下单 / 退款 / 查询 / 关闭),单商户模式足够。

📋 订单表字段映射(写订单模块必看)

业务方写订单支付模块时,按下表建字段:

订单表字段来源写入时机必存?
third_order_no业务系统生成创建订单时
amount业务系统生成创建订单时
status业务维护(0/1/2/3)支付/退款时更新
snap_merchant_no当前商户号(快照)创建订单时⭐⭐
snap_pay_key当前 pay_key(快照)创建订单时⭐⭐⭐
snap_appid当前 appid(快照)创建订单时⭐⭐
snap_jspay_flag当前 jspay_flag(快照)创建订单时⭐⭐
leshua_order_idcreateOrder 返回下单后⭐⭐⭐
jspay_urlcreateOrder 返回下单后(前端展示)
td_codecreateOrder 返回下单后(Native 模式)
pay_timequeryOrder 返回支付成功时⭐⭐
pay_wayqueryOrder 返回(WXZF/ZFBZF/YLZF)支付成功时⭐⭐
trade_typequeryOrder 返回(JSAPI/NATIVE/MICROPAY/SmPgPay)支付成功时
channel_order_idqueryOrder 返回(通道侧订单号)支付成功时⭐⭐⭐(对账用)
openidqueryOrder 返回(JSAPI 时)支付成功时
sub_merchant_idqueryOrder 返回(通道子商户号)支付成功时
leshua_refund_idrefund 返回退款发起时⭐⭐⭐
refund_amountrefund / queryRefund 返回退款时累计⭐⭐
refund_timequeryRefund 返回退款成功时⭐⭐
channel_refund_order_idqueryRefund 返回退款成功时⭐⭐⭐(对账用)

⭐ 关键:快照字段为什么必须存?

商户配置可能变更

  • 状态切换(启用 → 停用)
  • 商户删除
  • 密钥轮换
  • 校区切换到其他商户

如果不快照,退款时会失败(用当前配置 + 历史订单号 → 乐刷找不到对应交易)。

退款时正确写法

// 从订单表读快照字段
$order = Db::name('order')->where('third_order_no', $thirdOrderNo)->find();

// 用快照字段构造 SDK(不用当前商户配置)
$snapLeshua = new Leshua([
    'merchant_no' => $order['snap_merchant_no'],
    'pay_key'     => $order['snap_pay_key'],
    'appid'       => $order['snap_appid'],
    'sandbox'     => false,
]);

// 发起退款
$result = $snapLeshua->payment->refund(
    $order['third_order_no'],
    $refundNo,
    $refundAmount
);

⚠️ 重要注意事项(踩坑汇总)

本节记录 真实生产商户联调时 遇到的所有坑,请务必读完再写代码。

1. service 名是 query_status 不是 query_order(最坑)

文档 API 目录写错(写 .cg 少 i),详细章节正确。SDK 实际常量:

Jiaoyukun\Leshua\Payment\PaymentService::SERVICE_QUERY_ORDER = 'query_status';

如果你手动构造请求,必须用 query_status!用 query_order 会返回 -5042 系统错误

2. 加签不 URL 编码

乐刷明确说「字段名和字段值都采用原始值,不进行URL Encode」。

❌ 错误(用 http_build_query):

body=%E4%B9%90%E5%88%B7&notify_url=https%3A%2F%2F...

✅ 正确(原始值):

body=乐刷&notify_url=https://...

SDK 已用 buildRawQuery() 处理。手动调 API 时切记不要 URL 编码

3. 响应是 XML 格式(CDATA 包裹)

虽然 Content-Type 可能是 application/jsontext/xml,实际响应可能是 XML

<leshua>
    <resp_code><![CDATA[0]]></resp_code>
    <leshua_order_id><![CDATA[1000626023126233]]></leshua_order_id>
    ...
</leshua>

SDK 已自动处理 JSON / XML / Form 三种响应格式。如果你手动解析:

$xml = simplexml_load_string($raw);
$resp_code = (string)$xml->resp_code;

4. nonce_str 必填(<= 32 字符)

所有接口都要求 nonce_str 参数,SDK 已自动注入。手动调用需自行加:

$params['nonce_str'] = substr(str_shuffle('abcdefghijklmnopqrstuvwxyz0123456789'), 0, 32);

5. jspay_flag 用 0 时别用 empty() 校验

empty('0') === true,会误判。正确:

// ❌ if (empty($params['jspay_flag']))  // jspay_flag=0 会被认为空
// ✅ if (!in_array((string)$params['jspay_flag'], ['0','1','2','3'], true))

6. queryRefund 必须用 leshua_order_id

测试了所有参数:

参数结果
merchant_refund_id❌ -1005 无效的订单号
leshua_refund_id❌ -1005 无效的订单号
third_order_id❌ -4006 订单不存在
leshua_order_id成功

SDK 的 queryRefund($leshuaOrderId) 参数就是乐刷订单号(不是商户订单号或商户退款单号)。

7. Config::getAppid/getParseKey 允许空

如果你没配置 parse_keyappid,SDK 会返回空字符串(不抛 TypeError)。 如需手动校验,自己做即可。

8. terminal_info 必填

乐刷要求 terminal_info(JSON 字符串),最少包含 device_typeserial_num。SDK 自动拼:

{"device_type":"11","serial_num":"lhsdxxxxxxxxxx"}

device_type=11 = 条码支付辅助受理终端,serial_num=lhsd+商户号 是乐刷规则。

9. queryOrder 对 jspay_flag=2 收银台订单支持有限

实际测试发现:

  • jspay_flag=2(收银台)下单 → 拿到 jspay_url,是真下单
  • queryOrderthird_order_idleshua_order_id 都能查到(前提是 service=query_status 写对
  • closeOrder 对收银台预下单的订单会返回「订单不存在」(因为订单未真生成)

注意:乐刷工作人员建议只用主动轮询 queryOrder,不用异步通知(通知可能丢)。

10. 不要被「API 目录」误导

文档目录写 /cgi-bin/lepos_pay_gateway.cg(少 i),详细章节写 .cgi(对的)。 永远以详细章节为准

11. 响应字段对应

接口响应成功标识失败错误码字段
下单/退款/查询/关闭result_code="0"error_code(业务失败)
接口层错误resp_code="0"resp_code="非0"

成功响应没有 error_code 字段。SDK 的 checkBusinessError() 已正确处理。

12. 退款成功后 status

status含义
10退款处理中
11退款成功

📊 真实联调案例

已通过乐刷商户 xxxxxxxxxx(xxxxxxxxxxxx)真实环境测试。

// 1. 下单 1 分钱
$result = $leshua->payment->createOrder([
    'third_order_id' => 'SDK_PAY_20260821150018_406285',
    'amount'         => 1,
    'jspay_flag'     => 2,  // 收银台
    'body'           => 'SDK联调测试',
]);
// 返回:leshua_order_id=1000626023126233, jspay_url=https://qr.leshuazf.com/...

// 2. 用户扫码支付 1 分钱
// (微信/支付宝/云闪付扫 jspay_url)

// 3. 订单状态查询(service=query_status)
$r = $leshua->payment->queryOrder('SDK_PAY_20260821150018_406285');
// status=2(支付成功), pay_time=2026-08-21 15:01:33, pay_way=WXZF

// 4. 退款 1 分钱
$refund = $leshua->payment->refund('SDK_PAY_20260821150018_406285', 'SDK_RF_001', 1);
// status=10(退款中)

// 5. 退款查询(leshua_order_id!)
$r = $leshua->payment->queryRefund('1000626023126233');
// status=11(退款成功), refund_time=2026-08-21 15:03:18

🔍 错误码速查

错误码含义
0成功
-4006订单不存在
-5033请求签名错误(检查 key、参数、URL 编码)
-5042系统错误(service 名错 / 商户未开通该功能 / 接口不可用)
-1005无效的订单号(参数名错了,例如 queryRefund 用 merchant_refund_id)

📜 License

MIT

🔗 链接

文档最后更新:2026-08-21 真实联调商户:xxxxxxxxxx(xxxxxxxxxxxx河南)