// +----------------------------------------------------------------------
// | ShopXO 国内领先企业级B2C免费开源电商系统
// +----------------------------------------------------------------------
// | Copyright (c) 2011~2019 http://shopxo.net All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: Devil
// +----------------------------------------------------------------------
namespace base;
/**
* 短信驱动
* @author liuyi
* @blog http://ourbrnader.com/
* @version 0.0.1
* @datetime 2020-05-18
*/
class TencentSms
{
// 保存错误信息
public $error;
// Access Key ID
private $accessKeyId = '';
// Access Access Key Secret
private $accessKeySecret = '';
// 签名
private $signName = '';
private $apiHost = "sms.tencentcloudapi.com";
//短信平台返回的原始数据
public $rawData;
/**
* [__construct 构造方法]
* @param [int] $param['interval_time'] [间隔时间(默认30)单位(秒)]
* @param [int] $param['expire_time'] [到期时间(默认30)单位(秒)]
* @param [string] $param['key_prefix'] [验证码种存储前缀key(默认 空)]
* @version 0.0.1
* @datetime 2017-03-07T14:03:02+0800
* @author Devil
* @blog http://gong.gg/
*/
public function __construct()
{
$this->signName = MyC('common_sms_sign');
$this->accessKeyId = MyC('common_sms_apikey');
$this->accessKeySecret = MyC('common_sms_apisecret');
$this->platform_appid = MyC('common_sms_sdk_appid');
}
/**
* 签名
*
* @param unknown $parameters
* @param unknown $accessKeySecret
* @return string
*/
private function computeSignature($host, $payload, $timestamp)
{
$secretId = $this->accessKeyId;
$secretKey = $this->accessKeySecret;
$service = 'sms';
$algorithm = "TC3-HMAC-SHA256";
$httpRequestMethod = 'POST';
$canonicalUri = "/";
$canonicalQueryString = "";
$canonicalHeaders = "content-type:application/json\n" . "host:" . $host . "\n";
$signedHeaders = "content-type;host";
// $payload = json_encode($param,JSON_UNESCAPED_UNICODE);
// echo $payload.PHP_EOL;
$hashedRequestPayload = hash("SHA256", $payload);
$canonicalRequest = $httpRequestMethod . "\n"
. $canonicalUri . "\n"
. $canonicalQueryString . "\n"
. $canonicalHeaders . "\n"
. $signedHeaders . "\n"
. $hashedRequestPayload;
// echo $canonicalRequest.PHP_EOL;
// step 2: build string to sign
$date = gmdate("Y-m-d", $timestamp);
$credentialScope = $date . "/" . $service . "/tc3_request";
$hashedCanonicalRequest = hash("SHA256", $canonicalRequest);
$stringToSign = $algorithm . "\n"
. $timestamp . "\n"
. $credentialScope . "\n"
. $hashedCanonicalRequest;
// echo $stringToSign.PHP_EOL;
// step 3: sign string
$secretDate = hash_hmac("SHA256", $date, "TC3" . $secretKey, true);
$secretService = hash_hmac("SHA256", $service, $secretDate, true);
$secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true);
$signature = hash_hmac("SHA256", $stringToSign, $secretSigning);
// echo $signature.PHP_EOL;
// step 4: build authorization
$authorization = $algorithm
. " Credential=" . $secretId . "/" . $credentialScope
. ", SignedHeaders=content-type;host, Signature=" . $signature;
// echo $authorization.PHP_EOL;
return $authorization;//返回认证签名
}
/**
* 短信发送
* @param [string] $mobile [手机号码,多个以 英文逗号 , 分割]
* @param [string|array] $code [变量code(单个直接传入 code 即可,多个传入数组)]
* @param [string] $template_code [模板 id]
* @param [boolean] $sign_name [自定义签名,默认使用基础配置的签名]
* @author Devil
* @blog http://gong.gg/
* @version 1.0.0
* @date 2020-04-02
* @desc description
*/
public function SendCode($mobile, $code, $template_code, $sign_name = '')
{
// 签名
$sign_name = empty($sign_name) ? $this->signName : $sign_name;
// 请求参数
$params = array( //此处作了修改
'PhoneNumberSet' => $mobile,
'TemplateID' => $template_code,
'Sign' => $sign_name,
'TemplateParamSet' => $code,
'SessionContext' => '',
'SmsSdkAppid' => $this->platform_appid
);
// 计算签名并把签名结果加入请求参数
$timestamp = time();
$url = 'https://' . $this->apiHost;
$version = '2019-07-11';
$action = "SendSms";
$payload = json_encode($params, JSON_UNESCAPED_UNICODE);
$authorization = $this->computeSignature($this->apiHost, $payload, $timestamp);
$headers = array(
"Authorization:$authorization",
"Content-type:application/json",
"Host:$this->apiHost",
"X-TC-Action:$action",
"X-TC-Version:$version",
"X-TC-Timestamp:$timestamp",
// "X-TC-Region:$region"
);
$curl = curl_init();
curl_setopt($curl, CURLOPT_URL, $url);
curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($curl, CURLOPT_HTTPHEADER, $headers);
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
curl_setopt($curl, CURLOPT_FOLLOWLOCATION, false);
curl_setopt($curl, CURLOPT_AUTOREFERER, 1);
curl_setopt($curl, CURLOPT_POST, 1);
curl_setopt($curl, CURLOPT_POSTFIELDS, $payload);
curl_setopt($curl, CURLOPT_POST, 1);
$result = curl_exec($curl);
curl_close($curl);
if ($result == false) {
$this->error = "访问腾讯云短信服务失败";
} else {
$result = json_decode($result, true);
}
//因为一般业务情况是发给一个人,这里就简单处理为取第一个短信的状态。
//如果是批量的话,需要遍历获得所有的情况,这样的可以另外写一个接口。
$this->rawData = $result;
if (isset($result["Response"]['Error'])) {
$errorCode = $result["Response"]['Error']['Code'];
} else if (isset($result["Response"]["SendStatusSet"][0])) {
$sendStatusItem = $result["Response"]["SendStatusSet"][0];
$errorCode = $sendStatusItem['Code'];
} else {
$errorCode = '';
}
if (strtolower($errorCode) != 'ok') {
$this->error = $this->getErrorMessage($errorCode);
return false;
}
return true;
}
/**
* 获取详细错误信息
*
* @param unknown $status
*/
public function getErrorMessage($status)
{
//查询错误码地址:
//https://cloud.tencent.com/document/product/382/38780
$message = array(
'FailedOperation.ContainSensitiveWord' => '短信内容中含有敏感词',
'FailedOperation.FailResolvePacket' => '请求包解析失败,通常情况下是由于没有遵守 API 接口说明规范导致的',
'FailedOperation.InsufficientBalanceInSmsPackage' => '套餐包余量不足',
'FailedOperation.JsonParseFail' => '解析请求包体时候失败',
'FailedOperation.MarketingSendTimeConstraint' => '营销短信发送时间限制,为避免骚扰用户,营销短信只允许在8点到22点发送。',
'FailedOperation.MissingSignature' => '没有申请签名之前,无法申请模板,请根据 创建签名 申请完成之后再次申请。',
'FailedOperation.PhoneNumberInBlacklist' => '手机号在黑名单库中,通常是用户退订或者命中运营商黑名单导致的,可联系 sms helper 解决。',
'FailedOperation.PhoneNumberOnBlacklist' => '手机号在黑名单库中,通常是用户退订或者命中运营商黑名单导致的,可联系 sms helper 解决。',
'InternalError.RequestTimeException' => '请求发起时间不正常,通常是由于您的服务器时间与腾讯云服务器时间差异超过10分钟导致的,请核对服务器时间及 API 接口中的时间字段是否正常。',
'InternalError.Timeout' => '请求下发短信超时,请参考 60008错误详解。',
'InvalidParameterValue.IncorrectPhoneNumber' => '手机号格式错误,请参考 1016错误详解',
'InvalidParameterValue.ProhibitedUseUrlInTemplateParameter' => '禁止在模板变量中使用 URL。',
'InvalidParameterValue.TemplateParameterFormatError' => '验证码模板参数格式错误,验证码类模版,模版变量只能传入0 - 6位(包括6位)纯数字',
'LimitExceeded.AppDailyLimit' => '业务短信日下发条数超过设定的上限 ,可自行到控制台调整短信频率限制策略。',
'LimitExceeded.DailyLimit' => '短信日下发条数超过设定的上限 (国际/港澳台),如需调整限制,可联系 sms helper。',
'LimitExceeded.DeliveryFrequencyLimit' => '下发短信命中了频率限制策略,可自行到控制台调整短信频率限制策略,如有其他需求请联系 sms helper。',
'LimitExceeded.PhoneNumberCountLimit' => '调用短信发送 API 接口单次提交的手机号个数超过200个',
'LimitExceeded.PhoneNumberDailyLimit' => '单个手机号日下发短信条数超过设定的上限,可自行到控制台调整短信频率限制策略。',
'LimitExceeded.PhoneNumberOneHourLimit' => '单个手机号1小时内下发短信条数超过设定的上限,可自行到控制台调整短信频率限制策略。',
'LimitExceeded.PhoneNumberSameContentDailyLimit' => '单个手机号下发相同内容超过设定的上限,可自行到控制台调整短信频率限制策略。',
'LimitExceeded.PhoneNumberThirtySecondLimit' => '单个手机号30秒内下发短信条数超过设定的上限,可自行到控制台调整短信频率限制策略。',
'MissingParameter.EmptyPhoneNumberSet' => '传入的号码列表为空,请确认您的参数中是否传入号码。',
'UnauthorizedOperation.IndividualUserMarketingSmsPermissionDeny' => '个人用户没有发营销短信的权限',
'UnauthorizedOperation.RequestIpNotInWhitelist' => '请求 IP 不在白名单中,您配置了校验请求来源 IP,但是检测到当前请求 IP 不在配置列表中,',
'UnauthorizedOperation.SerivceSuspendDueToArrears' => '欠费被停止服务,可自行登录腾讯云充值来缴清欠款。',
'ContainDomesticAndInternationalPhoneNumber' => '群发请求里既有国内手机号也有国际手机号。',
'UnsupportedOperation.UnsuportedRegion' => '不支持该地区短信下发。',
);
if (isset ($message [$status])) {
return $message [$status];
}
return '错误状态码:' . $status . ',详细错查询:https://cloud.tencent.com/document/product/382/38780';
}
}
?>
一键复制
编辑
原始数据
按行查看
历史