初始上传

This commit is contained in:
2026-04-04 17:27:12 +08:00
parent 4d80d28eb4
commit b7e11774ee
11191 changed files with 1588469 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
<?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 AliyunGateway.
*
* @author carson <docxcn@gmail.com>
*
* @see https://help.aliyun.com/document_detail/55451.html
*/
class AliyunGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'http://dysmsapi.aliyuncs.com';
const ENDPOINT_METHOD = 'SendSms';
const ENDPOINT_VERSION = '2017-05-25';
const ENDPOINT_FORMAT = 'JSON';
const ENDPOINT_REGION_ID = 'cn-hangzhou';
const ENDPOINT_SIGNATURE_METHOD = 'HMAC-SHA1';
const ENDPOINT_SIGNATURE_VERSION = '1.0';
/**
* @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']);
$params = [
'RegionId' => self::ENDPOINT_REGION_ID,
'AccessKeyId' => $config->get('access_key_id'),
'Format' => self::ENDPOINT_FORMAT,
'SignatureMethod' => self::ENDPOINT_SIGNATURE_METHOD,
'SignatureVersion' => self::ENDPOINT_SIGNATURE_VERSION,
'SignatureNonce' => uniqid(),
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
'Action' => self::ENDPOINT_METHOD,
'Version' => self::ENDPOINT_VERSION,
'PhoneNumbers' => !\is_null($to->getIDDCode()) ? strval($to->getZeroPrefixedNumber()) : $to->getNumber(),
'SignName' => $signName,
'TemplateCode' => $message->getTemplate($this),
'TemplateParam' => json_encode($data, JSON_FORCE_OBJECT),
];
$params['Signature'] = $this->generateSign($params);
$result = $this->get(self::ENDPOINT_URL, $params);
if ('OK' != $result['Code']) {
throw new GatewayErrorException($result['Message'], $result['Code'], $result);
}
return $result;
}
/**
* Generate Sign.
*
* @param array $params
*
* @return string
*/
protected function generateSign($params)
{
ksort($params);
$accessKeySecret = $this->config->get('access_key_secret');
$stringToSign = 'GET&%2F&'.urlencode(http_build_query($params, null, '&', PHP_QUERY_RFC3986));
return base64_encode(hash_hmac('sha1', $stringToSign, $accessKeySecret.'&', true));
}
}

View File

@@ -0,0 +1,107 @@
<?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 AliyunrestGateway.
*/
class AliyunrestGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'http://gw.api.taobao.com/router/rest';
const ENDPOINT_VERSION = '2.0';
const ENDPOINT_FORMAT = 'json';
const ENDPOINT_METHOD = 'alibaba.aliqin.fc.sms.num.send';
const ENDPOINT_SIGNATURE_METHOD = 'md5';
const ENDPOINT_PARTNER_ID = 'EasySms';
/**
* @param PhoneNumberInterface $to
* @param MessageInterface $message
* @param Config $config
*
* @return array|void
*/
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$urlParams = [
'app_key' => $config->get('app_key'),
'v' => self::ENDPOINT_VERSION,
'format' => self::ENDPOINT_FORMAT,
'sign_method' => self::ENDPOINT_SIGNATURE_METHOD,
'method' => self::ENDPOINT_METHOD,
'timestamp' => date('Y-m-d H:i:s'),
'partner_id' => self::ENDPOINT_PARTNER_ID,
];
$params = [
'extend' => '',
'sms_type' => 'normal',
'sms_free_sign_name' => $config->get('sign_name'),
'sms_param' => json_encode($message->getData($this)),
'rec_num' => !\is_null($to->getIDDCode()) ? strval($to->getZeroPrefixedNumber()) : $to->getNumber(),
'sms_template_code' => $message->getTemplate($this),
];
$urlParams['sign'] = $this->generateSign(array_merge($params, $urlParams));
$result = $this->post($this->getEndpointUrl($urlParams), $params);
if (isset($result['error_response']) && 0 != $result['error_response']['code']) {
throw new GatewayErrorException($result['error_response']['msg'], $result['error_response']['code'], $result);
}
return $result;
}
/**
* @param array $params
*
* @return string
*/
protected function getEndpointUrl($params)
{
return self::ENDPOINT_URL.'?'.http_build_query($params);
}
/**
* @param array $params
*
* @return string
*/
protected function generateSign($params)
{
ksort($params);
$stringToBeSigned = $this->config->get('app_secret_key');
foreach ($params as $k => $v) {
if (!is_array($v) && '@' != substr($v, 0, 1)) {
$stringToBeSigned .= "$k$v";
}
}
unset($k, $v);
$stringToBeSigned .= $this->config->get('app_secret_key');
return strtoupper(md5($stringToBeSigned));
}
}

View File

@@ -0,0 +1,60 @@
<?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 AvatardataGateway.
*
* @see http://www.avatardata.cn/Docs/Api/fd475e40-7809-4be7-936c-5926dd41b0fe
*/
class AvatardataGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'http://v1.avatardata.cn/Sms/Send';
const ENDPOINT_FORMAT = 'json';
/**
* @param PhoneNumberInterface $to
* @param MessageInterface $message
* @param Config $config
*
* @return array
*
* @throws GatewayErrorException;
*/
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$params = [
'mobile' => $to->getNumber(),
'templateId' => $message->getTemplate($this),
'param' => implode(',', $message->getData($this)),
'dtype' => self::ENDPOINT_FORMAT,
'key' => $config->get('app_key'),
];
$result = $this->get(self::ENDPOINT_URL, $params);
if ($result['error_code']) {
throw new GatewayErrorException($result['reason'], $result['error_code'], $result);
}
return $result;
}
}

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

View File

@@ -0,0 +1,156 @@
<?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\Exceptions\InvalidArgumentException;
use Overtrue\EasySms\Support\Config;
use Overtrue\EasySms\Traits\HasHttpRequest;
/**
* Class ChuanglanGateway.
*
* @see https://zz.253.com/v5.html#/api_doc
*/
class ChuanglanGateway extends Gateway
{
use HasHttpRequest;
/**
* URL模板
*/
const ENDPOINT_URL_TEMPLATE = 'https://%s.253.com/msg/send/json';
/**
* 国际短信
*/
const INT_URL = 'http://intapi.253.com/send/json';
/**
* 验证码渠道code.
*/
const CHANNEL_VALIDATE_CODE = 'smsbj1';
/**
* 会员营销渠道code.
*/
const CHANNEL_PROMOTION_CODE = 'smssh1';
/**
* @param PhoneNumberInterface $to
* @param MessageInterface $message
* @param Config $config
*
* @return array
*
* @throws GatewayErrorException
* @throws InvalidArgumentException
*/
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$IDDCode = !empty($to->getIDDCode()) ? $to->getIDDCode() : 86;
$params = [
'account' => $config->get('account'),
'password' => $config->get('password'),
'phone' => $to->getNumber(),
'msg' => $this->wrapChannelContent($message->getContent($this), $config, $IDDCode),
];
if (86 != $IDDCode) {
$params['mobile'] = $to->getIDDCode().$to->getNumber();
$params['account'] = $config->get('intel_account') ?: $config->get('account');
$params['password'] = $config->get('intel_password') ?: $config->get('password');
}
$result = $this->postJson($this->buildEndpoint($config, $IDDCode), $params);
if (!isset($result['code']) || '0' != $result['code']) {
throw new GatewayErrorException(json_encode($result, JSON_UNESCAPED_UNICODE), isset($result['code']) ? $result['code'] : 0, $result);
}
return $result;
}
/**
* @param Config $config
* @param int $IDDCode
*
* @return string
*
* @throws InvalidArgumentException
*/
protected function buildEndpoint(Config $config, $IDDCode = 86)
{
$channel = $this->getChannel($config, $IDDCode);
if (self::INT_URL === $channel) {
return $channel;
}
return sprintf(self::ENDPOINT_URL_TEMPLATE, $channel);
}
/**
* @param Config $config
* @param int $IDDCode
*
* @return mixed
*
* @throws InvalidArgumentException
*/
protected function getChannel(Config $config, $IDDCode)
{
if (86 != $IDDCode) {
return self::INT_URL;
}
$channel = $config->get('channel', self::CHANNEL_VALIDATE_CODE);
if (!in_array($channel, [self::CHANNEL_VALIDATE_CODE, self::CHANNEL_PROMOTION_CODE])) {
throw new InvalidArgumentException('Invalid channel for ChuanglanGateway.');
}
return $channel;
}
/**
* @param string $content
* @param Config $config
* @param int $IDDCode
*
* @return string|string
*
* @throws InvalidArgumentException
*/
protected function wrapChannelContent($content, Config $config, $IDDCode)
{
$channel = $this->getChannel($config, $IDDCode);
if (self::CHANNEL_PROMOTION_CODE == $channel) {
$sign = (string) $config->get('sign', '');
if (empty($sign)) {
throw new InvalidArgumentException('Invalid sign for ChuanglanGateway when using promotion channel');
}
$unsubscribe = (string) $config->get('unsubscribe', '');
if (empty($unsubscribe)) {
throw new InvalidArgumentException('Invalid unsubscribe for ChuanglanGateway when using promotion channel');
}
$content = $sign.$content.$unsubscribe;
}
return $content;
}
}

View File

@@ -0,0 +1,50 @@
<?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\Support\Config;
/**
* Class ErrorlogGateway.
*/
class ErrorlogGateway extends Gateway
{
/**
* @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return array
*/
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
if (is_array($to)) {
$to = implode(',', $to);
}
$message = sprintf(
"[%s] to: %s | message: \"%s\" | template: \"%s\" | data: %s\n",
date('Y-m-d H:i:s'),
$to,
$message->getContent($this),
$message->getTemplate($this),
json_encode($message->getData($this))
);
$file = $this->config->get('file', ini_get('error_log'));
$status = error_log($message, 3, $file);
return compact('status', 'file');
}
}

View File

@@ -0,0 +1,120 @@
<?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\GatewayInterface;
use Overtrue\EasySms\Support\Config;
/**
* Class Gateway.
*/
abstract class Gateway implements GatewayInterface
{
const DEFAULT_TIMEOUT = 5.0;
/**
* @var \Overtrue\EasySms\Support\Config
*/
protected $config;
/**
* @var array
*/
protected $options;
/**
* @var float
*/
protected $timeout;
/**
* Gateway constructor.
*
* @param array $config
*/
public function __construct(array $config)
{
$this->config = new Config($config);
}
/**
* Return timeout.
*
* @return int|mixed
*/
public function getTimeout()
{
return $this->timeout ?: $this->config->get('timeout', self::DEFAULT_TIMEOUT);
}
/**
* Set timeout.
*
* @param int $timeout
*
* @return $this
*/
public function setTimeout($timeout)
{
$this->timeout = floatval($timeout);
return $this;
}
/**
* @return \Overtrue\EasySms\Support\Config
*/
public function getConfig()
{
return $this->config;
}
/**
* @param \Overtrue\EasySms\Support\Config $config
*
* @return $this
*/
public function setConfig(Config $config)
{
$this->config = $config;
return $this;
}
/**
* @param $options
*
* @return $this
*/
public function setGuzzleOptions($options)
{
$this->options = $options;
return $this;
}
/**
* @return array
*/
public function getGuzzleOptions()
{
return $this->options ?: $this->config->get('options', []);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return \strtolower(str_replace([__NAMESPACE__.'\\', 'Gateway'], '', \get_class($this)));
}
}

View File

@@ -0,0 +1,148 @@
<?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 GuzzleHttp\Exception\RequestException;
use Overtrue\EasySms\Contracts\MessageInterface;
use Overtrue\EasySms\Contracts\PhoneNumberInterface;
use Overtrue\EasySms\Exceptions\GatewayErrorException;
use Overtrue\EasySms\Exceptions\InvalidArgumentException;
use Overtrue\EasySms\Support\Config;
use Overtrue\EasySms\Traits\HasHttpRequest;
class HuaweiGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_HOST = 'https://api.rtc.huaweicloud.com:10443';
const ENDPOINT_URI = '/sms/batchSendSms/v1';
const SUCCESS_CODE = '000000';
/**
* 发送信息.
*
* @param PhoneNumberInterface $to
* @param MessageInterface $message
* @param Config $config
*
* @return array
*
* @throws GatewayErrorException
* @throws InvalidArgumentException
*/
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$appKey = $config->get('app_key');
$appSecret = $config->get('app_secret');
$channels = $config->get('from');
$statusCallback = $config->get('callback', '');
$endpoint = $this->getEndpoint($config);
$headers = $this->getHeaders($appKey, $appSecret);
$templateId = $message->getTemplate($this);
$messageData = $message->getData($this);
// 短信签名通道号码
$from = 'default';
if (isset($messageData['from'])) {
$from = $messageData['from'];
unset($messageData['from']);
}
$channel = isset($channels[$from]) ? $channels[$from] : '';
if (empty($channel)) {
throw new InvalidArgumentException("From Channel [{$from}] Not Exist");
}
$params = [
'from' => $channel,
'to' => $to->getUniversalNumber(),
'templateId' => $templateId,
'templateParas' => json_encode($messageData),
'statusCallback' => $statusCallback,
];
try {
$result = $this->request('post', $endpoint, [
'headers' => $headers,
'form_params' => $params,
//为防止因HTTPS证书认证失败造成API调用失败需要先忽略证书信任问题
'verify' => false,
]);
} catch (RequestException $e) {
$result = $this->unwrapResponse($e->getResponse());
}
if (self::SUCCESS_CODE != $result['code']) {
throw new GatewayErrorException($result['description'], ltrim($result['code'], 'E'), $result);
}
return $result;
}
/**
* 构造 Endpoint.
*
* @param Config $config
*
* @return string
*/
protected function getEndpoint(Config $config)
{
$endpoint = rtrim($config->get('endpoint', self::ENDPOINT_HOST), '/');
return $endpoint.self::ENDPOINT_URI;
}
/**
* 获取请求 Headers 参数.
*
* @param string $appKey
* @param string $appSecret
*
* @return array
*/
protected function getHeaders($appKey, $appSecret)
{
return [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => 'WSSE realm="SDP",profile="UsernameToken",type="Appkey"',
'X-WSSE' => $this->buildWsseHeader($appKey, $appSecret),
];
}
/**
* 构造X-WSSE参数值
*
* @param string $appKey
* @param string $appSecret
*
* @return string
*/
protected function buildWsseHeader($appKey, $appSecret)
{
$now = date('Y-m-d\TH:i:s\Z');
$nonce = uniqid();
$passwordDigest = base64_encode(hash('sha256', ($nonce.$now.$appSecret)));
return sprintf(
'UsernameToken Username="%s",PasswordDigest="%s",Nonce="%s",Created="%s"',
$appKey,
$passwordDigest,
$nonce,
$now
);
}
}

View File

@@ -0,0 +1,73 @@
<?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 HuaxinGateway.
*
* @see http://www.ipyy.com/help/
*/
class HuaxinGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'http://%s/smsJson.aspx';
/**
* @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)
{
$endpoint = $this->buildEndpoint($config->get('ip'));
$result = $this->post($endpoint, [
'userid' => $config->get('user_id'),
'account' => $config->get('account'),
'password' => $config->get('password'),
'mobile' => $to->getNumber(),
'content' => $message->getContent($this),
'sendTime' => '',
'action' => 'send',
'extno' => $config->get('ext_no'),
]);
if ('Success' !== $result['returnstatus']) {
throw new GatewayErrorException($result['message'], 400, $result);
}
return $result;
}
/**
* Build endpoint url.
*
* @param string $ip
*
* @return string
*/
protected function buildEndpoint($ip)
{
return sprintf(self::ENDPOINT_TEMPLATE, $ip);
}
}

View File

@@ -0,0 +1,77 @@
<?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 HuyiGateway.
*
* @see http://www.ihuyi.com/api/sms.html
*/
class HuyiGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'http://106.ihuyi.com/webservice/sms.php?method=Submit';
const ENDPOINT_FORMAT = 'json';
const SUCCESS_CODE = 2;
/**
* @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 = [
'account' => $config->get('api_id'),
'mobile' => $to->getIDDCode() ? \sprintf('%s %s', $to->getIDDCode(), $to->getNumber()) : $to->getNumber(),
'content' => $message->getContent($this),
'time' => time(),
'format' => self::ENDPOINT_FORMAT,
'sign' => $config->get('signature'),
];
$params['password'] = $this->generateSign($params);
$result = $this->post(self::ENDPOINT_URL, $params);
if (self::SUCCESS_CODE != $result['code']) {
throw new GatewayErrorException($result['msg'], $result['code'], $result);
}
return $result;
}
/**
* Generate Sign.
*
* @param array $params
*
* @return string
*/
protected function generateSign($params)
{
return md5($params['account'].$this->config->get('api_key').$params['mobile'].$params['content'].$params['time']);
}
}

View File

@@ -0,0 +1,76 @@
<?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 JuheGateway.
*
* @see https://www.juhe.cn/docs/api/id/54
*/
class JuheGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'http://v.juhe.cn/sms/send';
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)
{
$params = [
'mobile' => $to->getNumber(),
'tpl_id' => $message->getTemplate($this),
'tpl_value' => $this->formatTemplateVars($message->getData($this)),
'dtype' => self::ENDPOINT_FORMAT,
'key' => $config->get('app_key'),
];
$result = $this->get(self::ENDPOINT_URL, $params);
if ($result['error_code']) {
throw new GatewayErrorException($result['reason'], $result['error_code'], $result);
}
return $result;
}
/**
* @param array $vars
*
* @return string
*/
protected function formatTemplateVars(array $vars)
{
$formatted = [];
foreach ($vars as $key => $value) {
$formatted[sprintf('#%s#', trim($key, '#'))] = $value;
}
return http_build_query($formatted);
}
}

View File

@@ -0,0 +1,61 @@
<?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 KingttoGateWay.
*
* @see http://www.kingtto.cn/
*/
class KingttoGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'http://101.201.41.194:9999/sms.aspx';
const ENDPOINT_METHOD = 'send';
/**
* @param PhoneNumberInterface $to
* @param MessageInterface $message
* @param Config $config
*
* @return \Psr\Http\Message\ResponseInterface|array|string
*
* @throws GatewayErrorException
*/
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$params = [
'action' => self::ENDPOINT_METHOD,
'userid' => $config->get('userid'),
'account' => $config->get('account'),
'password' => $config->get('password'),
'mobile' => $to->getNumber(),
'content' => $message->getContent(),
];
$result = $this->post(self::ENDPOINT_URL, $params);
if ('Success' != $result['returnstatus']) {
throw new GatewayErrorException($result['message'], $result['remainpoint'], $result);
}
return $result;
}
}

View File

@@ -0,0 +1,74 @@
<?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 LuosimaoGateway.
*
* @see https://luosimao.com/docs/api/
*/
class LuosimaoGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'https://%s.luosimao.com/%s/%s.%s';
const ENDPOINT_VERSION = 'v1';
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)
{
$endpoint = $this->buildEndpoint('sms-api', 'send');
$result = $this->post($endpoint, [
'mobile' => $to->getNumber(),
'message' => $message->getContent($this),
], [
'Authorization' => 'Basic '.base64_encode('api:key-'.$config->get('api_key')),
]);
if ($result['error']) {
throw new GatewayErrorException($result['msg'], $result['error'], $result);
}
return $result;
}
/**
* Build endpoint url.
*
* @param string $type
* @param string $function
*
* @return string
*/
protected function buildEndpoint($type, $function)
{
return sprintf(self::ENDPOINT_TEMPLATE, $type, self::ENDPOINT_VERSION, $function, self::ENDPOINT_FORMAT);
}
}

View File

@@ -0,0 +1,106 @@
<?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 ModuyunGateway.
*
* @see https://www.moduyun.com/doc/index.html#10002
*/
class ModuyunGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'https://live.moduyun.com/sms/v2/sendsinglesms';
/**
* @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)
{
$urlParams = [
'accesskey' => $config->get('accesskey'),
'random' => rand(100000, 999999),
];
$params = [
'tel' => [
'mobile' => $to->getNumber(),
'nationcode' => $to->getIDDCode() ?: '86',
],
'signId' => $config->get('signId', ''),
'templateId' => $message->getTemplate($this),
'time' => time(),
'type' => $config->get('type', 0),
'params' => array_values($message->getData($this)),
'ext' => '',
'extend' => '',
];
$params['sig'] = $this->generateSign($params, $urlParams['random']);
$result = $this->request('post', $this->getEndpointUrl($urlParams), [
'headers' => [
'Content-Type' => 'application/json',
'Accept' => 'application/json'
],
'json' => $params,
]);
$result = is_string($result) ? json_decode($result, true) : $result;
if (0 != $result['result']) {
throw new GatewayErrorException($result['errmsg'], $result['result'], $result);
}
return $result;
}
/**
* @param array $params
*
* @return string
*/
protected function getEndpointUrl($params)
{
return self::ENDPOINT_URL . '?' . http_build_query($params);
}
/**
* Generate Sign.
*
* @param array $params
* @param string $random
*
* @return string
*/
protected function generateSign($params, $random)
{
return hash('sha256', sprintf(
'secretkey=%s&random=%d&time=%d&mobile=%s',
$this->config->get('secretkey'),
$random,
$params['time'],
$params['tel']['mobile']
));
}
}

View File

@@ -0,0 +1,115 @@
<?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 QcloudGateway.
*
* @see https://cloud.tencent.com/document/product/382/13297
*/
class QcloudGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'https://yun.tim.qq.com/v5/';
const ENDPOINT_METHOD = 'tlssmssvr/sendsms';
const ENDPOINT_VERSION = 'v5';
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']);
$msg = $message->getContent($this);
if (!empty($msg) && '【' != mb_substr($msg, 0, 1) && !empty($signName)) {
$msg = '【'.$signName.'】'.$msg;
}
$type = !empty($data['type']) ? $data['type'] : 0;
$params = [
'tel' => [
'nationcode' => $to->getIDDCode() ?: 86,
'mobile' => $to->getNumber(),
],
'type' => $type,
'msg' => $msg,
'time' => time(),
'extend' => '',
'ext' => '',
];
if (!is_null($message->getTemplate($this)) && is_array($data)) {
unset($params['msg']);
$params['params'] = array_values($data);
$params['tpl_id'] = $message->getTemplate($this);
$params['sign'] = $signName;
}
$random = substr(uniqid(), -10);
$params['sig'] = $this->generateSign($params, $random);
$url = self::ENDPOINT_URL.self::ENDPOINT_METHOD.'?sdkappid='.$config->get('sdk_app_id').'&random='.$random;
$result = $this->request('post', $url, [
'headers' => ['Accept' => 'application/json'],
'json' => $params,
]);
if (0 != $result['result']) {
throw new GatewayErrorException($result['errmsg'], $result['result'], $result);
}
return $result;
}
/**
* Generate Sign.
*
* @param array $params
* @param string $random
*
* @return string
*/
protected function generateSign($params, $random)
{
ksort($params);
return hash('sha256', sprintf(
'appkey=%s&random=%s&time=%s&mobile=%s',
$this->config->get('app_key'),
$random,
$params['time'],
$params['tel']['mobile']
), false);
}
}

View File

@@ -0,0 +1,148 @@
<?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 QiniuGateway.
*
* @see https://developer.qiniu.com/sms/api/5897/sms-api-send-message
*/
class QiniuGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'https://%s.qiniuapi.com/%s/%s';
const ENDPOINT_VERSION = 'v1';
/**
* @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)
{
$endpoint = $this->buildEndpoint('sms', 'message/single');
$data = $message->getData($this);
$params = [
'template_id' => $message->getTemplate($this),
'mobile' => $to->getNumber(),
];
if (!empty($data)) {
$params['parameters'] = $data;
}
$headers = [
'Content-Type' => 'application/json',
];
$headers['Authorization'] = $this->generateSign($endpoint, 'POST', json_encode($params), $headers['Content-Type'], $config);
$result = $this->postJson($endpoint, $params, $headers);
if (isset($result['error'])) {
throw new GatewayErrorException($result['message'], $result['error'], $result);
}
return $result;
}
/**
* Build endpoint url.
*
* @param string $type
* @param string $function
*
* @return string
*/
protected function buildEndpoint($type, $function)
{
return sprintf(self::ENDPOINT_TEMPLATE, $type, self::ENDPOINT_VERSION, $function);
}
/**
* Build endpoint url.
*
* @param string $url
* @param string $method
* @param string $body
* @param string $contentType
* @param Config $config
*
* @return string
*/
protected function generateSign($url, $method, $body, $contentType, Config $config)
{
$urlItems = parse_url($url);
$host = $urlItems['host'];
if (isset($urlItems['port'])) {
$port = $urlItems['port'];
} else {
$port = '';
}
$path = $urlItems['path'];
if (isset($urlItems['query'])) {
$query = $urlItems['query'];
} else {
$query = '';
}
//write request uri
$toSignStr = $method.' '.$path;
if (!empty($query)) {
$toSignStr .= '?'.$query;
}
//write host and port
$toSignStr .= "\nHost: ".$host;
if (!empty($port)) {
$toSignStr .= ':'.$port;
}
//write content type
if (!empty($contentType)) {
$toSignStr .= "\nContent-Type: ".$contentType;
}
$toSignStr .= "\n\n";
//write body
if (!empty($body)) {
$toSignStr .= $body;
}
$hmac = hash_hmac('sha1', $toSignStr, $config->get('secret_key'), true);
return 'Qiniu '.$config->get('access_key').':'.$this->base64UrlSafeEncode($hmac);
}
/**
* @param string $data
*
* @return string
*/
protected function base64UrlSafeEncode($data)
{
$find = array('+', '/');
$replace = array('-', '_');
return str_replace($find, $replace, base64_encode($data));
}
}

View File

@@ -0,0 +1,134 @@
<?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 GuzzleHttp\Exception\ClientException;
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 RongcloudGateway.
*
* @author Darren Gao <realgaodacheng@gmail.com>
*
* @see http://www.rongcloud.cn/docs/sms_service.html#send_sms_code
*/
class RongcloudGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'http://api.sms.ronghub.com/%s.%s';
const ENDPOINT_ACTION = 'sendCode';
const ENDPOINT_FORMAT = 'json';
const ENDPOINT_REGION = '86'; // 中国区,目前只支持此国别
const SUCCESS_CODE = 200;
/**
* @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();
$action = array_key_exists('action', $data) ? $data['action'] : self::ENDPOINT_ACTION;
$endpoint = $this->buildEndpoint($action);
$headers = [
'Nonce' => uniqid(),
'App-Key' => $config->get('app_key'),
'Timestamp' => time(),
];
$headers['Signature'] = $this->generateSign($headers, $config);
switch ($action) {
case 'sendCode':
$params = [
'mobile' => $to->getNumber(),
'region' => self::ENDPOINT_REGION,
'templateId' => $message->getTemplate($this),
];
break;
case 'verifyCode':
if (!array_key_exists('code', $data)
or !array_key_exists('sessionId', $data)) {
throw new GatewayErrorException('"code" or "sessionId" is not set', 0);
}
$params = [
'code' => $data['code'],
'sessionId' => $data['sessionId'],
];
break;
case 'sendNotify':
$params = [
'mobile' => $to->getNumber(),
'region' => self::ENDPOINT_REGION,
'templateId' => $message->getTemplate($this),
];
$params = array_merge($params, $data);
break;
default:
throw new GatewayErrorException(sprintf('action: %s not supported', $action));
}
try {
$result = $this->post($endpoint, $params, $headers);
if (self::SUCCESS_CODE !== $result['code']) {
throw new GatewayErrorException($result['errorMessage'], $result['code'], $result);
}
} catch (ClientException $e) {
throw new GatewayErrorException($e->getMessage(), $e->getCode());
}
return $result;
}
/**
* Generate Sign.
*
* @param array $params
* @param \Overtrue\EasySms\Support\Config $config
*
* @return string
*/
protected function generateSign($params, Config $config)
{
return sha1(sprintf('%s%s%s', $config->get('app_secret'), $params['Nonce'], $params['Timestamp']));
}
/**
* Build endpoint url.
*
* @param string $action
*
* @return string
*/
protected function buildEndpoint($action)
{
return sprintf(self::ENDPOINT_TEMPLATE, $action, self::ENDPOINT_FORMAT);
}
}

View File

@@ -0,0 +1,95 @@
<?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 SendcloudGateway.
*
* @see http://sendcloud.sohu.com/doc/sms/
*/
class SendcloudGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'http://www.sendcloud.net/smsapi/%s';
/**
* Send a short 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 = [
'smsUser' => $config->get('sms_user'),
'templateId' => $message->getTemplate($this),
'msgType' => $to->getIDDCode() ? 2 : 0,
'phone' => $to->getZeroPrefixedNumber(),
'vars' => $this->formatTemplateVars($message->getData($this)),
];
if ($config->get('timestamp', false)) {
$params['timestamp'] = time() * 1000;
}
$params['signature'] = $this->sign($params, $config->get('sms_key'));
$result = $this->post(sprintf(self::ENDPOINT_TEMPLATE, 'send'), $params);
if (!$result['result']) {
throw new GatewayErrorException($result['message'], $result['statusCode'], $result);
}
return $result;
}
/**
* @param array $vars
*
* @return string
*/
protected function formatTemplateVars(array $vars)
{
$formatted = [];
foreach ($vars as $key => $value) {
$formatted[sprintf('%%%s%%', trim($key, '%'))] = $value;
}
return json_encode($formatted, JSON_FORCE_OBJECT);
}
/**
* @param array $params
* @param string $key
*
* @return string
*/
protected function sign($params, $key)
{
ksort($params);
return md5(sprintf('%s&%s&%s', $key, urldecode(http_build_query($params)), $key));
}
}

View File

@@ -0,0 +1,78 @@
<?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 SmsbaoGateway
* @author iwindy <203962638@qq.com>
* @see http://www.smsbao.com/openapi/
*/
class SmsbaoGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'http://api.smsbao.com/%s';
const SUCCESS_CODE = '0';
protected $errorStatuses = [
'0' => '短信发送成功',
'-1' => '参数不全',
'-2' => '服务器空间不支持,请确认支持curl或者fsocket联系您的空间商解决或者更换空间',
'30' => '密码错误',
'40' => '账号不存在',
'41' => '余额不足',
'42' => '帐户已过期',
'43' => 'IP地址限制',
'50' => '内容含有敏感词'
];
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$data = $message->getContent($this);
if (is_null($to->getIDDCode())) {
$number = $to->getNumber();
$action = 'sms';
} else {
$number = $to->getUniversalNumber();
$action = 'wsms';
}
$params = [
'u' => $config->get('user'),
'p' => md5($config->get('password')),
'm' => $number,
'c' => $data
];
$result = $this->get($this->buildEndpoint($action), $params);
if ($result !== self::SUCCESS_CODE) {
throw new GatewayErrorException($this->errorStatuses[$result], $result);
}
return $result;
}
protected function buildEndpoint($type)
{
return sprintf(self::ENDPOINT_URL, $type);
}
}

View File

@@ -0,0 +1,88 @@
<?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 SubmailGateway.
*
* @see https://www.mysubmail.com/chs/documents/developer/index
*/
class SubmailGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'https://api.mysubmail.com/%s.%s';
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)
{
$endpoint = $this->buildEndpoint($this->inChineseMainland($to) ? 'message/xsend' : 'internationalsms/xsend');
$data = $message->getData($this);
$result = $this->post($endpoint, [
'appid' => $config->get('app_id'),
'signature' => $config->get('app_key'),
'project' => !empty($data['project']) ? $data['project'] : $config->get('project'),
'to' => $to->getUniversalNumber(),
'vars' => json_encode($data, JSON_FORCE_OBJECT),
]);
if ('success' != $result['status']) {
throw new GatewayErrorException($result['msg'], $result['code'], $result);
}
return $result;
}
/**
* Build endpoint url.
*
* @param string $function
*
* @return string
*/
protected function buildEndpoint($function)
{
return sprintf(self::ENDPOINT_TEMPLATE, $function, self::ENDPOINT_FORMAT);
}
/**
* Check if the phone number belongs to chinese mainland.
*
* @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to
*
* @return bool
*/
protected function inChineseMainland($to)
{
$code = $to->getIDDCode();
return empty($code) || 86 === $code;
}
}

View File

@@ -0,0 +1,84 @@
<?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 TianyiwuxianGateway.
*
* @author Darren Gao <realgaodacheng@gmail.com>
*/
class TianyiwuxianGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'http://jk.106api.cn/sms%s.aspx';
const ENDPOINT_ENCODE = 'UTF8';
const ENDPOINT_TYPE = 'send';
const ENDPOINT_FORMAT = 'json';
const SUCCESS_STATUS = 'success';
const SUCCESS_CODE = '0';
/**
* @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)
{
$endpoint = $this->buildEndpoint();
$params = [
'gwid' => $config->get('gwid'),
'type' => self::ENDPOINT_TYPE,
'rece' => self::ENDPOINT_FORMAT,
'mobile' => $to->getNumber(),
'message' => $message->getContent($this),
'username' => $config->get('username'),
'password' => strtoupper(md5($config->get('password'))),
];
$result = $this->post($endpoint, $params);
$result = json_decode($result, true);
if (self::SUCCESS_STATUS !== $result['returnstatus'] || self::SUCCESS_CODE !== $result['code']) {
throw new GatewayErrorException($result['remark'], $result['code']);
}
return $result;
}
/**
* Build endpoint url.
*
* @return string
*/
protected function buildEndpoint()
{
return sprintf(self::ENDPOINT_TEMPLATE, self::ENDPOINT_ENCODE);
}
}

View File

@@ -0,0 +1,86 @@
<?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 GuzzleHttp\Exception\ClientException;
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 Tiniyo Gateway.
*
* @see https://tiniyo.com/sms.html
*/
class TiniyoGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'https://api.tiniyo.com/v1/Account/%s/Message';
const SUCCESS_CODE = '000000';
public function getName()
{
return 'tiniyo';
}
/**
* @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)
{
$accountSid = $config->get('account_sid');
$endpoint = $this->buildEndPoint($accountSid);
$params = [
'dst' => $to->getUniversalNumber(),
'src' => $config->get('from'),
'text' => $message->getContent($this),
];
$result = $this->request('post', $endpoint, [
'json' => $params,
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json;charset=utf-8',
'Authorization' => base64_encode($config->get('account_sid').':'.$config->get('token')),
],
]);
if (self::SUCCESS_CODE != $result['statusCode']) {
throw new GatewayErrorException($result['statusCode'], $result['statusCode'], $result);
}
return $result;
}
/**
* build endpoint url.
*
* @param string $accountSid
*
* @return string
*/
protected function buildEndPoint($accountSid)
{
return sprintf(self::ENDPOINT_URL, $accountSid);
}
}

View File

@@ -0,0 +1,91 @@
<?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 GuzzleHttp\Exception\ClientException;
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 TwilioGateway.
*
* @see https://www.twilio.com/docs/api/messaging/send-messages
*/
class TwilioGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'https://api.twilio.com/2010-04-01/Accounts/%s/Messages.json';
protected $errorStatuses = [
'failed',
'undelivered',
];
public function getName()
{
return 'twilio';
}
/**
* @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)
{
$accountSid = $config->get('account_sid');
$endpoint = $this->buildEndPoint($accountSid);
$params = [
'To' => $to->getUniversalNumber(),
'From' => $config->get('from'),
'Body' => $message->getContent($this),
];
try {
$result = $this->request('post', $endpoint, [
'auth' => [
$accountSid,
$config->get('token'),
],
'form_params' => $params,
]);
if (in_array($result['status'], $this->errorStatuses) || !is_null($result['error_code'])) {
throw new GatewayErrorException($result['message'], $result['error_code'], $result);
}
} catch (ClientException $e) {
throw new GatewayErrorException($e->getMessage(), $e->getCode());
}
return $result;
}
/**
* build endpoint url.
*
* @param string $accountSid
*
* @return string
*/
protected function buildEndPoint($accountSid)
{
return sprintf(self::ENDPOINT_URL, $accountSid);
}
}

View File

@@ -0,0 +1,130 @@
<?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 UcloudGateway.
*/
class UcloudGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_URL = 'https://api.ucloud.cn';
const ENDPOINT_Action = 'SendUSMSMessage';
const SUCCESS_CODE = 0;
/**
* Send Message.
*
* @param PhoneNumberInterface $to
* @param MessageInterface $message
* @param Config $config
*
* @return array
*
* @throws GatewayErrorException
*/
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$params = $this->buildParams($to, $message, $config);
$result = $this->get(self::ENDPOINT_URL, $params);
if (self::SUCCESS_CODE != $result['RetCode']) {
throw new GatewayErrorException($result['Message'], $result['RetCode'], $result);
}
return $result;
}
/**
* Build Params.
*
* @param PhoneNumberInterface $to
* @param MessageInterface $message
* @param Config $config
*
* @return array
*/
protected function buildParams(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$data = $message->getData($this);
$params = [
'Action' => self::ENDPOINT_Action,
'SigContent' => $config->get('sig_content'),
'TemplateId' => $message->getTemplate($this),
'PublicKey' => $config->get('public_key'),
];
$code = isset($data['code']) ? $data['code'] : '';
if (is_array($code) && !empty($code)) {
foreach ($code as $key => $value) {
$params['TemplateParams.'.$key] = $value;
}
} else {
if (!empty($code) || !is_null($code)) {
$params['TemplateParams.0'] = $code;
}
}
$mobiles = isset($data['mobiles']) ? $data['mobiles'] : '';
if (!empty($mobiles) && !is_null($mobiles)) {
if (is_array($mobiles)) {
foreach ($mobiles as $key => $value) {
$params['PhoneNumbers.'.$key] = $value;
}
} else {
$params['PhoneNumbers.0'] = $mobiles;
}
} else {
$params['PhoneNumbers.0'] = $to->getNumber();
}
if (!is_null($config->get('project_id')) && !empty($config->get('project_id'))) {
$params['ProjectId'] = $config->get('project_id');
}
$signature = $this->getSignature($params, $config->get('private_key'));
$params['Signature'] = $signature;
return $params;
}
/**
* Generate Sign.
*
* @param array $params
* @param string $privateKey
*
* @return string
*/
protected function getSignature($params, $privateKey)
{
ksort($params);
$paramsData = '';
foreach ($params as $key => $value) {
$paramsData .= $key;
$paramsData .= $value;
}
$paramsData .= $privateKey;
return sha1($paramsData);
}
}

View File

@@ -0,0 +1,77 @@
<?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 Ue35Gateway.
*
* @see https://shimo.im/docs/380b42d8cba24521
*/
class Ue35Gateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_HOST = 'sms.ue35.cn';
const ENDPOINT_URI = '/sms/interface/sendmess.htm';
const SUCCESS_CODE = 1;
/**
* 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 = [
'username' => $config->get('username'),
'userpwd' => $config->get('userpwd'),
'mobiles' => $to->getNumber(),
'content' => $message->getContent($this),
];
$headers = [
'host' => static::ENDPOINT_HOST,
'content-type' => 'application/json',
'user-agent' => 'PHP EasySms Client',
];
$result = $this->request('get', self::getEndpointUri().'?'.http_build_query($params), ['headers' => $headers]);
if (is_string($result)) {
$result = json_decode(json_encode(simplexml_load_string($result)), true);
}
if (self::SUCCESS_CODE != $result['errorcode']) {
throw new GatewayErrorException($result['message'], $result['errorcode'], $result);
}
return $result;
}
public static function getEndpointUri()
{
return 'http://'.static::ENDPOINT_HOST.static::ENDPOINT_URI;
}
}

View File

@@ -0,0 +1,99 @@
<?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 YunpianGateway.
*
* @see https://www.yunpian.com/doc/zh_CN/intl/single_send.html
*/
class YunpianGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'https://%s.yunpian.com/%s/%s/%s.%s';
const ENDPOINT_VERSION = 'v2';
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)
{
$template = $message->getTemplate($this);
$function = 'single_send';
$option = [
'form_params' => [
'apikey' => $config->get('api_key'),
'mobile' => $to->getUniversalNumber()
],
'exceptions' => false,
];
if(!is_null($template)){
$function = 'tpl_single_send';
$data = [];
foreach ($message->getData($this) ?? [] as $key => $value) {
$data[] = urlencode('#'.$key.'#') . '=' . urlencode($value);
}
$option['form_params'] = array_merge($option['form_params'],[
'tpl_id' => $template,
'tpl_value' => implode('&', $data)
]);
}else{
$content = $message->getContent($this);
$signature = $config->get('signature', '');
$option['form_params'] = array_merge($option['form_params'],[
'text' => 0 === \stripos($content, '【') ? $content : $signature.$content
]);
}
$endpoint = $this->buildEndpoint('sms', 'sms', $function);
$result = $this->request('post', $endpoint, $option);
if ($result['code']) {
throw new GatewayErrorException($result['msg'], $result['code'], $result);
}
return $result;
}
/**
* Build endpoint url.
*
* @param string $type
* @param string $resource
* @param string $function
*
* @return string
*/
protected function buildEndpoint($type, $resource, $function)
{
return sprintf(self::ENDPOINT_TEMPLATE, $type, self::ENDPOINT_VERSION, $resource, $function, self::ENDPOINT_FORMAT);
}
}

View File

@@ -0,0 +1,99 @@
<?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 YuntongxunGateway.
*
* @see http://www.yuntongxun.com/doc/rest/sms/3_2_2_2.html
*/
class YuntongxunGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'https://%s:%s/%s/%s/%s/%s/%s?sig=%s';
const SERVER_IP = 'app.cloopen.com';
const DEBUG_SERVER_IP = 'sandboxapp.cloopen.com';
const DEBUG_TEMPLATE_ID = 1;
const SERVER_PORT = '8883';
const SDK_VERSION = '2013-12-26';
const SUCCESS_CODE = '000000';
/**
* @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)
{
$datetime = date('YmdHis');
$endpoint = $this->buildEndpoint('SMS', 'TemplateSMS', $datetime, $config);
$result = $this->request('post', $endpoint, [
'json' => [
'to' => $to,
'templateId' => (int) ($this->config->get('debug') ? self::DEBUG_TEMPLATE_ID : $message->getTemplate($this)),
'appId' => $config->get('app_id'),
'datas' => $message->getData($this),
],
'headers' => [
'Accept' => 'application/json',
'Content-Type' => 'application/json;charset=utf-8',
'Authorization' => base64_encode($config->get('account_sid').':'.$datetime),
],
]);
if (self::SUCCESS_CODE != $result['statusCode']) {
throw new GatewayErrorException($result['statusCode'], $result['statusCode'], $result);
}
return $result;
}
/**
* Build endpoint url.
*
* @param string $type
* @param string $resource
* @param string $datetime
* @param \Overtrue\EasySms\Support\Config $config
*
* @return string
*/
protected function buildEndpoint($type, $resource, $datetime, Config $config)
{
$serverIp = $this->config->get('debug') ? self::DEBUG_SERVER_IP : self::SERVER_IP;
$accountType = $this->config->get('is_sub_account') ? 'SubAccounts' : 'Accounts';
$sig = strtoupper(md5($config->get('account_sid').$config->get('account_token').$datetime));
return sprintf(self::ENDPOINT_TEMPLATE, $serverIp, self::SERVER_PORT, self::SDK_VERSION, $accountType, $config->get('account_sid'), $type, $resource, $sig);
}
}

View File

@@ -0,0 +1,162 @@
<?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 YunxinGateway.
*
* @author her-cat <i@her-cat.com>
*
* @see https://dev.yunxin.163.com/docs/product/%E7%9F%AD%E4%BF%A1/%E7%9F%AD%E4%BF%A1%E6%8E%A5%E5%8F%A3%E6%8C%87%E5%8D%97
*/
class YunxinGateway extends Gateway
{
use HasHttpRequest;
const ENDPOINT_TEMPLATE = 'https://api.netease.im/%s/%s.action';
const ENDPOINT_ACTION = 'sendCode';
const SUCCESS_CODE = 200;
/**
* Send a short message.
*
* @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return array
*
* @throws GatewayErrorException
*/
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$data = $message->getData($this);
$action = isset($data['action']) ? $data['action'] : self::ENDPOINT_ACTION;
$endpoint = $this->buildEndpoint('sms', $action);
switch ($action) {
case 'sendCode':
$params = $this->buildSendCodeParams($to, $message, $config);
break;
case 'verifyCode':
$params = $this->buildVerifyCodeParams($to, $message);
break;
default:
throw new GatewayErrorException(sprintf('action: %s not supported', $action), 0);
}
$headers = $this->buildHeaders($config);
try {
$result = $this->post($endpoint, $params, $headers);
if (!isset($result['code']) || self::SUCCESS_CODE !== $result['code']) {
$code = isset($result['code']) ? $result['code'] : 0;
$error = isset($result['msg']) ? $result['msg'] : json_encode($result, JSON_UNESCAPED_UNICODE);
throw new GatewayErrorException($error, $code);
}
} catch (\Exception $e) {
throw new GatewayErrorException($e->getMessage(), $e->getCode());
}
return $result;
}
/**
* @param $resource
* @param $function
*
* @return string
*/
protected function buildEndpoint($resource, $function)
{
return sprintf(self::ENDPOINT_TEMPLATE, $resource, strtolower($function));
}
/**
* Get the request headers.
*
* @param Config $config
*
* @return array
*/
protected function buildHeaders(Config $config)
{
$headers = [
'AppKey' => $config->get('app_key'),
'Nonce' => md5(uniqid('easysms')),
'CurTime' => (string) time(),
'Content-Type' => 'application/x-www-form-urlencoded;charset=utf-8',
];
$headers['CheckSum'] = sha1("{$config->get('app_secret')}{$headers['Nonce']}{$headers['CurTime']}");
return $headers;
}
/**
* @param PhoneNumberInterface $to
* @param MessageInterface $message
* @param Config $config
*
* @return array
*/
public function buildSendCodeParams(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$data = $message->getData($this);
$template = $message->getTemplate($this);
return [
'mobile' => $to->getUniversalNumber(),
'authCode' => array_key_exists('code', $data) ? $data['code'] : '',
'deviceId' => array_key_exists('device_id', $data) ? $data['device_id'] : '',
'templateid' => is_string($template) ? $template : '',
'codeLen' => $config->get('code_length', 4),
'needUp' => $config->get('need_up', false),
];
}
/**
* @param PhoneNumberInterface $to
* @param MessageInterface $message
*
* @return array
*
* @throws GatewayErrorException
*/
public function buildVerifyCodeParams(PhoneNumberInterface $to, MessageInterface $message)
{
$data = $message->getData($this);
if (!array_key_exists('code', $data)) {
throw new GatewayErrorException('"code" cannot be empty', 0);
}
return [
'mobile' => $to->getUniversalNumber(),
'code' => $data['code'],
];
}
}

View File

@@ -0,0 +1,121 @@
<?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 YunzhixunGateway.
*
* @author her-cat <i@her-cat.com>
*
* @see http://docs.ucpaas.com/doku.php?id=%E7%9F%AD%E4%BF%A1:sendsms
*/
class YunzhixunGateway extends Gateway
{
use HasHttpRequest;
const SUCCESS_CODE = '000000';
const FUNCTION_SEND_SMS = 'sendsms';
const FUNCTION_BATCH_SEND_SMS = 'sendsms_batch';
const ENDPOINT_TEMPLATE = 'https://open.ucpaas.com/ol/%s/%s';
/**
* Send a short message.
*
* @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to
* @param \Overtrue\EasySms\Contracts\MessageInterface $message
* @param \Overtrue\EasySms\Support\Config $config
*
* @return array
*
* @throws GatewayErrorException
*/
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$data = $message->getData($this);
$function = isset($data['mobiles']) ? self::FUNCTION_BATCH_SEND_SMS : self::FUNCTION_SEND_SMS;
$endpoint = $this->buildEndpoint('sms', $function);
$params = $this->buildParams($to, $message, $config);
return $this->execute($endpoint, $params);
}
/**
* @param $resource
* @param $function
*
* @return string
*/
protected function buildEndpoint($resource, $function)
{
return sprintf(self::ENDPOINT_TEMPLATE, $resource, $function);
}
/**
* @param PhoneNumberInterface $to
* @param MessageInterface $message
* @param Config $config
*
* @return array
*/
protected function buildParams(PhoneNumberInterface $to, MessageInterface $message, Config $config)
{
$data = $message->getData($this);
return [
'sid' => $config->get('sid'),
'token' => $config->get('token'),
'appid' => $config->get('app_id'),
'templateid' => $message->getTemplate($this),
'uid' => isset($data['uid']) ? $data['uid'] : '',
'param' => isset($data['params']) ? $data['params'] : '',
'mobile' => isset($data['mobiles']) ? $data['mobiles'] : $to->getNumber(),
];
}
/**
* @param $endpoint
* @param $params
*
* @return array
*
* @throws GatewayErrorException
*/
protected function execute($endpoint, $params)
{
try {
$result = $this->postJson($endpoint, $params);
if (!isset($result['code']) || self::SUCCESS_CODE !== $result['code']) {
$code = isset($result['code']) ? $result['code'] : 0;
$error = isset($result['msg']) ? $result['msg'] : json_encode($result, JSON_UNESCAPED_UNICODE);
throw new GatewayErrorException($error, $code);
}
return $result;
} catch (\Exception $e) {
throw new GatewayErrorException($e->getMessage(), $e->getCode());
}
}
}