* * 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 QcloudGateway. * * @see https://cloud.tencent.com/document/api/382/55981 */ class QcloudGateway extends Gateway { use HasHttpRequest; const ENDPOINT_URL = 'https://sms.tencentcloudapi.com'; const ENDPOINT_HOST = 'sms.tencentcloudapi.com'; const ENDPOINT_SERVICE = 'sms'; const ENDPOINT_METHOD = 'SendSms'; const ENDPOINT_VERSION = '2021-01-11'; const ENDPOINT_REGION = 'ap-guangzhou'; const ENDPOINT_FORMAT = 'json'; /** * @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) { $data = $message->getData($this); $signName = !empty($data['sign_name']) ? $data['sign_name'] : $config->get('sign_name', ''); unset($data['sign_name']); $phone = !\is_null($to->getIDDCode()) ? strval($to->getUniversalNumber()) : $to->getNumber(); $params = [ 'PhoneNumberSet' => [ $phone ], 'SmsSdkAppId' => $this->config->get('sdk_app_id'), 'SignName' => $signName, 'TemplateId' => (string) $message->getTemplate($this), 'TemplateParamSet' => array_map('strval', array_values($data)), ]; $time = time(); $result = $this->request('post', self::ENDPOINT_URL, [ 'headers' => [ 'Authorization' => $this->generateSign($params, $time), 'Host' => self::ENDPOINT_HOST, 'Content-Type' => 'application/json; charset=utf-8', 'X-TC-Action' => self::ENDPOINT_METHOD, 'X-TC-Region' => $this->config->get('region', self::ENDPOINT_REGION), 'X-TC-Timestamp' => $time, 'X-TC-Version' => self::ENDPOINT_VERSION, ], 'json' => $params, ]); if (!empty($result['Response']['Error']['Code'])) { throw new GatewayErrorException($result['Response']['Error']['Message'], 400, $result); } if (!empty($result['Response']['SendStatusSet'])) { foreach ($result['Response']['SendStatusSet'] as $group) { if ($group['Code'] != 'Ok') { throw new GatewayErrorException($group['Message'], 400, $result); } } } return $result; } /** * Generate Sign. * * @param array $params * * @return string */ protected function generateSign($params, $timestamp) { $date = gmdate("Y-m-d", $timestamp); $secretKey = $this->config->get('secret_key'); $secretId = $this->config->get('secret_id'); $canonicalRequest = 'POST'."\n". '/'."\n". '' ."\n". 'content-type:application/json; charset=utf-8'."\n". 'host:' . self::ENDPOINT_HOST."\n"."\n". 'content-type;host'."\n". hash("SHA256", json_encode($params)); $stringToSign = 'TC3-HMAC-SHA256'."\n". $timestamp."\n". $date . '/'. self::ENDPOINT_SERVICE .'/tc3_request'."\n". hash("SHA256", $canonicalRequest); $secretDate = hash_hmac("SHA256", $date, "TC3".$secretKey, true); $secretService = hash_hmac("SHA256", self::ENDPOINT_SERVICE, $secretDate, true); $secretSigning = hash_hmac("SHA256", "tc3_request", $secretService, true); $signature = hash_hmac("SHA256", $stringToSign, $secretSigning); return 'TC3-HMAC-SHA256' ." Credential=". $secretId ."/". $date . '/'. self::ENDPOINT_SERVICE .'/tc3_request' .", SignedHeaders=content-type;host, Signature=".$signature; } }