gzjhzy/think-withdraw

withdraw terminal service api sdk for thinkphp

Maintainers

Package info

gitee.com/goodbugood/think-pay.git

Type:think-extend

pkg:composer/gzjhzy/think-withdraw

Transparency log

Statistics

Installs: 68

Dependents: 0

Suggesters: 0

3.1.10 2026-07-10 07:57 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}

  1. platform_code_and_app_id 是平台码 + 应用 ID/商户 id,例如 yunzhanghu_123456。他是 transfer 服务读取你提供的配置参数,进行验签,解密的核心。
  2. 提供那种类型的通知地址给上游,取决于上游的通知参数中是否包含业务方法。

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

问题排查

日志

  1. 如果是 think-withdraw 报错,日志写在 runtime/log 目录下
  2. 如果正常的业务日志写在 runtime/transfer/log 目录下,如果你的 config/log.php 日志配置文件未配置 transfer 通道,业务日志写会写在 runtime/log 目录下。

要注意这两点区别。