我一直在尝试使用 cURL PHP 调用停车费/计算 API,并且我能够获得 200 状态代码,这意味着它到达了服务器。但不知何故,即使我收到 200 状态代码,JSON 数据也没有显示。我得出的结论是这是一个身份验证问题,但我无法真正弄清楚我哪里出了问题。
我已经联系了海康威视的技术支持,但他们在该期限内没有提供帮助,并一直建议我使用 HCP 下载附带的 CSHCSDKDemo 应用程序。看来他们无法理解我的要求。我所附的图片是我收到的错误,它也显示状态代码 200。任何帮助将不胜感激,谢谢。
一开始,我不断收到 HTTP failed request: 400 status code 这样的错误,但是当我更改 headers 参数时,我终于得到了 200 状态码,这意味着成功调用了 API,但附带的 JSON 数据没有显示。而且,在文档中,它提到我需要使用计算签名所以我不确定我哪里出错了。
此 API 的文档链接是:
https://pinfo.hikvision.com/hkwsen/unzip/20240201150721_74009_doc/
API URL 格式为:
https://[serverAddress]:[serverPort]/artemis/api/vehicle/v1/parkingfee/calculate
以下是如何计算签名的说明: 签名计算
以下是完整代码:
<?php
$apiUrl = 'https://127.0.0.1:443/artemis/api/vehicle/v1/parkingfee/calculate'; // Replace with actual endpoint
$urlEndpoint = '/api/vehicle/v1/parkingfee/calculate';
$httpMethod = 'POST';
$accept = 'application/json';
$acceptEncoding = 'gzip, deflate, sdch';
$acceptLanguage = 'en-US,en;q=0.8';
$connection = 'keep-alive';
$contentType = 'application/json;charset=UTF-8';
$x_requested_with = 'XMLHttpRequest';
$server = '-';
$host = '127.0.0.1';
$transferEncoding = 'chunked';
$x_application_context = '';
$plateLicense = '';
$userAgent = 'Chrome/61.0.3163.100';
$apiKey = '';
$apiSecret = '';
/******************************************************* CONTENT-MD5 ************************************************************************** */
$requestBody = '';
if(isset($_POST['submitform'])){
$apiKey = $_POST['apikey'];
$apiSecret = $_POST['apisecret'];
$apiUrl = $_POST['apiurl'];
$requestBody = $_POST['jsonbody'];
}
// $requestBody = array(
// 'userId' => 'admin',
// //'plateLicense' => 'UKM9999');
// 'plateLicense' => $plateLicense);
$jsonRequestBody = json_encode($requestBody, JSON_UNESCAPED_UNICODE);
$contentMD5 = base64_encode(md5($jsonRequestBody, true));
$contentLength = strlen($requestBody);
/******************************************************* RESPONSE BODY ************************************************************************** */
//response body format
$responseBody = array(
'code' => 0,
'msg' => 'Success',
'data' => array(
'plateLicense' => 'UKM9999',
'cardNum' => '',
'parkingInTime' => '2024-06-13T14:22:01+08:00',
'parkingDuration' => 437809,
'feeRuleType' => 0,
'feeRuleIndexCode' => '1',
'feeRuleName' => 'test rule',
'fee' => '00.00'));
//encode the above in json format
$jsonResponseBody = json_encode($responseBody, JSON_UNESCAPED_UNICODE);
$jsonDecoded = json_decode($jsonResponseBody, true);
/******************************************************* UUID/X-CA-NONCE *********************************************************************** */
function generateUUID() {
$data = openssl_random_pseudo_bytes(16);
assert(strlen($data) === 16);
$data[6] = chr(ord($data[6]) & 0x0f | 0x40); // Set version to 0100
$data[8] = chr(ord($data[8]) & 0x3f | 0x80); // Set bits 6-7 to 10
return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
}
$x_ca_nonce = generateUUID();
/******************************************************* TIMESTAMP ************************************************************************** */
$dateString = strtotime("Y-M-D h:i:sa");
$dateTime = new DateTime($dateString);
$timestampInSeconds = $dateTime->getTimestamp();
$timestampInMilliseconds = $timestampInSeconds * 1000;
$timestampInMilliseconds2 = round(microtime(true) * 1000);
// Initialize cURL session
$ch = curl_init();
/******************************************************* SIGNATURE CALCULATION ***************************************************************** */
$stringToSign = "{$httpMethod}\n". "{$contentMD5}\n". "{$contentType}\n". "{$apiKey}\n". "{$x_ca_nonce}\n". "{$timestampInMilliseconds}\n". "{$urlEndpoint}";
// Generate Signature
$signature = hash_hmac('sha256', $stringToSign, $apiSecret, true);
// Encode Signature (base64)
$base64encodedSignature = base64_encode($signature);
/******************************************************* CURL SETOPT ************************************************************************** */
// Set cURL options
curl_setopt($ch, CURLOPT_URL, $apiUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTP_VERSION, 'CURL_HTTP_VERSION_1_1');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, TRUE);
curl_setopt($ch, CURLOPT_VERBOSE, TRUE);
curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
curl_setopt($ch, CURLOPT_POST, 1); //set request method to post
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Authorization: Basic YWRtaW46V2VtYWwzMjEh', // Adjust according to API's authentication requirements
'Accept: '. $accept.'*/*',
'Accept-Encoding: '. $acceptEncoding,
'Accept-Language: '. $acceptLanguage,
'Connection: '. $connection,
'Content-Length: '. $contentLength,
'Content-MD5: '. $contentMD5,
'Content-Type: '. $contentType,
'Host: '. $host,
'Server: '. $server,
'transferEncoding: '. $transferEncoding, //cant make it like others, will display 400 error again, like Transfer-Encoding
'X-CA-Key: ' . $apiKey,
'X-CA-Signature: ' . $base64encodedSignature,
'X-CA-Signature-Headers: X-CA-Key, X-CA-Timestamp',
'X-CA-Timestamp: ' . $timestampInMilliseconds,
'X-CA-Nonce: ' . $x_ca_nonce,
'userId: admin',
'X-Requested-With: '. $x_requested_with
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
// Execute the request
$response = curl_exec($ch);
// Close the cURL session
curl_close($ch);
?>
<!-- Form Request API -->
<!DOCTYPE html>
<head>
<title>Test HikVision API</title>
<style>
body {
color:rgb(220, 241, 208);
background-color: rgb(44, 42, 62);
}
.container {
display: flex;
justify-content: center;
align-items: center;
/*height: 100vh;*/
margin-top: 100px;
margin-bottom: 80px;
}
.container2 {
display: flex;
justify-content: center;
align-items: center;
}
.container3 {
display: flex;
justify-content: center;
/* align-items: center; */
/*height: 100vh;*/
margin-top: 50px;
margin-bottom: 80px;
}
.textlarge {
height: 200px;
width: 200px;
}
</style>
</head>
<body>
<div class="container">
<h1>Hikvision Open API Test </h1>
<div class="container2">
<form name="submitrequest" action="" method="post">
<label for="APPKey">APP Key: </label><br>
<input required type="text" name="apikey" placeholder="APP key"><br><br>
<label for="APPSecret">APP Secret: </label><br>
<input required type="text" name="apisecret" placeholder="APP secret"><br><br>
<label for="APIurl">API: </label><br>
<input required type="text" name="apiurl" placeholder="URL"><br><br>
<label for="Body">Body: </label><br>
<input type="text" class="textlarge" name="jsonbody"placeholder="Request Body"><br><br>
<input type="submit" value="Submit" name="submitform">
</form>
</div>
</div>
<div class="container3">
<h1>Results</h1>
</div>
<div class="container3">
<?php
// Check for errors
if(isset($_POST['submitform'])){
if ($response === false) {
echo 'cURL error: ' . curl_error($ch);
} else {
// Decode the JSON response
$responseData = json_decode($response, false, 512, JSON_UNESCAPED_UNICODE);
// $responseData = json_decode($response, true);
// echo $responseData;
// encoded response
echo "Encoded response body: " . $response . "<br><br><br>";
echo "Decoded response body: ";
if($responseData === NULL) echo "No decoded JSON response. <br><br><br>";
else echo $responseData . "<br><br><br>";
$resultStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($resultStatus == 200) {
// everything went better than expected
echo "Status Code: ".$resultStatus."<br><br><br>";
print_r($responseData);
} else {
// the request did not complete as expected. common errors are 4xx
// (not found, bad request, etc.) and 5xx (usually concerning
// errors/exceptions in the remote script execution)
die('Request failed: HTTP status code: ' . $resultStatus);
}
// Process the response data
if ($responseData === null && json_last_error() !== JSON_ERROR_NONE) {
// echo 'cURL Error: ' . curl_error($ch);
echo "Error decoding JSON response: " . json_last_error_msg();
} elseif ($responseData === null) {
echo "The response body is empty!";
} else {
// Everything went as expected
echo "HTTP status code: $resultStatus\n";
print_r($responseData);
}
// print_r($_POST);
//example if want json to print pretty
// echo json_encode($this->errors, JSON_PRETTY_PRINT);
}
}
?>
</div>
</body>
</html>
我期待它给出这样的 JSON 响应:
{
"code": "0",
"msg": "Success",
"data": {
"plateLicense": "UKM9999",
"cardNum": "",
"parkingInTime": "2024-06-13T14:22:01+08:00",
"parkingDuration": 2223046,
"feeRuleType": 0,
"feeRuleIndexCode": "1",
"feeRuleName": "test rule",
"fee": "260.00"
}
}
我不确定错误是来自签名计算还是头部。但我收到的错误显示:
状态代码:200 解码 JSON 响应时出错:控制字符 错误,可能编码不正确
更新: 我已在这篇文章中附加了一张图片,并编辑了curl 请求并添加了完整的代码。 API调用错误
我已经找到了问题的解决方案。首先,我删除了不必要的标头,只保留 Connection、Accept、Content-Type、App Key 和签名作为 CURLOPT_HTTPHEADER 中的标头。然后,我修复了签名计算。我还将文本框中查询的plateLicense 作为数组传递,然后将其编码为 JSON,而不是传递整个 JSON 正文(这也可以工作,但为了测试,我更喜欢将其作为数组传递)。下面的代码是我在 PHP 文件中更改的内容:
//Header
curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Connection: '. $connection,
'Accept: '. $accept,
'Content-Type: '. $contentType,
'X-Ca-Key: '. $apiKey,
'X-Ca-Signature: '. $signature
]);
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonRequestBody);
//Signature calculation
function generateHmacSha256Signature($jsonRequestBody, $apiSecret) {
return hash_hmac('sha256', $jsonRequestBody, $apiSecret, true);
}
希望这个答案可以帮助遇到同样问题的人!