gzjhzy / think-withdraw
withdraw terminal service api sdk for thinkphp
Package info
gitee.com/goodbugood/think-pay.git
Type:think-extend
pkg:composer/gzjhzy/think-withdraw
Requires
- php: ^7.2.0
- ext-bcmath: *
- ext-curl: *
- ext-json: *
- ext-openssl: *
- nesbot/carbon: 2.73.0
- php-curl-class/php-curl-class: 10.0.1
- psr/log: ~1.0
- shali/think-pos: ^4.0.0
- topthink/framework: ^5.0 || ^6.0.0
- topthink/think-queue: ^3.0
Requires (Dev)
- phpunit/phpunit: ^9.6
This package is auto-updated.
Last update: 2026-07-15 01:27:06 UTC
README
如果觉得
think-withdraw不错,欢迎给个 star,谢谢。
目录结构
src/config/withdraw.php // 渠道配置文件,写好每个厂商的配置参数模版,记住是模版,不要把真实参数提交
src/dto/ // 接口数据传输对象
src/exception/ // 接口异常,不细分不需要再定义
src/extend/ // 接口扩展,你在对接时定义的工具类
src/provider/ // 接口服务提供者,一个厂商一个包,每个包里新建 `README.md` 文件,里面写厂商的接口文档
src/phpmate/ // 该目录下的文件未来将独立出去进行单独维护的,你不可以在此包下添加文件
使用方法
// 1. 在你的 tp 项目下面安装 `yaoliyong/think-withdraw` 包
composer require yaoliyong/think-withdraw
// 2. 构建 withdraw 策略业务
$configParams = [];// 服务商配置参数(参数请参考各个渠道的配置类公共属性);notifyUrl + channelId 用来配置回调地址,订单回调路由 = {notifyUrl}/channel/{channelId};签约回调路由 = {notifyUrl}/channel/{channelId}/type/h5Sign
$WithdrawStrategy = WithdrawStrategyFactory::create('yunzhanghu', $configParams);
$requestDto = new CreateOrderRequestDto();
// 省略Dto设置参数
$WithdrawStrategy->createOrder($requestDto);
通知消息通道接入
think-withdraw 接收到代付平台通知后,会把不同平台的通知转换成统一的消息对象,再通过队列投递给业务项目。
业务项目需要实现消息读取通道
think\withdraw\channel\SignNotifyChannel // 签约状态通知
think\withdraw\channel\TransferOrderNotifyChannel // 代付订单状态通知
绑定通道实现
在 ThinkPHP 项目的 app/provider.php 里,把扩展接口绑定到自己的实现类:
<?php
return [
// 其他容器绑定...
\think\withdraw\channel\SignNotifyChannel::class
=> \app\common\channel\transfer\SignNotifyChannelImpl::class,
\think\withdraw\channel\TransferOrderNotifyChannel::class
=> \app\common\channel\transfer\TransferOrderNotifyChannelImpl::class,
];
告知代付平台你的通知地址
由于 transfer 服务接管了代付平台的通知,所以你不需要配置路由,书写 controller,只需要把地址告诉代付平台即可。
类型1:
https://你的域名/plugin/transfer/notify/all-business/platform/{platform_code_and_app_id}
类型2:
https://你的域名/plugin/transfer/notify/signStatus/platform/{platform_code_and_app_id}
- platform_code_and_app_id 是平台码 + 应用 ID/商户 id,例如
yunzhanghu_123456。他是 transfer 服务读取你提供的配置参数,进行验签,解密的核心。 - 提供那种类型的通知地址给上游,取决于上游的通知参数中是否包含业务方法。
ReadResult 返回值
ReadResult::ack() // 已消费。若消息状态是终态,Job 会删除;非终态会继续延迟通知
ReadResult::retry(30, '我这边还没准备好,30 秒后再通知我') // 延迟指定秒数后重试
ReadResult::fail('我做业务更新失败了,请稍后再通知我') // 记录失败原因,并按退避延时重试
ReadResult::garbage('这不是我要的消息,你可以删除了') // 垃圾消息,直接删除 Job
代付订单通知通道
class TransferOrderNotifyChannelImpl implements TransferOrderNotifyChannel
{
public function read(Message $message): ReadResult
{
if (!$message instanceof TransferNotifyMessage) {
return ReadResult::garbage('这不是我这个身份应该看到的消息');
}
// 根据 platformCode + merchantOrderNo 定位本地订单
// 保存渠道订单号、状态、金额、税费、手续费、代付时间等字段
// 如果本地系统临时不可用,返回 ReadResult::retry() 或 ReadResult::fail()
return ReadResult::ack();
}
}
TransferNotifyMessage 常用字段:
$message->getPlatformCode(); // 平台码
$message->getMerchantOrderNo(); // 商户订单号
$message->getChannelOrderNo(); // 渠道/平台订单号
$message->getStatus(); // 统一代付状态
$message->getStatusDesc(); // 状态描述
$message->getAmount(); // 代付金额,Money|null
$message->getActualAmount(); // 实际到账金额,Money|null
$message->getIndividualIncomeTax(); // 个人所得税,Money|null
$message->getValueAddedTax(); // 增值税,Money|null
$message->getAdditionalTax(); // 附加税,Money|null
$message->getFee(); // 服务费,Money|null
$message->getTransferTime(); // 代付时间,LocalDateTime|null
签约通知通道
class SignNotifyChannelImpl implements SignNotifyChannel
{
public function read(Message $message): ReadResult
{
if (!$message instanceof SignNotifyMessage) {
return ReadResult::garbage('你是不是发错了消息');
}
// 根据 platformCode + idCardNo 定位本地签约记录
// 保存签约状态、状态描述、签约链接、合同文件地址
// 如果本地系统临时不可用,返回 ReadResult::retry() 或 ReadResult::fail()
return ReadResult::ack();
}
}
SignNotifyMessage 常用字段:
$message->getPlatformCode(); // 平台码
$message->getIdCardNo(); // 身份证号
$message->getStatus(); // 统一签约状态
$message->getStatusDesc(); // 状态描述
$message->getSignUrl(); // 签约链接
$message->getSignFileUrl(); // 签约合同文件下载地址
启动队列消费
通知消息会进入扩展统一的读取队列,业务项目需要启动队列监听:
php think queue:listen --queue transfer:message:read
测试
vendor/bin/phpunit --filter "XzbPayTest::testQueryBalance" --configuration phpunit.xml vendor/bin/phpunit --filter "XzbPayTest::testUserSign" --configuration phpunit.xml vendor/bin/phpunit --filter "XzbPayTest::testCreateOrder" --configuration phpunit.xml vendor/bin/phpunit --filter "XzbPayTest::testQueryOrder" --configuration phpunit.xml vendor/bin/phpunit --filter "XzbPayTest::testHandleCallbackOfWithdraw" --configuration phpunit.xml
问题排查
日志
- 如果是
think-withdraw报错,日志写在runtime/log目录下 - 如果正常的业务日志写在
runtime/transfer/log目录下,如果你的config/log.php日志配置文件未配置transfer通道,业务日志写会写在runtime/log目录下。
要注意这两点区别。