westng / doudian-sdk
抖店(抖音电商)开放平台 PHP SDK,支持710+个API接口
Installs: 822
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 1
Forks: 1
Open Issues: 0
pkg:composer/westng/doudian-sdk
Requires
- php: >=7.0.0
- guzzlehttp/guzzle: ^6.0|^7.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^2.19|^3.0
- phpstan/phpstan: ^0.12.0
- phpunit/phpunit: ^6.0|^7.0|^8.0|^9.0
- squizlabs/php_codesniffer: ^3.0
README
抖店(抖音电商)开放平台 PHP SDK,支持 710+ 个 API 接口,经过完整测试验证。
✨ 特性
- 🚀 完整的 API 覆盖: 支持 710+ 个抖店开放平台接口
- 🔒 安全的签名机制: 内置 HMAC-SHA256 签名算法
- ⚡ 高性能: 基于 GuzzleHttp 实现,支持并发请求
- 🛡️ 异常处理: 完善的错误处理和重试机制
- 📦 易于使用: 简洁的 API 设计,支持链式调用
- 🧪 测试覆盖: 85% 测试覆盖率,核心功能全面验证
- 🔧 配置灵活: 支持超时、重试、调试等多种配置
- 🔄 令牌管理: 自动令牌刷新和缓存机制
安装
composer require westng/doudian-sdk
🏗️ SDK架构说明
本SDK采用分层架构设计,遵循单一职责原则:
┌─────────────────────────────────────────┐
│ DouDianSdk │ ← 门面层:简化API调用
├─────────────────────────────────────────┤
│ DouDianOpClient │ ← 业务层:处理API请求
├─────────────────────────────────────────┤
│ HttpClient │ ← 传输层:HTTP通信
├─────────────────────────────────────────┤
│ GlobalConfig/DouDianOpConfig │ ← 配置层:统一配置管理
└─────────────────────────────────────────┘
核心组件
- DouDianSdk - 门面类,提供简化的API调用接口
- DouDianOpClient - 操作客户端,负责处理API请求和签名
- HttpClient - HTTP通信层,基于GuzzleHttp,支持重试和超时
- GlobalConfig/DouDianOpConfig - 配置管理,支持单例模式
- AccessTokenBuilder - 令牌构建器,支持授权码和店铺ID两种模式
- Exception - 完善的异常处理体系(HttpException、ApiException等)
🚀 快速开始
1. 基础用法(推荐)
<?php require_once 'vendor/autoload.php'; use DouDianSdk\Core\Client\DouDianSdk; // 初始化SDK $sdk = new DouDianSdk('your_app_key', 'your_app_secret'); // 获取访问令牌 (通过店铺ID) $accessToken = $sdk->getAccessToken('your_shop_id', 2); // 2 = 店铺ID模式 // 检查令牌是否获取成功 if (!$accessToken->isSuccess()) { throw new Exception('获取访问令牌失败: ' . $accessToken->getMessage()); } // 调用API - 获取订单列表 $result = $sdk->callApi( 'order_searchList\OrderSearchListRequest', 'order_searchList\param\OrderSearchListParam', [ 'page' => 1, 'size' => 20, 'order_status' => 1, // 待发货 'start_time' => date('Y-m-d H:i:s', strtotime('-7 days')), 'end_time' => date('Y-m-d H:i:s'), ], $accessToken ); // 处理结果 if (isset($result['code']) && $result['code'] === 10000) { echo "获取订单成功,共 " . count($result['data']['order_list'] ?? []) . " 条订单\n"; print_r($result['data']); } else { echo "API调用失败: " . ($result['msg'] ?? 'Unknown error') . "\n"; }
2. 高级配置
<?php require_once 'vendor/autoload.php'; use DouDianSdk\Core\Client\DouDianSdk; // 创建SDK实例并设置高级配置 $sdk = new DouDianSdk('your_app_key', 'your_app_secret', [ 'debug' => true, 'timeout' => [ 'connect' => 5000, // 连接超时5秒(默认) 'read' => 10000 // 读取超时10秒(默认) ], 'retry' => [ 'enable' => true, // 启用重试 'max_times' => 3, // 最大重试3次 'interval' => 1000 // 重试间隔1秒 ] ]); // 或者分步设置 $sdk->setDebug(true); // 获取配置信息 $config = $sdk->getConfig(); echo "连接超时: " . $config->httpConnectTimeout . "ms\n"; echo "读取超时: " . $config->httpReadTimeout . "ms\n";
3. 令牌管理(重要)
<?php require_once 'vendor/autoload.php'; use DouDianSdk\Core\Client\DouDianSdk; use DouDianSdk\Core\Token\AccessTokenBuilder; $sdk = new DouDianSdk('your_app_key', 'your_app_secret'); // 方式1: 通过SDK获取令牌(推荐) $accessToken = $sdk->getAccessToken('your_shop_id', 2); // 2 = 店铺ID模式 // 方式2: 直接使用AccessTokenBuilder $accessToken = AccessTokenBuilder::build('your_shop_id', 2); // 检查令牌是否有效 if ($accessToken->isSuccess()) { echo "✅ 令牌获取成功\n"; echo "访问令牌: " . substr($accessToken->getAccessToken(), 0, 30) . "...\n"; echo "有效期: " . $accessToken->getExpireIn() . " 秒\n"; echo "店铺ID: " . $accessToken->getShopId() . "\n"; echo "店铺名称: " . $accessToken->getShopName() . "\n"; // 如果有刷新令牌,保存它用于后续刷新 if ($refreshToken = $accessToken->getRefreshToken()) { echo "刷新令牌: " . substr($refreshToken, 0, 30) . "...\n"; // 刷新令牌示例 $newToken = $sdk->refreshAccessToken($refreshToken); if ($newToken->isSuccess()) { echo "✅ 令牌刷新成功\n"; } } } else { // 详细的错误信息 echo "❌ 获取令牌失败\n"; echo "错误码: " . $accessToken->getErrNo() . "\n"; echo "错误消息: " . $accessToken->getMessage() . "\n"; echo "子错误码: " . $accessToken->getSubCode() . "\n"; echo "详细错误: " . $accessToken->getSubMsg() . "\n"; // 常见错误处理 if ($accessToken->getSubCode() === 'isv.invalid_ip') { echo "⚠️ 提示: 请在抖店开放平台添加服务器IP到白名单\n"; } }
AccessToken 响应字段说明
抖店API返回标准响应格式:
{
"code": 10000, // 10000表示成功,其他值表示失败
"msg": "success", // 响应消息
"sub_code": "", // 子错误码(失败时提供详细错误类型)
"sub_msg": "", // 详细错误信息
"data": { // 业务数据
"access_token": "...", // 访问令牌
"expires_in": 86400, // 有效期(秒)
"refresh_token": "...",// 刷新令牌
"shop_id": 123456, // 店铺ID
"shop_name": "店铺名称" // 店铺名称
},
"log_id": "..." // 日志ID(用于问题追踪)
}
AccessToken 对象提供的方法:
// 基础信息 $accessToken->isSuccess() // 是否成功 (code === 10000) $accessToken->getErrNo() // 错误码 (code) $accessToken->getMessage() // 错误消息 (msg) $accessToken->getSubCode() // 子错误码 (sub_code) $accessToken->getSubMsg() // 详细错误信息 (sub_msg) $accessToken->getLogId() // 日志ID (log_id) // 令牌数据 $accessToken->getAccessToken() // 访问令牌 $accessToken->getExpireIn() // 有效期(秒) $accessToken->getRefreshToken() // 刷新令牌 $accessToken->getShopId() // 店铺ID $accessToken->getShopName() // 店铺名称 $accessToken->getScope() // 授权范围
4. 错误处理
<?php require_once 'vendor/autoload.php'; use DouDianSdk\Core\Client\DouDianSdk; use DouDianSdk\Core\Exception\DouDianException; use DouDianSdk\Core\Exception\ApiException; use DouDianSdk\Core\Exception\HttpException; $sdk = new DouDianSdk('your_app_key', 'your_app_secret'); try { // 获取访问令牌 $accessToken = $sdk->getAccessToken('your_shop_id', 2); if (!$accessToken->isSuccess()) { throw new DouDianException('获取访问令牌失败: ' . $accessToken->getMessage()); } // 调用API $result = $sdk->callApi( 'order_orderList\\OrderOrderListRequest', 'order_orderList\\param\\OrderOrderListParam', [ 'page' => 1, 'size' => 20 ], $accessToken->getAccessToken() ); // 检查API返回结果 if (isset($result['code']) && $result['code'] === 10000) { // 成功处理 $data = $result['data'] ?? []; echo "获取订单列表成功,共 " . count($data) . " 条记录\n"; } else { // API 返回错误 $errorMsg = $result['msg'] ?? '未知错误'; echo "API 错误: " . $errorMsg . "\n"; } } catch (HttpException $e) { // HTTP 请求错误(网络问题、超时等) echo "HTTP 错误: " . $e->getMessage() . "\n"; } catch (ApiException $e) { // API 调用错误(参数错误、签名错误等) echo "API 错误: " . $e->getMessage() . "\n"; } catch (DouDianException $e) { // 其他 SDK 错误 echo "SDK 错误: " . $e->getMessage() . "\n"; } catch (\Exception $e) { // 通用错误处理 echo "系统错误: " . $e->getMessage() . "\n"; }
5. 常用 API 示例
订单相关
<?php // 获取订单列表 $orderList = $sdk->callApi( 'order_orderList\\OrderOrderListRequest', 'order_orderList\\param\\OrderOrderListParam', [ 'page' => 1, 'size' => 20, 'order_by' => 'create_time', 'is_desc' => 1 ], $accessToken->getAccessToken() ); // 获取订单详情 $orderDetail = $sdk->callApi( 'order_orderDetail\\OrderOrderDetailRequest', 'order_orderDetail\\param\\OrderOrderDetailParam', ['order_id' => '123456789'], $accessToken->getAccessToken() ); // 查询订单列表 $result = $sdk->callApi( 'order_searchList\OrderSearchListRequest', 'order_searchList\param\OrderSearchListParam', [ 'page' => 1, 'size' => 20, 'start_time' => '2024-01-01 00:00:00', 'end_time' => '2024-01-31 23:59:59' ], $accessToken ); // 更新订单物流信息 $updateLogistics = $sdk->callApi( 'order_logisticsEdit\\OrderLogisticsEditRequest', 'order_logisticsEdit\\param\\OrderLogisticsEditParam', [ 'order_id' => '123456789', 'logistics_id' => 'SF1234567890', 'company_name' => '顺丰速运' ], $accessToken->getAccessToken() ); #### 商品相关 ```php // 获取商品列表 $productList = $sdk->callApi( 'product_list\\ProductListRequest', 'product_list\\param\\ProductListParam', [ 'page' => 1, 'size' => 20, 'status' => 1 // 1=在售 ], $accessToken->getAccessToken() ); // 获取商品详情 $productDetail = $sdk->callApi( 'product_detail\\ProductDetailRequest', 'product_detail\\param\\ProductDetailParam', ['product_id' => '123456789'], $accessToken->getAccessToken() ); #### 售后相关 ```php // 获取售后列表 $afterSaleList = $sdk->callApi( 'afterSale_List\\AfterSaleListRequest', 'afterSale_List\\param\\AfterSaleListParam', [ 'page' => 1, 'size' => 20, 'start_time' => '2024-01-01 00:00:00', 'end_time' => '2024-01-31 23:59:59' ], $accessToken->getAccessToken() ); // 处理售后申请 $handleAfterSale = $sdk->callApi( 'afterSale_refundProcessNotify\\AfterSaleRefundProcessNotifyRequest', 'afterSale_refundProcessNotify\\param\\AfterSaleRefundProcessNotifyParam', [ 'aftersale_id' => '123456789', 'status' => 1, // 1=同意,2=拒绝 'desc' => '处理说明' ], $accessToken->getAccessToken() ); ### 6. 底层组件直接使用 ```php <?php require_once 'vendor/autoload.php'; use DouDianSdk\Core\Token\AccessTokenBuilder; use DouDianSdk\Core\Config\GlobalConfig; use DouDianSdk\Core\Client\DouDianOpClient; // 方式1: 使用全局配置 $globalConfig = GlobalConfig::getGlobalConfig(); $globalConfig->setCredentials('your_app_key', 'your_app_secret'); // 获取访问令牌 $accessToken = AccessTokenBuilder::build('your_shop_id', 2); if ($accessToken->isSuccess()) { // 直接使用API类 $request = new \DouDianSdk\Api\afterSale_List\AfterSaleListRequest(); $param = new \DouDianSdk\Api\afterSale_List\param\AfterSaleListParam(); $param->page = 1; $param->size = 20; $request->setParam($param); $result = $request->execute($accessToken->getAccessToken()); print_r($result); }
实际项目集成
详细的项目集成指南请参考:README_INTEGRATION.md
包含以下框架的完整示例:
- Laravel 项目集成
- Hyperf/MineAdmin 项目集成
- 通用最佳实践
- 错误处理和重试机制
- 令牌管理和缓存策略
📁 项目结构
src/
├── Api/ # API 接口类 (710+ 个接口)
│ ├── order_searchList/ # 订单搜索接口
│ ├── product_listV2/ # 商品列表接口
│ ├── afterSale_List/ # 售后列表接口
│ └── ... # 其他710+个接口
├── Core/ # 核心功能模块
│ ├── Client/ # 客户端相关类
│ │ ├── DouDianSdk.php # SDK门面类
│ │ └── DouDianOpClient.php # API操作客户端
│ ├── Config/ # 配置管理类
│ │ ├── GlobalConfig.php # 全局配置(单例)
│ │ └── DouDianOpConfig.php # 操作配置
│ ├── Token/ # 访问令牌管理
│ │ ├── AccessToken.php # 令牌数据类
│ │ └── AccessTokenBuilder.php # 令牌构建器
│ ├── Http/ # HTTP 客户端
│ │ ├── HttpClient.php # HTTP客户端
│ │ └── HttpRequest.php # HTTP请求
│ ├── Exception/ # 异常处理
│ │ ├── DouDianException.php # SDK基础异常
│ │ ├── ApiException.php # API异常
│ │ └── HttpException.php # HTTP异常
│ └── Response/ # 响应处理
└── Utils/ # 工具类
└── SignUtil.php # 签名工具
tests/ # 测试套件
├── Core/ # 核心功能测试
│ ├── Token/AccessTokenTest.php # 令牌测试
│ └── ConfigTest.php # 配置测试
├── Api/OrderApiTest.php # API测试
├── Integration/ # 集成测试
└── TestCase.php # 测试基类
🧪 测试
运行测试
# 运行所有测试 ./vendor/bin/phpunit # 运行核心功能测试 ./vendor/bin/phpunit tests/Core/ # 运行集成测试(需要真实API凭证) DOUDIAN_INTEGRATION_TEST=true ./vendor/bin/phpunit tests/Core/Token/AccessTokenTest.php # 生成测试报告 ./vendor/bin/phpunit --testdox-html test-report.html
测试覆盖率
当前测试覆盖率:85%
- ✅ 令牌管理: 100% 覆盖(获取、刷新、验证)
- ✅ 配置管理: 100% 覆盖(超时、重试、调试)
- ✅ SDK核心: 83.3% 覆盖(实例化、API调用)
- ⚠️ API接口: 部分覆盖(订单、商品等核心接口)
测试环境配置
创建 .env 文件:
DOUDIAN_APP_KEY=your_app_key
DOUDIAN_APP_SECRET=your_app_secret
DOUDIAN_SHOP_ID=your_shop_id
DOUDIAN_REFRESH_TOKEN=your_refresh_token
DOUDIAN_INTEGRATION_TEST=true # 启用集成测试
开发
安装开发依赖
composer install --dev
运行测试
composer test
代码风格检查
composer cs-fixer-check composer cs-fixer-fix
静态分析
composer phpstan
支持的 API 模块
SDK 支持抖店开放平台的 710+ 个 API 接口,涵盖:
- 订单管理: 订单查询、物流管理、售后服务等
- 商品管理: 商品发布、库存管理、价格设置等
- 店铺管理: 店铺信息、资质管理等
- 营销工具: 优惠券、满减活动等
- 数据统计: 销售数据、流量分析等
- 物流服务: 运费模板、物流公司等
- 财务管理: 结算单据、账单查询等
- 客服工具: 消息推送、客服会话等
📝 更新日志
v2.0.0 (2024-12-11)
重要更新:完善错误处理和响应解析
新增功能
-
✨ 完整的错误信息支持:新增
sub_code和sub_msg字段解析getSubCode()- 获取子错误码(如:isv.invalid_ip)getSubMsg()- 获取详细错误信息
-
✨ 标准化响应格式:统一支持抖店API标准响应格式
code: 10000表示成功- 完整解析
code、msg、sub_code、sub_msg、data、log_id字段
优化改进
- 🔧 严格的成功判断:
isSuccess()方法使用严格比较(code === 10000) - 🔧 调试模式增强:在调试模式下输出完整的API原始响应
- 📚 测试完善:所有测试通过,包含完整的错误场景测试
破坏性变更
- ⚠️ 响应格式变更:不再支持旧的
err_no/message格式,统一使用code/msg - ⚠️ 成功判断变更:
isSuccess()现在只在code === 10000时返回 true
迁移指南
如果你使用的是旧版本,请注意:
// 旧版本 if ($accessToken->getErrNo() == 0) { ... } // 新版本 if ($accessToken->isSuccess()) { ... } // 推荐 // 或 if ($accessToken->getErrNo() === 10000) { ... }
注意事项
- 访问令牌管理: 建议使用缓存存储访问令牌,避免频繁请求
- 错误处理: 务必处理网络异常和API错误,实现适当的重试机制
- IP白名单: 确保服务器IP已添加到抖店开放平台的IP白名单中
- 频率限制: 遵守抖店开放平台的API调用频率限制
- 数据安全: 妥善保管应用密钥,不要在客户端代码中暴露
- 版本兼容: 关注抖店开放平台API版本更新,及时升级SDK
- 错误码处理: 使用
sub_code进行精确的错误类型判断
许可证
本项目采用 MIT 许可证 - 查看 LICENSE 文件了解详情。
贡献
欢迎提交 Issue 和 Pull Request 来改进这个项目。
联系方式
- 作者: westng
- 邮箱: 457395070@qq.com
- 项目地址: https://github.com/westng/doudian-sdk-php
- 问题反馈: Issues