初始化项目
This commit is contained in:
@@ -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 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 (!empty($result['Code']) && 'OK' != $result['Code']) {
|
||||
throw new GatewayErrorException($result['Message'], $result['Code'], $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Sign.
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*
|
||||
* @see https://help.aliyun.com/document_detail/101343.html
|
||||
*/
|
||||
protected function generateSign($params)
|
||||
{
|
||||
ksort($params);
|
||||
$accessKeySecret = $this->config->get('access_key_secret');
|
||||
$stringToSign = 'GET&%2F&'.urlencode(http_build_query($params, '', '&', PHP_QUERY_RFC3986));
|
||||
$stringToSign = str_replace('%7E', '~', $stringToSign);
|
||||
|
||||
return base64_encode(hash_hmac('sha1', $stringToSign, $accessKeySecret.'&', true));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
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 AliyunIntlGateway
|
||||
*
|
||||
* @package \Overtrue\EasySms\Gateways
|
||||
*
|
||||
* @see https://www.alibabacloud.com/help/zh/doc-detail/162279.htm
|
||||
*/
|
||||
class AliyunIntlGateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
const ENDPOINT_URL = 'https://dysmsapi.ap-southeast-1.aliyuncs.com';
|
||||
|
||||
const ENDPOINT_ACTION = 'SendMessageWithTemplate';
|
||||
|
||||
const ENDPOINT_VERSION = '2018-05-01';
|
||||
|
||||
const ENDPOINT_FORMAT = 'JSON';
|
||||
|
||||
const ENDPOINT_REGION_ID = 'ap-southeast-1';
|
||||
|
||||
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('', true),
|
||||
'Timestamp' => gmdate('Y-m-d\TH:i:s\Z'),
|
||||
'Version' => self::ENDPOINT_VERSION,
|
||||
'To' => !\is_null($to->getIDDCode()) ? (int) $to->getZeroPrefixedNumber() : $to->getNumber(),
|
||||
'Action' => self::ENDPOINT_ACTION,
|
||||
'From' => $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['ResponseCode']) {
|
||||
throw new GatewayErrorException($result['ResponseDescription'], $result['ResponseCode'], $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate sign
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateSign(array $params)
|
||||
{
|
||||
ksort($params);
|
||||
$accessKeySecret = $this->config->get('access_key_secret');
|
||||
$stringToSign = 'GET&%2F&'.urlencode(http_build_query($params, '', '&', PHP_QUERY_RFC3986));
|
||||
|
||||
return base64_encode(hash_hmac('sha1', $stringToSign, $accessKeySecret.'&', true));
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?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://www.chuanglan.com/document/6110e57909fd9600010209de/62b3dc1d272e290001af3e75
|
||||
*/
|
||||
class Chuanglanv1Gateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
/**
|
||||
* 国际短信
|
||||
*/
|
||||
const INT_URL = 'http://intapi.253.com/send/json';
|
||||
|
||||
/**
|
||||
* URL模板
|
||||
*/
|
||||
const ENDPOINT_URL_TEMPLATE = 'https://smssh1.253.com/msg/%s/json';
|
||||
|
||||
/**
|
||||
* 支持单发、群发短信
|
||||
*/
|
||||
const CHANNEL_NORMAL_CODE = 'v1/send';
|
||||
|
||||
/**
|
||||
* 单号码对应单内容批量下发
|
||||
*/
|
||||
const CHANNEL_VARIABLE_CODE = 'variable';
|
||||
|
||||
/**
|
||||
* @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'),
|
||||
'report' => $config->get('needstatus') ?? false
|
||||
];
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
if (self::CHANNEL_VARIABLE_CODE == $this->getChannel($config, $IDDCode)) {
|
||||
$params['params'] = $message->getData($this);
|
||||
$params['msg'] = $this->wrapChannelContent($message->getTemplate($this), $config, $IDDCode);
|
||||
} else {
|
||||
$params['phone'] = $to->getNumber();
|
||||
$params['msg'] = $this->wrapChannelContent($message->getContent($this), $config, $IDDCode);
|
||||
}
|
||||
|
||||
$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_NORMAL_CODE);
|
||||
|
||||
if (!in_array($channel, [self::CHANNEL_NORMAL_CODE, self::CHANNEL_VARIABLE_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)
|
||||
{
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -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)));
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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']);
|
||||
}
|
||||
}
|
||||
@@ -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 urldecode(http_build_query($formatted));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
<?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 MaapGateway.
|
||||
*
|
||||
* @see https://maap.wo.cn/
|
||||
*/
|
||||
class MaapGateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
const ENDPOINT_URL = 'http://rcsapi.wo.cn:8000/umcinterface/sendtempletmsg';
|
||||
|
||||
/**
|
||||
* 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 = [
|
||||
'cpcode' => $config->get('cpcode'),
|
||||
'msg' => implode(',', $message->getData($this)),
|
||||
'mobiles' => $to->getNumber(),
|
||||
'excode' => $config->get('excode', ''),
|
||||
'templetid' => $message->getTemplate($this),
|
||||
];
|
||||
$params['sign'] = $this->generateSign($params, $config->get('key'));
|
||||
|
||||
$result = $this->postJson(self::ENDPOINT_URL, $params);
|
||||
|
||||
if (0 != $result['resultcode']) {
|
||||
throw new GatewayErrorException($result['resultmsg'], $result['resultcode'], $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Sign.
|
||||
*
|
||||
* @param array $params
|
||||
* @param string $key 签名Key
|
||||
* @return string
|
||||
*/
|
||||
protected function generateSign($params, $key)
|
||||
{
|
||||
return md5($params['cpcode'] . $params['msg'] . $params['mobiles'] . $params['excode'] . $params['templetid'] . $key);
|
||||
}
|
||||
}
|
||||
@@ -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 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->postJson($this->getEndpointUrl($urlParams), $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']
|
||||
));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
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 NowcnGateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
const ENDPOINT_URL = 'http://ad1200.now.net.cn:2003/sms/sendSMS';
|
||||
|
||||
const SUCCESS_CODE = 0;
|
||||
|
||||
public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config)
|
||||
{
|
||||
if (!$config->get('key')) {
|
||||
throw new GatewayErrorException("key not found", -2, []);
|
||||
}
|
||||
$params = [
|
||||
'mobile' => $to->getNumber(),
|
||||
'content' => $message->getContent($this),
|
||||
'userId' => $config->get('key'),
|
||||
'password' => $config->get('secret'),
|
||||
'apiType' => $config->get('api_type'),
|
||||
];
|
||||
$result = $this->get(self::ENDPOINT_URL, $params);
|
||||
$result = is_string($result) ? json_decode($result, true) : $result;
|
||||
if (self::SUCCESS_CODE != $result['code']) {
|
||||
throw new GatewayErrorException($result['msg'], $result['code'], $result);
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
<?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/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;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?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 RongheyunGateway.
|
||||
*
|
||||
* @see https://doc.zthysms.com/web/#/1?page_id=13
|
||||
*/
|
||||
class RongheyunGateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
const ENDPOINT_URL = 'https://api.mix2.zthysms.com/v2/sendSmsTp';
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
$tKey = time();
|
||||
$password = md5(md5($config->get('password')) . $tKey);
|
||||
$params = [
|
||||
'username' => $config->get('username', ''),
|
||||
'password' => $password,
|
||||
'tKey' => $tKey,
|
||||
'signature' => $config->get('signature', ''),
|
||||
'tpId' => $message->getTemplate($this),
|
||||
'ext' => '',
|
||||
'extend' => '',
|
||||
'records' => [
|
||||
'mobile' => $to->getNumber(),
|
||||
'tpContent' => $message->getData($this),
|
||||
],
|
||||
];
|
||||
|
||||
$result = $this->postJson(
|
||||
self::ENDPOINT_URL,
|
||||
$params,
|
||||
['Content-Type' => 'application/json; charset="UTF-8"']
|
||||
);
|
||||
if (200 != $result['code']) {
|
||||
throw new GatewayErrorException($result['msg'], $result['code'], $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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 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()) || $to->getIDDCode() == '86') {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<?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)
|
||||
{
|
||||
$isContent = !!$message->getContent($this);
|
||||
if ($isContent) {
|
||||
$endpoint = $this->buildEndpoint($this->inChineseMainland($to) ? 'sms/send' : 'internationalsms/send');
|
||||
$params = [
|
||||
'appid' => $config->get('app_id'),
|
||||
'signature' => $config->get('app_key'),
|
||||
'content' => $message->getContent($this),
|
||||
'to' => $to->getUniversalNumber(),
|
||||
];
|
||||
} else {
|
||||
$endpoint = $this->buildEndpoint($this->inChineseMainland($to) ? 'message/xsend' : 'internationalsms/xsend');
|
||||
$data = $message->getData($this);
|
||||
$template_code = $message->getTemplate($this);
|
||||
$params = [
|
||||
'appid' => $config->get('app_id'),
|
||||
'signature' => $config->get('app_key'),
|
||||
'project' => !empty($template_code) ? $template_code : (!empty($data['project']) ? $data['project'] : $config->get('project')),
|
||||
'to' => $to->getUniversalNumber(),
|
||||
'vars' => json_encode($data, JSON_FORCE_OBJECT),
|
||||
];
|
||||
}
|
||||
|
||||
$result = $this->post($endpoint, $params);
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
<?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 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);
|
||||
}
|
||||
}
|
||||
@@ -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\Support\Config;
|
||||
use Overtrue\EasySms\Traits\HasHttpRequest;
|
||||
use Overtrue\EasySms\Contracts\MessageInterface;
|
||||
use Overtrue\EasySms\Contracts\PhoneNumberInterface;
|
||||
use Overtrue\EasySms\Exceptions\GatewayErrorException;
|
||||
|
||||
/**
|
||||
* Class TinreeGateway.
|
||||
*
|
||||
* @see http://cms.tinree.com
|
||||
*/
|
||||
class TinreeGateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
const ENDPOINT_URL = 'http://api.tinree.com/api/v2/single_send';
|
||||
|
||||
/**
|
||||
* @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 = [
|
||||
'accesskey' => $config->get('accesskey'),
|
||||
'secret' => $config->get('secret'),
|
||||
'sign' => $config->get('sign'),
|
||||
'templateId' => $message->getTemplate($this),
|
||||
'mobile' => $to->getNumber(),
|
||||
'content' => $this->buildContent($message),
|
||||
];
|
||||
|
||||
$result = $this->post(self::ENDPOINT_URL, $params);
|
||||
|
||||
if (0 != $result['code']) {
|
||||
throw new GatewayErrorException($result['msg'], $result['code'], $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 构建发送内容
|
||||
* 用 data 数据合成内容,或者直接使用 data 的值
|
||||
*
|
||||
* @param MessageInterface $message
|
||||
* @return string
|
||||
*/
|
||||
protected function buildContent(MessageInterface $message)
|
||||
{
|
||||
$data = $message->getData($this);
|
||||
|
||||
if (is_array($data)) {
|
||||
return implode("##", $data);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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' => !empty($data['sig_content']) ? $data['sig_content'] : $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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,311 @@
|
||||
<?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 GuzzleHttp\HandlerStack;
|
||||
use GuzzleHttp\Psr7\Query;
|
||||
use GuzzleHttp\Psr7\Utils;
|
||||
use GuzzleHttp\Psr7;
|
||||
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;
|
||||
use Psr\Http\Message\RequestInterface;
|
||||
|
||||
/**
|
||||
* Class VolcengineGateway.
|
||||
*
|
||||
* @see https://www.volcengine.com/docs/6361/66704
|
||||
*/
|
||||
class VolcengineGateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
const ENDPOINT_ACTION = 'SendSms';
|
||||
const ENDPOINT_VERSION = '2020-01-01';
|
||||
const ENDPOINT_CONTENT_TYPE = 'application/json; charset=utf-8';
|
||||
const ENDPOINT_ACCEPT = 'application/json';
|
||||
const ENDPOINT_USER_AGENT = 'overtrue/easy-sms';
|
||||
const ENDPOINT_SERVICE = 'volcSMS';
|
||||
|
||||
const Algorithm = 'HMAC-SHA256';
|
||||
|
||||
const ENDPOINT_DEFAULT_REGION_ID = 'cn-north-1';
|
||||
|
||||
public static $endpoints = [
|
||||
'cn-north-1' => 'https://sms.volcengineapi.com',
|
||||
'ap-singapore-1' => 'https://sms.byteplusapi.com',
|
||||
];
|
||||
|
||||
private $regionId = self::ENDPOINT_DEFAULT_REGION_ID;
|
||||
protected $requestDate;
|
||||
|
||||
|
||||
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');
|
||||
$smsAccount = !empty($data['sms_account']) ? $data['sms_account'] : $config->get('sms_account');
|
||||
$templateId = $message->getTemplate($this);
|
||||
$phoneNumbers = !empty($data['phone_numbers']) ? $data['phone_numbers'] : $to->getNumber();
|
||||
$templateParam = !empty($data['template_param']) ? $data['template_param'] : $message->getData($this);
|
||||
|
||||
$tag = !empty($data['tag']) ? $data['tag'] : '';
|
||||
|
||||
$payload = [
|
||||
'SmsAccount' => $smsAccount, // 消息组帐号,火山短信页面右上角,短信应用括号中的字符串
|
||||
'Sign' => $signName, // 短信签名
|
||||
'TemplateID' => $templateId, // 短信模板ID
|
||||
'TemplateParam' => json_encode($templateParam), // 短信模板占位符要替换的值
|
||||
'PhoneNumbers' => $phoneNumbers, // 手机号,如果有多个使用英文逗号分割
|
||||
];
|
||||
if ($tag) {
|
||||
$payload['Tag'] = $tag;
|
||||
}
|
||||
$queries = [
|
||||
'Action' => self::ENDPOINT_ACTION,
|
||||
'Version' => self::ENDPOINT_VERSION,
|
||||
];
|
||||
|
||||
try {
|
||||
$stack = HandlerStack::create();
|
||||
$stack->push($this->signHandle());
|
||||
$this->setGuzzleOptions([
|
||||
'headers' => [
|
||||
'Content-Type' => self::ENDPOINT_CONTENT_TYPE,
|
||||
'Accept' => self::ENDPOINT_ACCEPT,
|
||||
'User-Agent' => self::ENDPOINT_USER_AGENT
|
||||
],
|
||||
'timeout' => $this->getTimeout(),
|
||||
'handler' => $stack,
|
||||
'base_uri' => $this->getEndpoint(),
|
||||
]);
|
||||
|
||||
$response = $this->request('post', $this->getEndpoint().$this->getCanonicalURI(), [
|
||||
'query' => $queries,
|
||||
'json' => $payload,
|
||||
]);
|
||||
if ($response instanceof Psr7\Response) {
|
||||
$response = json_decode($response->getBody()->getContents(), true);
|
||||
}
|
||||
if (isset($response['ResponseMetadata']['Error'])) { // 请求错误参数,如果请求没有错误,则不存在该参数返回
|
||||
// 火山引擎错误码格式为:'ZJ'+ 5位数字,比如 ZJ20009,取出数字部分
|
||||
preg_match('/\d+/', $response['ResponseMetadata']['Error']['Code'], $matches);
|
||||
throw new GatewayErrorException($response['ResponseMetadata']['Error']['Code'].":".$response['ResponseMetadata']['Error']['Message'], $matches[0], $response);
|
||||
}
|
||||
return $response;
|
||||
} catch (ClientException $exception) {
|
||||
$responseContent = $exception->getResponse()->getBody()->getContents();
|
||||
$response = json_decode($responseContent, true);
|
||||
if (isset($response['ResponseMetadata']['Error']) && $error = $response['ResponseMetadata']['Error']) { // 请求错误参数,如果请求没有错误,则不存在该参数返回
|
||||
// 火山引擎公共错误码Error与业务错误码略有不同,比如:"Error":{"CodeN":100004,"Code":"MissingRequestInfo","Message":"The request is missing timestamp information."}
|
||||
// 此处错误码直接取 CodeN
|
||||
throw new GatewayErrorException($error["CodeN"].":".$error['Message'], $error["CodeN"], $response);
|
||||
}
|
||||
throw new GatewayErrorException($responseContent, $exception->getCode(), ['content' => $responseContent]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function signHandle()
|
||||
{
|
||||
return function (callable $handler) {
|
||||
return function (RequestInterface $request, array $options) use ($handler) {
|
||||
$request = $request->withHeader('X-Date', $this->getRequestDate());
|
||||
list($canonicalHeaders, $signedHeaders) = $this->getCanonicalHeaders($request);
|
||||
|
||||
$queries = Query::parse($request->getUri()->getQuery());
|
||||
$canonicalRequest = $request->getMethod()."\n"
|
||||
.$this->getCanonicalURI()."\n"
|
||||
.$this->getCanonicalQueryString($queries)."\n"
|
||||
.$canonicalHeaders."\n"
|
||||
.$signedHeaders."\n"
|
||||
.$this->getPayloadHash($request);
|
||||
|
||||
$stringToSign = $this->getStringToSign($canonicalRequest);
|
||||
|
||||
$signingKey = $this->getSigningKey();
|
||||
|
||||
$signature = hash_hmac('sha256', $stringToSign, $signingKey);
|
||||
$parsed = $this->parseRequest($request);
|
||||
|
||||
$parsed['headers']['Authorization'] = self::Algorithm.
|
||||
' Credential='.$this->getAccessKeyId().'/'.$this->getCredentialScope().', SignedHeaders='.$signedHeaders.', Signature='.$signature;
|
||||
|
||||
$buildRequest = function () use ($request, $parsed) {
|
||||
if ($parsed['query']) {
|
||||
$parsed['uri'] = $parsed['uri']->withQuery(Query::build($parsed['query']));
|
||||
}
|
||||
|
||||
return new Psr7\Request(
|
||||
$parsed['method'],
|
||||
$parsed['uri'],
|
||||
$parsed['headers'],
|
||||
$parsed['body'],
|
||||
$parsed['version']
|
||||
);
|
||||
};
|
||||
|
||||
return $handler($buildRequest(), $options);
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
private function parseRequest(RequestInterface $request)
|
||||
{
|
||||
$uri = $request->getUri();
|
||||
return [
|
||||
'method' => $request->getMethod(),
|
||||
'path' => $uri->getPath(),
|
||||
'query' => Query::parse($uri->getQuery()),
|
||||
'uri' => $uri,
|
||||
'headers' => $request->getHeaders(),
|
||||
'body' => $request->getBody(),
|
||||
'version' => $request->getProtocolVersion()
|
||||
];
|
||||
}
|
||||
|
||||
public function getPayloadHash(RequestInterface $request)
|
||||
{
|
||||
if ($request->hasHeader('X-Content-Sha256')) {
|
||||
return $request->getHeaderLine('X-Content-Sha256');
|
||||
}
|
||||
|
||||
return Utils::hash($request->getBody(), 'sha256');
|
||||
}
|
||||
|
||||
public function getRegionId()
|
||||
{
|
||||
return $this->config->get('region_id', self::ENDPOINT_DEFAULT_REGION_ID);
|
||||
}
|
||||
|
||||
public function getEndpoint()
|
||||
{
|
||||
$regionId = $this->getRegionId();
|
||||
if (!in_array($regionId, array_keys(self::$endpoints))) {
|
||||
$regionId = self::ENDPOINT_DEFAULT_REGION_ID;
|
||||
}
|
||||
return static::$endpoints[$regionId];
|
||||
}
|
||||
|
||||
public function getRequestDate()
|
||||
{
|
||||
return $this->requestDate ?: gmdate('Ymd\THis\Z');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 指代信任状,格式为:YYYYMMDD/region/service/request
|
||||
* @return string
|
||||
*/
|
||||
public function getCredentialScope()
|
||||
{
|
||||
return date('Ymd', strtotime($this->getRequestDate())).'/'.$this->getRegionId().'/'.self::ENDPOINT_SERVICE.'/request';
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算签名密钥
|
||||
* 在计算签名前,首先从私有访问密钥(Secret Access Key)派生出签名密钥(signing key),而不是直接使用私有访问密钥。具体计算过程如下:
|
||||
* kSecret = *Your Secret Access Key*
|
||||
* kDate = HMAC(kSecret, Date)
|
||||
* kRegion = HMAC(kDate, Region)
|
||||
* kService = HMAC(kRegion, Service)
|
||||
* kSigning = HMAC(kService, "request")
|
||||
* 其中Date精确到日,与RequestDate中YYYYMMDD部分相同。
|
||||
* @return string
|
||||
*/
|
||||
protected function getSigningKey()
|
||||
{
|
||||
$dateKey = hash_hmac('sha256', date("Ymd", strtotime($this->getRequestDate())), $this->getAccessKeySecret(), true);
|
||||
$regionKey = hash_hmac('sha256', $this->getRegionId(), $dateKey, true);
|
||||
$serviceKey = hash_hmac('sha256', self::ENDPOINT_SERVICE, $regionKey, true);
|
||||
return hash_hmac('sha256', 'request', $serviceKey, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建签名字符串
|
||||
* 签名字符串主要包含请求以及正规化请求的元数据信息,由签名算法、请求日期、信任状和正规化请求哈希值连接组成,伪代码如下:
|
||||
* StringToSign = Algorithm + '\n' + RequestDate + '\n' + CredentialScope + '\n' + HexEncode(Hash(CanonicalRequest))
|
||||
* @return string
|
||||
*/
|
||||
public function getStringToSign($canonicalRequest)
|
||||
{
|
||||
return self::Algorithm."\n".$this->getRequestDate()."\n".$this->getCredentialScope()."\n".hash('sha256', $canonicalRequest);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessKeySecret()
|
||||
{
|
||||
return $this->config->get('access_key_secret');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
public function getAccessKeyId()
|
||||
{
|
||||
return $this->config->get('access_key_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 指代正规化后的Header。
|
||||
* 其中伪代码如下:
|
||||
* CanonicalHeaders = CanonicalHeadersEntry0 + CanonicalHeadersEntry1 + ... + CanonicalHeadersEntryN
|
||||
* 其中CanonicalHeadersEntry = Lowercase(HeaderName) + ':' + Trimall(HeaderValue) + '\n'
|
||||
* Lowcase代表将Header的名称全部转化成小写,Trimall表示去掉Header的值的前后多余的空格。
|
||||
* 特别注意:最后需要添加"\n"的换行符,header的顺序是以headerName的小写后ascii排序。
|
||||
* @return array
|
||||
*/
|
||||
public function getCanonicalHeaders(RequestInterface $request)
|
||||
{
|
||||
$headers = $request->getHeaders();
|
||||
ksort($headers);
|
||||
$canonicalHeaders = '';
|
||||
$signedHeaders = [];
|
||||
foreach ($headers as $key => $val) {
|
||||
$lowerKey = strtolower($key);
|
||||
$canonicalHeaders .= $lowerKey.':'.trim($val[0]).PHP_EOL;
|
||||
$signedHeaders[] = $lowerKey;
|
||||
}
|
||||
$signedHeadersString = implode(';', $signedHeaders);
|
||||
return [$canonicalHeaders, $signedHeadersString];
|
||||
}
|
||||
|
||||
/**
|
||||
* urlencode(注:同RFC3986方法)每一个querystring参数名称和参数值。
|
||||
* 按照ASCII字节顺序对参数名称严格排序,相同参数名的不同参数值需保持请求的原始顺序。
|
||||
* 将排序好的参数名称和参数值用=连接,按照排序结果将“参数对”用&连接。
|
||||
* 例如:CanonicalQueryString = "Action=ListUsers&Version=2018-01-01"
|
||||
* @return string
|
||||
*/
|
||||
public function getCanonicalQueryString(array $query)
|
||||
{
|
||||
ksort($query);
|
||||
return http_build_query($query);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指代正规化后的URI。
|
||||
* 如果URI为空,那么使用"/"作为绝对路径。
|
||||
* 在火山引擎中绝大多数接口的URI都为"/"。
|
||||
* 如果是复杂的path,请通过RFC3986规范进行编码。
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getCanonicalURI()
|
||||
{
|
||||
return '/';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
<?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 YidongmasblackGateway.
|
||||
* 移动MAS黑名单模式(免创建模板)
|
||||
* @author houang <mail@houang.cn>
|
||||
*
|
||||
* @see https://mas.10086.cn
|
||||
*/
|
||||
class YidongmasblackGateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
const ENDPOINT_URL = 'http://112.35.1.155:1992/sms/norsubmit';
|
||||
|
||||
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["ecName"] = $config->get('ecName');
|
||||
$params["apId"] = $config->get('apId');
|
||||
$params["sign"] = $config->get('sign');
|
||||
$params["addSerial"] = $config->get('addSerial');
|
||||
$params["mobiles"] = $to->getNumber();
|
||||
$params["content"] = $message->getContent();
|
||||
$result = $this->postJson(self::ENDPOINT_URL, $this->generateContent($params));
|
||||
|
||||
if ('true' != $result['success']) {
|
||||
throw new GatewayErrorException($result['success'], $result['rspcod'], $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate Content.
|
||||
*
|
||||
* @param array $params
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function generateContent($params)
|
||||
{
|
||||
$secretKey = $this->config->get('secretKey');
|
||||
$params['mac'] = md5($params["ecName"].$params["apId"].$secretKey.$params["mobiles"].$params["content"].$params["sign"].$params["addSerial"]);
|
||||
|
||||
return base64_encode(json_encode($params));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?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 = [];
|
||||
|
||||
$templateData = $message->getData($this);
|
||||
$templateData = isset($templateData) ? $templateData : [];
|
||||
foreach ($templateData 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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
<?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 Chinese Mainland: http://doc.yuntongxun.com/pe/5a533de33b8496dd00dce07c
|
||||
* @see International: http://doc.yuntongxun.com/pe/604f29eda80948a1006e928d
|
||||
*/
|
||||
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 SDK_VERSION_INT = 'v2';
|
||||
|
||||
const SUCCESS_CODE = '000000';
|
||||
|
||||
private $international = false; // if international SMS, default false means no.
|
||||
|
||||
/**
|
||||
* @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');
|
||||
|
||||
$data = [
|
||||
'appId' => $config->get('app_id'),
|
||||
];
|
||||
|
||||
if ($to->inChineseMainland()) {
|
||||
$type = 'SMS';
|
||||
$resource = 'TemplateSMS';
|
||||
$data['to'] = $to->getNumber();
|
||||
$data['templateId'] = (int) ($this->config->get('debug') ? self::DEBUG_TEMPLATE_ID : $message->getTemplate($this));
|
||||
$data['datas'] = $message->getData($this);
|
||||
} else {
|
||||
$type = 'international';
|
||||
$resource = 'send';
|
||||
$this->international = true;
|
||||
$data['mobile'] = $to->getZeroPrefixedNumber();
|
||||
$data['content'] = $message->getContent($this);
|
||||
}
|
||||
|
||||
$endpoint = $this->buildEndpoint($type, $resource, $datetime, $config);
|
||||
|
||||
$result = $this->request('post', $endpoint, [
|
||||
'json' => $data,
|
||||
'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;
|
||||
|
||||
if ($this->international) {
|
||||
$accountType = 'account';
|
||||
$sdkVersion = self::SDK_VERSION_INT;
|
||||
} else {
|
||||
$accountType = $this->config->get('is_sub_account') ? 'SubAccounts' : 'Accounts';
|
||||
$sdkVersion = self::SDK_VERSION;
|
||||
}
|
||||
|
||||
$sig = strtoupper(md5($config->get('account_sid').$config->get('account_token').$datetime));
|
||||
|
||||
return sprintf(self::ENDPOINT_TEMPLATE, $serverIp, self::SERVER_PORT, $sdkVersion, $accountType, $config->get('account_sid'), $type, $resource, $sig);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
<?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;
|
||||
case "sendTemplate":
|
||||
$params = $this->buildTemplateParams($to, $message, $config);
|
||||
|
||||
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'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param PhoneNumberInterface $to
|
||||
* @param MessageInterface $message
|
||||
* @param Config $config
|
||||
* @return array
|
||||
*
|
||||
*/
|
||||
public function buildTemplateParams(PhoneNumberInterface $to, MessageInterface $message, Config $config)
|
||||
{
|
||||
$data = $message->getData($this);
|
||||
|
||||
$template = $message->getTemplate($this);
|
||||
|
||||
return [
|
||||
'templateid'=>$template,
|
||||
'mobiles'=>json_encode([$to->getUniversalNumber()]),
|
||||
'params'=>array_key_exists('params',$data) ? json_encode($data['params']) : '',
|
||||
'needUp'=>$config->get('need_up', false)
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
<?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 RongheyunGateway.
|
||||
*
|
||||
* @see https://zzyun.com/
|
||||
*/
|
||||
class ZzyunGateway extends Gateway
|
||||
{
|
||||
use HasHttpRequest;
|
||||
|
||||
const ENDPOINT_URL = 'https://zzyun.com/api/sms/sendByTplCode';
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
$time = time();
|
||||
$user_id = $config->get('user_id');
|
||||
$token = md5($time . $user_id . $config->get('secret'));
|
||||
$params = [
|
||||
'user_id' => $user_id,
|
||||
'time' => $time,
|
||||
'token' => $token,
|
||||
'mobiles' => $to->getNumber(),// 手机号码,多个英文逗号隔开
|
||||
'tpl_code' => $message->getTemplate($this),
|
||||
'tpl_params' => $message->getData($this),
|
||||
'sign_name' => $config->get('sign_name'),
|
||||
];
|
||||
|
||||
$result = $this->post(self::ENDPOINT_URL, $params);
|
||||
|
||||
if ('Success' != $result['Code']) {
|
||||
throw new GatewayErrorException($result['Message'], $result['Code'], $result);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user