sms
sms copied to clipboard
测试发现百度云已经不支持了?
简单测试了一下百度云的短信好像不支持了,看了一下驱动源码,用了很老的接口,百度云已经更新至V3了,可能还是可以用,我的测试方法没对 然后我感觉如果要实现动态配置实现多平台,多签名,多模板用这类的集合组件可能不是一个很好的选择
简单的改成V3支持方法:
1、config\autoload\sms.php增加v3的配置,ak、sk我放在了env中设置,也可以直接写,但是不要提交到托管平台避免泄露
// ……其他配置
'baidu_cloud_v3' => [
'driver' => \cms\sms\drivers\BaiduCloudV3Driver::class,
'config' => [
'ak' => env('SMS_BAIDU_CLOUD_V3_AK', ''),
'sk' => env('SMS_BAIDU_CLOUD_V3_SK', ''),
'signature_id' => env('SMS_BAIDU_CLOUD_V3_SIGN', ''), //公用的短信签名
],
],
2、创建百度云短信v3的驱动,内容跟原驱动一模一样,只是把接口地址,参数修改了,也可以做在配置中,我这里直接写到代码中了,因为一般不会换,换了就意味着版本升级,之前的代码肯定不能用(文件位置随便放,能够加载就行)
<?php
declare(strict_types=1);
namespace cms\sms\drivers;
use HyperfExt\Sms\Contracts\SmsableInterface;
use HyperfExt\Sms\Drivers\AbstractDriver;
use HyperfExt\Sms\Exceptions\DriverErrorException;
class BaiduCloudV3Driver extends AbstractDriver
{
protected const ENDPOINT_HOST = 'smsv3.bj.baidubce.com';
protected const ENDPOINT_URI = '/api/v3/sendSms';
protected const BCE_AUTH_VERSION = 'bce-auth-v1';
protected const DEFAULT_EXPIRATION_IN_SECONDS = 1800; //签名有效期默认1800秒
protected const SUCCESS_CODE = 1000;
public function send(SmsableInterface $smsable): array
{
$params = [
// 手机号码
'mobile' => $smsable->to->getNationalNumber(),
// 短信模板
'template' => $smsable->template,
// 签名
'signatureId' => $smsable->signature ?: $this->config->get('signature_id'),
// 模板变量
'contentVar' => $smsable->data,
// 自定义参数,暂不支持
// 'custom' => '',
// 自定义扩展码,暂不支持
// 'userExtId' => '',
];
$datetime = gmdate('Y-m-d\TH:i:s\Z');
$headers = [
'host' => self::ENDPOINT_HOST,
'content-type' => 'application/json',
'x-bce-date' => $datetime,
// 'x-bce-content-sha256' => hash('sha256', json_encode($params)),
];
//获得需要签名的数据
$signHeaders = $this->getHeadersToSign($headers, ['host', 'x-bce-date']);
$headers['Authorization'] = $this->generateSign($signHeaders, $datetime);
$response = $this->client->postJson($this->buildEndpoint(), $params, $headers);
$result = $response->toArray();
if ($result['code'] != self::SUCCESS_CODE) {
throw new DriverErrorException($result['message'], $result['code'], $response);
}
return $result;
}
protected function buildEndpoint(): string
{
return 'http://' . $this->config->get('domain', self::ENDPOINT_HOST) . self::ENDPOINT_URI;
}
protected function generateSign(array $signHeaders, string $datetime): string
{
// 生成 authString
$authString = self::BCE_AUTH_VERSION . '/' . $this->config->get('ak') . '/'
. $datetime . '/' . self::DEFAULT_EXPIRATION_IN_SECONDS;
// 使用 sk 和 authString 生成 signKey
$signingKey = hash_hmac('sha256', $authString, $this->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}";
}
protected function getCanonicalHeaders(array $headers): string
{
$headerStrings = [];
foreach ($headers as $name => $value) {
//trim后再encode,之后使用':'号连接起来
$headerStrings[] = rawurlencode(strtolower(trim($name))) . ':' . rawurlencode(trim($value));
}
sort($headerStrings);
return implode("\n", $headerStrings);
}
protected function getHeadersToSign(array $headers, array $keys): array
{
return array_intersect_key($headers, array_flip($keys));
}
}
3、使用方式与文档中的示例一致,不过经实测发现,百度云的短信就算是普通营销短信,模板变量也必须要传?随便传一个都可以,不传要报错
控制器中示例:
<?php
declare(strict_types=1);
namespace cms\sms\api_controller;
use cms\sms\service\VerificationCodeService;
use Hyperf\HttpServer\Annotation\AutoController;
use HyperfExt\Sms\Sms;
/**
* Class TestController
* @package cms\sms\api_controller
* @AutoController(prefix="api/sms/test")
*/
class TestController extends CommonController
{
public function t001()
{
$a = Sms::sender('baidu_cloud_v3')->to('1898047xxxx', '86')->send(new VerificationCodeService());
var_dump($a);
return 'ok';
}
}
消息类中示例(这里要注意,使用gen:sms命令生成的代码默认使用了队列,根据自己情况来):
<?php
declare(strict_types=1);
/**
* This file is part of hyperf-ext/sms.
*
* @link https://github.com/hyperf-ext/sms
* @contact [email protected]
* @license https://github.com/hyperf-ext/sms/blob/master/LICENSE
*/
namespace cms\sms\service;
// use HyperfExt\Contract\ShouldQueue;
use HyperfExt\Sms\Contracts\SenderInterface;
use HyperfExt\Sms\Smsable;
class VerificationCodeService extends Smsable
{
/**
* Create a new SMS message instance.
*/
public function __construct()
{
//
}
/**
* Build the SMS message.
*/
public function build(SenderInterface $sender): void
{
// 不带签名使用配置
// $this->template('sms-tmpl-TuEOTX86xxxx')->with(['name' => 'awen']);
// 带签名
$this->template('sms-tmpl-ZHFXcnxxx')->with(['name' => 'awen'])->signature('sms-sign-RmFfnu564xxx');
// 不带签名,模板没有变量也要传变量?
// $this->template('sms-tmpl-AIgLRm0xxx')->with('now', '');
}
}
后续想要实现的功能:
1、需要支持数据库配置,多模板,多签名,多服务商 有一个hyperf的代码学习仓库:Hyperf学习