travel/service/app/server/DyApiService.php

260 lines
8.9 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\server;
use app\model\Orders;
use app\model\ThirdMobileLogs;
use http\Client;
use support\Log;
use support\Redis;
use think\Exception;
/**
* 抖音接口
*/
class DyApiService {
// 生成 client_token
CONST CLIENT_TOKEN = '/oauth/client_token/';
// 确认接单
CONST ORDER_CONFIRM = '/goodlife/v1/trip/trade/travelagency/order/confirm/';
// 订单POI信息查询接口
CONST POI_QUERY = '/goodlife/v1/trip/trade/travelagency/order/poi/query/';
// redis key
CONST DY_TOKEN_KEY = 'dy_client_key';
// 请求地址
private $host;
private $app_id;
// 加密密钥
private $app_secret;
/**
* 构造函数.
*/
public function __construct() {
$this->host = env('DY_API_URL');
$this->app_id = env('DY_APPID');
$this->app_secret = env('DY_APPSECRET');
}
/**
* @param $url
* @param array $data
* @param array $extra
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function send($url, array $data = [], $extra = []): array {
$signData = [
'Api-App-Key' => $this->app_id,
'Api-Time-Stamp' => time() * 1000,
];
// 组装请求数据
$reqData = $data;
// 请求头加上签名
// $header['Api-Sign'] = $this->getSign($signData, $reqData);
$header['access-token'] = $this->getAccessToken();
if ($this->app_id == 'sandbox289') {
$header['x-sandbox-token'] = 1;
}
return $this->request($url, $reqData, $header, $extra);
}
/**
* 获取token
* @return bool|mixed|string
* @throws Exception
* @throws \GuzzleHttp\Exception\GuzzleException
*/
private function getAccessToken() {
// 测试token
if ($this->app_id == 'sandbox289') {
return 'ka.F9tiX0EN5QB5plK43W22p3cDgji9O9ZTAigryyd1A7AlbDwTmDxEN0PwjfI9';
}
$key = self::DY_TOKEN_KEY;
if ($token = Redis::get($key)) {
return $token;
}
$reqData = [
'client_key' => $this->app_id,
'client_secret' => $this->app_secret,
'grant_type' => 'client_credential',
];
$res = $this->request(self::CLIENT_TOKEN, $reqData);
if (!$res['flag'] || !isset($res['data']['access_token'])) {
throw new Exception('获取token失败');
}
Redis::setEx($key, $res['data']['expires_in'], $res['data']['access_token']);
return $res['data']['access_token'];
}
/**
* 获取签名
*/
public function getSign($signData, $postData): string {
// $signData = array_merge($signData, $postData);
$buff = "";
ksort($signData);
// 将key和value按照顺序直接拼接到一起
foreach ($signData as $k => $v){
if (!empty($v)) {
if (is_array($v)) {
$v = json_encode(ksort($v), JSON_UNESCAPED_UNICODE);
}
$buff .= $k . $v;
}
}
// 签名步骤三在上一步的结果后直接拼secretKey
$str = $buff . $this->app_secret;
// 在上一步结果后直接拼接,经过去除空白字符(\s)的HTTP Body原始字符串
$str .= preg_replace('/\s+/', '', json_encode($postData));
// 对上一步结果进行sha1加密得到16进制字符串
$str = sha1($str);
// 对上一步结果进行md5加密得到16进制字符串
// 将上一步结果转为大写
return strtoupper(md5($str));
}
/**
* @param string $url
* @param array $data
* @param array $header
* @param array $extra
* @return array
* @throws \GuzzleHttp\Exception\GuzzleException
*/
private function request(string $url, array $data = [], $header = []): array {
$client = new \GuzzleHttp\Client();
try {
$reqUrl = $this->host . $url;
$header['Content-Type'] = 'application/json';
$header['charset'] = 'UTF-8';
$options['headers'] = $header;
$options['json'] = $data;
$method = 'POST';
$response = $client->request($method, $reqUrl, $options);
Log::info(sprintf('dy url:%s, req:%s, response:%s', $reqUrl, json_encode($data), $response->getBody())) ;
$result = ['code' => $response->getStatusCode(), 'body' => json_decode($response->getBody(), true, 512, JSON_BIGINT_AS_STRING)];
if (isset($result['code']) && $result['code'] == 200) {
if ($result['body']['data']['error_code'] == '0') {
return ['flag' => true, 'data' => $result['body']['data'] ?? [], 'message' => $result['body']['data']['description '] ?? ''];
} else {
return ['flag' => false, 'data' => $result['body']['data'] ?? [], 'message' => $result['body']['data']['description '] ?? ''];
}
}
return ['flag' => false, 'message' => $result['body']['data']['description'] ?? '', 'data' => []];
} catch (\Exception $exception) {
Log::info(sprintf('dy req fail:%s, req:%s,res:%s', $exception->getMessage(), $exception->getFile(), $exception->getLine())) ;
return ['flag' => false, 'message' => $exception->getMessage(), 'data' => []];
}
}
/**
1.根据ClientKev找到ClientSecret将ClientSecret向左右使用字符补齐32位/裁剪至32位补齐:补位字符:#先补左侧再补右侧再补左侧………直到补满32位。
裁剪:先裁剪左侧再裁右侧再裁左侧………直到剩余32位。(正常不需要补齐secret默认为32位此举是为了
以防万一)
2.将ClientSecret作为Key,右侧16位为向量IV
3.将密文进行base64解码。
4.使用AES-256-CBC模式解密解码后的密文对齐使用PKCS5Padding方式
* @param $cipherText
* @param $clientSecret
* @return false|string
*/
public function decrypt($cipherText, $clientSecret) {
Log::info('$cipherText:' . $cipherText);
// 补齐或裁剪 ClientSecret 为 32 位
$clientSecret = $this->padOrTruncate($clientSecret);
Log::info('$cipherText step 2:' . $clientSecret);
// 使用 ClientSecret 作为密钥
$key = $clientSecret;
// 右侧16位为初始化向量IV
$iv = substr($key, 16, 16);
// 将密文 base64 解码
$cipherTextDecoded = base64_decode($cipherText);
// 使用 AES-256-CBC 解密
return openssl_decrypt($cipherTextDecoded, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv);
}
public function encrypt($cipherText, $clientSecret) {
Log::info('$cipherText:' . $cipherText);
// 补齐或裁剪 ClientSecret 为 32 位
$clientSecret = $this->padOrTruncate($clientSecret);
Log::info('$cipherText step 2:' . $clientSecret);
// 使用 ClientSecret 作为密钥
$key = $clientSecret;
// 右侧16位为初始化向量IV
$iv = substr($key, 16, 16);
// 使用 AES-256-CBC 解密
return base64_encode(openssl_encrypt($cipherText, 'AES-256-CBC', $key, OPENSSL_RAW_DATA, $iv));
}
/**
* @param $clientSecret
* @return false|string
*/
private function padOrTruncate($clientSecret) {
// 1. 如果 clientSecret 长度为32直接返回
if (strlen($clientSecret) == 32) {
return $clientSecret;
}
// 2. 如果 length < 32补齐至32位
if (strlen($clientSecret) < 32) {
// 补齐过程先补左侧再补右侧再补左侧直到长度为32
$paddingChar = '#';
$padded = $clientSecret;
// 左侧补齐
$left = true;
while (strlen($padded) < 32) {
if ($left) {
$padded = $paddingChar . $padded;
$left = false;
} else {
$padded = $padded . $paddingChar;
$left = true;
}
}
return substr($padded, 0, 32); // 确保返回32位
}
// 3. 如果 length > 32裁剪至32位
if (strlen($clientSecret) > 32) {
// 裁剪过程先裁左侧再裁右侧再裁左侧直到长度为32
$cropped = $clientSecret;
// 左侧裁剪
while (strlen($cropped) > 32) {
$cropped = substr($cropped, 1);
}
// 右侧裁剪
while (strlen($cropped) > 32) {
$cropped = substr($cropped, 0, -1);
}
// 左侧裁剪(再一次)
while (strlen($cropped) > 32) {
$cropped = substr($cropped, 1);
}
return substr($cropped, 0, 32); // 确保返回32位
}
return $clientSecret;
}
}