Files
anxinyan/server-api/app/support/Kuaidi100Client.php
2026-05-26 17:08:33 +08:00

188 lines
6.0 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace app\support;
class Kuaidi100Client
{
private const QUERY_URL = 'https://poll.kuaidi100.com/poll/query.do';
private const SUBSCRIBE_URL = 'https://poll.kuaidi100.com/poll';
private const AUTONUMBER_URL = 'http://www.kuaidi100.com/autonumber/auto';
private const COMPANY_CATALOG_URL = 'http://api.kuaidi100.com/manager/openapi/download/kdbm.do';
public function __construct(private ?Kuaidi100ConfigService $configService = null)
{
$this->configService ??= new Kuaidi100ConfigService();
}
public function query(string $companyCode, string $trackingNo, string $phone = ''): array
{
$config = $this->configService->getConfig();
if (!$this->configService->isReadyForQuery()) {
throw new \RuntimeException('快递100实时查询配置未完成');
}
$param = [
'com' => $companyCode,
'num' => $trackingNo,
'resultv2' => '1',
];
if ($phone !== '') {
$param['phone'] = $phone;
}
$paramJson = $this->encodeJson($param);
$response = $this->postForm(self::QUERY_URL, [
'customer' => $config['customer'],
'sign' => strtoupper(md5($paramJson . $config['key'] . $config['customer'])),
'param' => $paramJson,
]);
$decoded = json_decode($response, true);
if (!is_array($decoded)) {
throw new \RuntimeException('快递100实时查询返回格式异常');
}
return $decoded;
}
public function recognize(string $trackingNo): array
{
$trackingNo = trim($trackingNo);
if ($trackingNo === '') {
throw new \InvalidArgumentException('快递100单号不能为空');
}
if (!$this->configService->isReadyForRecognition()) {
throw new \RuntimeException('快递100智能识别配置未完成');
}
$config = $this->configService->getConfig();
$response = $this->requestGet(self::AUTONUMBER_URL, [
'num' => $trackingNo,
'key' => $config['key'],
]);
$decoded = json_decode($response, true);
if (!is_array($decoded)) {
throw new \RuntimeException('快递100智能识别返回格式异常');
}
return $decoded;
}
public function downloadCompanyCatalogWorkbook(): string
{
return $this->requestGet(self::COMPANY_CATALOG_URL, [], 30, 15);
}
public function subscribe(string $companyCode, string $trackingNo, string $phone = ''): array
{
$config = $this->configService->getConfig();
if (!$this->configService->isReadyForSubscribe()) {
throw new \RuntimeException('快递100订阅配置未完成');
}
$parameters = [
'callbackurl' => $config['callback_url'],
'resultv2' => '1',
];
if ($config['callback_salt'] !== '') {
$parameters['salt'] = $config['callback_salt'];
}
if ($phone !== '') {
$parameters['phone'] = $phone;
}
$requestPayload = [
'company' => $companyCode,
'number' => $trackingNo,
'key' => $config['key'],
'parameters' => $parameters,
];
if ($companyCode === '') {
unset($requestPayload['company']);
$requestPayload['autoCom'] = '1';
}
$paramJson = $this->encodeJson($requestPayload);
$response = $this->postForm(self::SUBSCRIBE_URL, [
'schema' => 'json',
'param' => $paramJson,
]);
$decoded = json_decode($response, true);
if (!is_array($decoded)) {
throw new \RuntimeException('快递100订阅返回格式异常');
}
return $decoded;
}
private function postForm(string $url, array $fields): string
{
$ch = curl_init($url);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => http_build_query($fields),
CURLOPT_TIMEOUT => 8,
CURLOPT_CONNECTTIMEOUT => 4,
CURLOPT_HTTPHEADER => [
'Content-Type: application/x-www-form-urlencoded',
],
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$httpStatus = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errno) {
throw new \RuntimeException('快递100请求失败' . $error);
}
if ($httpStatus < 200 || $httpStatus >= 300) {
throw new \RuntimeException('快递100请求 HTTP 状态异常:' . $httpStatus);
}
return is_string($response) ? $response : '';
}
private function requestGet(string $url, array $query = [], int $timeout = 10, int $connectTimeout = 5): string
{
$fullUrl = $query ? $url . (str_contains($url, '?') ? '&' : '?') . http_build_query($query) : $url;
$ch = curl_init($fullUrl);
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPGET => true,
CURLOPT_TIMEOUT => $timeout,
CURLOPT_CONNECTTIMEOUT => $connectTimeout,
]);
$response = curl_exec($ch);
$errno = curl_errno($ch);
$error = curl_error($ch);
$httpStatus = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($errno) {
throw new \RuntimeException('快递100请求失败' . $error);
}
if ($httpStatus < 200 || $httpStatus >= 300) {
throw new \RuntimeException('快递100请求 HTTP 状态异常:' . $httpStatus);
}
return is_string($response) ? $response : '';
}
private function encodeJson(array $payload): string
{
$json = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if (!is_string($json)) {
throw new \RuntimeException('快递100请求参数编码失败');
}
return $json;
}
}