PHP 对接快递查询API:Guzzle 客户端与异常处理
2026-07-26 · 阅读约 7 分钟
PHP 里调快递查询API,主流做法是用 Guzzle 这个 HTTP 客户端:数组直接转 JSON、 响应流方便读、异常体系清晰。本文以 Guzzle 为主线讲完整流程,最后给一份不依赖 Composer 的纯 cURL 版本, 方便在受限主机上用。
一、安装 Guzzle
先注册拿密钥(邮箱注册,不需要企业认证):控制台生成 pk_ 开头的 API Key 和
sk_ 开头的 Secret Key。然后装 Guzzle:
composer require guzzlehttp/guzzle 二、发送查询请求
接口是 POST /v1/tracking/trace。认证头是把两段密钥用英文冒号拼成
Bearer pk_xxx:sk_xxx。Guzzle 的 json 选项会自动把数组序列化并设好 Content-Type:
<?php
require 'vendor/autoload.php';
use GuzzleHttp\Client;
$apiKey = getenv('KUAIDI_KEY'); // pk_ 开头
$secret = getenv('KUAIDI_SECRET'); // sk_ 开头
$client = new Client([
'base_uri' => 'https://api.kuaidichaxunapi.com',
'timeout' => 10,
]);
$response = $client->post('/v1/tracking/trace', [
'headers' => [
'Authorization' => 'Bearer ' . $apiKey . ':' . $secret,
'Content-Type' => 'application/json',
],
'json' => [
'items' => [
['courierCode' => 'sf', 'trackingNumber' => 'SF9900001234567'],
],
],
]);
$body = json_decode((string) $response->getBody(), true); items 一次最多 10 条。json_decode(..., true) 的第二个参数为 true,
把 JSON 解析成关联数组,后面用起来顺手。
三、读取返回数据
返回是统一信封:$body['data']['results'] 与请求顺序一一对应,
另有 summary 汇总。逐条先看 success:
$results = $body['data']['results'];
$summary = $body['data']['summary']; // total / successful / failed / billable
foreach ($results as $tiao) {
if ($tiao['success']) {
$d = $tiao['data'];
echo $d['trackingNumber'] . ' → ' . $d['deliveryStatusText'] . PHP_EOL;
// progresses 最新在前
foreach ($d['progresses'] as $p) {
echo ' ' . $p['dateTime'] . ' ' . $p['statusCode'] . ' ' . $p['description'] . PHP_EOL;
}
} else {
$err = $tiao['error'];
echo '失败 ' . $err['trackingNumber'] . ': ' . $err['code'] . PHP_EOL;
}
} deliveryStatus 是归一后的状态码(共 12 种),deliveryStatusText 已是对应中文,
可直接展示,省去自己维护映射表。失败条目里 COURIER_PREPARING(该公司查询开发中)
不计费,NOT_FOUND(暂无轨迹)会计费——看 error.billable 或
summary.billable 即知实际扣了几条。
四、异常处理(4xx / 5xx)
默认情况下 Guzzle 遇到 4xx/5xx 会抛异常。按类型分开捕获,才能对症处理:
use GuzzleHttp\Exception\ClientException; // 4xx
use GuzzleHttp\Exception\ServerException; // 5xx
use GuzzleHttp\Exception\ConnectException; // 网络/超时
try {
$response = $client->post('/v1/tracking/trace', [/* 同上 */]);
$body = json_decode((string) $response->getBody(), true);
} catch (ClientException $e) {
$status = $e->getResponse()->getStatusCode();
echo '请求被拒绝 HTTP ' . $status; // 401 密钥错误 / 400 参数缺失
} catch (ServerException $e) {
echo '服务端异常,请稍后重试';
} catch (ConnectException $e) {
echo '连接超时,建议做指数退避后重试';
}
分类要点:ClientException(4xx)多半是密钥或请求体的问题,重试无益,先查
Authorization 头和 items 结构;ServerException(5xx)和
ConnectException(超时)才值得退避重试。
五、在 Laravel 里封装成服务类
Laravel 项目里,把密钥写进 config/services.php,再包一个服务类,业务代码只调 trace():
// config/services.php
'kuaidi' => [
'key' => env('KUAIDI_KEY'),
'secret' => env('KUAIDI_SECRET'),
], <?php
namespace App\Services;
use GuzzleHttp\Client;
class KuaidiClient
{
private Client $http;
public function __construct()
{
$this->http = new Client(['base_uri' => 'https://api.kuaidichaxunapi.com', 'timeout' => 10]);
}
/** @param array $items 最多 10 个,每个含 courierCode 与 trackingNumber */
public function trace(array $items): array
{
$auth = 'Bearer ' . config('services.kuaidi.key')
. ':' . config('services.kuaidi.secret');
$res = $this->http->post('/v1/tracking/trace', [
'headers' => ['Authorization' => $auth, 'Content-Type' => 'application/json'],
'json' => ['items' => $items],
]);
return json_decode((string) $res->getBody(), true)['data']['results'];
}
}
之后在控制器里用构造注入拿到 KuaidiClient,调用
$client->trace([['courierCode' => 'zto', 'trackingNumber' => '78000000012345']]) 即可,
密钥与 HTTP 细节都被收进服务类。
六、无 Composer 环境:纯 cURL 版
有些共享主机装不了 Guzzle,用 PHP 自带的 cURL 扩展也能完成同样的请求:
<?php
$apiKey = getenv('KUAIDI_KEY');
$secret = getenv('KUAIDI_SECRET');
$ch = curl_init('https://api.kuaidichaxunapi.com/v1/tracking/trace');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 10,
CURLOPT_HTTPHEADER => [
'Authorization: Bearer ' . $apiKey . ':' . $secret,
'Content-Type: application/json',
],
CURLOPT_POSTFIELDS => json_encode([
'items' => [
['courierCode' => 'zto', 'trackingNumber' => '78000000012345'],
],
]),
]);
$raw = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$tiao = json_decode($raw, true)['data']['results'][0];
echo $tiao['success'] ? $tiao['data']['deliveryStatusText'] : $tiao['error']['code'];
} 小结
- 认证是一个头:
Bearer pk_xxx:sk_xxx,密钥放服务端配置,别进版本库。 - Guzzle 用
json选项发数组;按ClientException/ServerException分类处理异常。 - 逐条判断
success;COURIER_PREPARING不计费,看billable确认扣费。 - Laravel 建议封装服务类;受限主机可退回纯 cURL。