You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
yanzong/vendor/overtrue/easy-sms/src/Gateways/BaiduGateway.php

174 lines
5.8 KiB

<?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));
}
}