初始上传
This commit is contained in:
174
vendor/overtrue/easy-sms/src/Gateways/BaiduGateway.php
vendored
Executable file
174
vendor/overtrue/easy-sms/src/Gateways/BaiduGateway.php
vendored
Executable file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of the overtrue/easy-sms.
|
||||
*
|
||||
* (c) overtrue <i@overtrue.me>
|
||||
*
|
||||
* This source file is subject to the MIT license that is bundled
|
||||
* with this source code in the file LICENSE.
|
||||
*/
|
||||
|
||||
namespace Overtrue\EasySms\Gateways;
|
||||
|
||||
use Overtrue\EasySms\Contracts\MessageInterface;
|
||||
use Overtrue\EasySms\Contracts\PhoneNumberInterface;
|
||||
use Overtrue\EasySms\Exceptions\GatewayErrorException;
|
||||
use Overtrue\EasySms\Support\Config;
|
||||
use Overtrue\EasySms\Traits\HasHttpRequest;
|
||||
|
||||
/**
|
||||
* Class BaiduGateway.
|
||||
*
|
||||
* @see https://cloud.baidu.com/doc/SMS/index.html
|
||||
*/
|
||||
class BaiduGateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
const ENDPOINT_HOST = 'smsv3.bj.baidubce.com';
|
||||
|
||||
const ENDPOINT_URI = '/api/v3/sendSms';
|
||||
|
||||
const BCE_AUTH_VERSION = 'bce-auth-v1';
|
||||
|
||||
const DEFAULT_EXPIRATION_IN_SECONDS = 1800; //签名有效期默认1800秒
|
||||
|
||||
const SUCCESS_CODE = 1000;
|
||||
|
||||
/**
|
||||
* Send message.
|
||||
*
|
||||
* @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to
|
||||
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
|
||||
* @param \Overtrue\EasySms\Support\Config $config
|
||||
*
|
||||
* @return array
|
||||
*
|
||||
* @throws \Overtrue\EasySms\Exceptions\GatewayErrorException ;
|
||||
*/
|
||||
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
|
||||
{
|
||||
$params = [
|
||||
'signatureId' => $config->get('invoke_id'),
|
||||
'mobile' => $to->getNumber(),
|
||||
'template' => $message->getTemplate($this),
|
||||
'contentVar' => $message->getData($this),
|
||||
];
|
||||
if (!empty($params['contentVar']['custom'])) {
|
||||
//用户自定义参数,格式为字符串,状态回调时会回传该值
|
||||
$params['custom'] = $params['contentVar']['custom'];
|
||||
unset($params['contentVar']['custom']);
|
||||
}
|
||||
if (!empty($params['contentVar']['userExtId'])) {
|
||||
//通道自定义扩展码,上行回调时会回传该值,其格式为纯数字串。默认为不开通,请求时无需设置该参数。如需开通请联系客服申请
|
||||
$params['userExtId'] = $params['contentVar']['userExtId'];
|
||||
unset($params['contentVar']['userExtId']);
|
||||
}
|
||||
|
||||
$datetime = gmdate('Y-m-d\TH:i:s\Z');
|
||||
|
||||
$headers = [
|
||||
'host' => self::ENDPOINT_HOST,
|
||||
'content-type' => 'application/json',
|
||||
'x-bce-date' => $datetime,
|
||||
];
|
||||
//获得需要签名的数据
|
||||
$signHeaders = $this->getHeadersToSign($headers, ['host', 'x-bce-date']);
|
||||
|
||||
$headers['Authorization'] = $this->generateSign($signHeaders, $datetime, $config);
|
||||
|
||||
$result = $this->request('post', self::buildEndpoint($config), ['headers' => $headers, 'json' => $params]);
|
||||
|
||||
if (self::SUCCESS_CODE != $result['code']) {
|
||||
throw new GatewayErrorException($result['message'], $result['code'], $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build endpoint url.
|
||||
*
|
||||
* @param \Overtrue\EasySms\Support\Config $config
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function buildEndpoint(Config $config)
|
||||
{
|
||||
return 'http://'.$config->get('domain', self::ENDPOINT_HOST).self::ENDPOINT_URI;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Authorization header.
|
||||
*
|
||||
* @param array $signHeaders
|
||||
* @param int $datetime
|
||||
* @param \Overtrue\EasySms\Support\Config $config
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateSign(array $signHeaders, $datetime, Config $config)
|
||||
{
|
||||
// 生成 authString
|
||||
$authString = self::BCE_AUTH_VERSION.'/'.$config->get('ak').'/'
|
||||
.$datetime.'/'.self::DEFAULT_EXPIRATION_IN_SECONDS;
|
||||
|
||||
// 使用 sk 和 authString 生成 signKey
|
||||
$signingKey = hash_hmac('sha256', $authString, $config->get('sk'));
|
||||
// 生成标准化 URI
|
||||
// 根据 RFC 3986,除了:1.大小写英文字符 2.阿拉伯数字 3.点'.'、波浪线'~'、减号'-'以及下划线'_' 以外都要编码
|
||||
$canonicalURI = str_replace('%2F', '/', rawurlencode(self::ENDPOINT_URI));
|
||||
|
||||
// 生成标准化 QueryString
|
||||
$canonicalQueryString = ''; // 此 api 不需要此项。返回空字符串
|
||||
|
||||
// 整理 headersToSign,以 ';' 号连接
|
||||
$signedHeaders = empty($signHeaders) ? '' : strtolower(trim(implode(';', array_keys($signHeaders))));
|
||||
|
||||
// 生成标准化 header
|
||||
$canonicalHeader = $this->getCanonicalHeaders($signHeaders);
|
||||
|
||||
// 组成标准请求串
|
||||
$canonicalRequest = "POST\n{$canonicalURI}\n{$canonicalQueryString}\n{$canonicalHeader}";
|
||||
|
||||
// 使用 signKey 和标准请求串完成签名
|
||||
$signature = hash_hmac('sha256', $canonicalRequest, $signingKey);
|
||||
|
||||
// 组成最终签名串
|
||||
return "{$authString}/{$signedHeaders}/{$signature}";
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成标准化 http 请求头串.
|
||||
*
|
||||
* @param array $headers
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getCanonicalHeaders(array $headers)
|
||||
{
|
||||
$headerStrings = [];
|
||||
foreach ($headers as $name => $value) {
|
||||
//trim后再encode,之后使用':'号连接起来
|
||||
$headerStrings[] = rawurlencode(strtolower(trim($name))).':'.rawurlencode(trim($value));
|
||||
}
|
||||
|
||||
sort($headerStrings);
|
||||
|
||||
return implode("\n", $headerStrings);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 指定的 keys 过滤应该参与签名的 header.
|
||||
*
|
||||
* @param array $headers
|
||||
* @param array $keys
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getHeadersToSign(array $headers, array $keys)
|
||||
{
|
||||
return array_intersect_key($headers, array_flip($keys));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user