快递查询API

← 技术博客

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.billablesummary.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 分类处理异常。
  • 逐条判断 successCOURIER_PREPARING 不计费,看 billable 确认扣费。
  • Laravel 建议封装服务类;受限主机可退回纯 cURL。

字段与限制详见接口文档,快递编码见快递公司页。 免费额度每月 10000 次,无需企业认证。