diff --git a/app/common/service/SmsService.php b/app/common/service/SmsService.php new file mode 100644 index 00000000..43566a1c --- /dev/null +++ b/app/common/service/SmsService.php @@ -0,0 +1,65 @@ + 5.0, + + // 默认发送配置 + 'default' => [ + // 网关调用策略,默认:顺序调用 + 'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class, + + // 默认可用的发送网关 + 'gateways' => [ + 'yunpian', 'aliyun', + ], + ], + // 可用的网关配置 + 'gateways' => [ + 'errorlog' => [ + 'file' => runtime_path() . '/logs/' . date('Ymd') . '/easy-sms.log', + ], + 'aliyun' => [ + 'access_key_id' => 'LTAI5t7mhH3ij2cNWs1zhPmv', + 'access_key_secret' => 'gqo2wMpvi8h5bDBmCpMje6BaiXvcPu', + 'sign_name' => '里海科技', + ], + //... + ], + ]; + $this->config=$config; + + } + + public function client($phone,$template,$code) + { + try{ + $easySms = new EasySms($this->config); + + $easySms->send($phone, [ + 'content' => '您的验证码为: '.$code, + 'template' => $template, + 'data' => [ + 'code' => $code + ], + ]); + }catch(NoGatewayAvailableException $e){ + throw new BusinessException($e->getMessage()); + } + + } +} diff --git a/composer.json b/composer.json index 6906f2ce..5a665d60 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,8 @@ "jpush/jpush": "^3.6", "workerman/crontab": "^1.0", "intervention/image": "^3.6", - "picqer/php-barcode-generator": "^2.4" + "picqer/php-barcode-generator": "^2.4", + "overtrue/easy-sms": "^2.6" }, "suggest": { "ext-event": "For better performance. " diff --git a/composer.lock b/composer.lock index 6d7daedd..02626c93 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "974978f64812f55d8825aabadc43739f", + "content-hash": "54acb0fe3bc70f1e94406e7b79e84b8a", "packages": [ { "name": "aliyuncs/oss-sdk-php", @@ -2779,6 +2779,78 @@ ], "time": "2023-11-08T09:30:43+00:00" }, + { + "name": "overtrue/easy-sms", + "version": "2.6.0", + "source": { + "type": "git", + "url": "https://github.com/overtrue/easy-sms.git", + "reference": "bb88b244f0de8d1f74bc50c4c08414f4c5f30281" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/overtrue/easy-sms/zipball/bb88b244f0de8d1f74bc50c4c08414f4c5f30281", + "reference": "bb88b244f0de8d1f74bc50c4c08414f4c5f30281", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.2 || ^7.0", + "php": ">=5.6" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "^2.8", + "jetbrains/phpstorm-attributes": "^1.0", + "mockery/mockery": "~1.3.3 || ^1.4.2", + "phpunit/phpunit": "^5.7 || ^7.5 || ^8.5.19 || ^9.5.8" + }, + "type": "library", + "extra": { + "hooks": { + "pre-commit": [ + "composer check-style", + "composer psalm", + "composer test" + ], + "pre-push": [ + "composer check-style" + ] + } + }, + "autoload": { + "psr-4": { + "Overtrue\\EasySms\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "overtrue", + "email": "i@overtrue.me" + } + ], + "description": "The easiest way to send short message.", + "support": { + "issues": "https://github.com/overtrue/easy-sms/issues", + "source": "https://github.com/overtrue/easy-sms/tree/2.6.0" + }, + "funding": [ + { + "url": "https://github.com/overtrue", + "type": "github" + } + ], + "time": "2024-03-08T06:36:45+00:00" + }, { "name": "overtrue/socialite", "version": "4.10.1", diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 15491c6e..348204dd 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -72,6 +72,7 @@ return array( 'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'), 'PhpDocReader\\' => array($vendorDir . '/php-di/phpdoc-reader/src/PhpDocReader'), 'Overtrue\\Socialite\\' => array($vendorDir . '/overtrue/socialite/src'), + 'Overtrue\\EasySms\\' => array($vendorDir . '/overtrue/easy-sms/src'), 'OSS\\' => array($vendorDir . '/aliyuncs/oss-sdk-php/src/OSS'), 'Nyholm\\Psr7\\' => array($vendorDir . '/nyholm/psr7/src'), 'Nyholm\\Psr7Server\\' => array($vendorDir . '/nyholm/psr7-server/src'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index e2aa1e0f..a6ee1799 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -145,6 +145,7 @@ class ComposerStaticInitcefecbcff919f3c1c8084830bbb72adc 'O' => array ( 'Overtrue\\Socialite\\' => 19, + 'Overtrue\\EasySms\\' => 17, 'OSS\\' => 4, ), 'N' => @@ -492,6 +493,10 @@ class ComposerStaticInitcefecbcff919f3c1c8084830bbb72adc array ( 0 => __DIR__ . '/..' . '/overtrue/socialite/src', ), + 'Overtrue\\EasySms\\' => + array ( + 0 => __DIR__ . '/..' . '/overtrue/easy-sms/src', + ), 'OSS\\' => array ( 0 => __DIR__ . '/..' . '/aliyuncs/oss-sdk-php/src/OSS', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index fc3ca934..9dfcf3b4 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2893,6 +2893,81 @@ ], "install-path": "../nyholm/psr7-server" }, + { + "name": "overtrue/easy-sms", + "version": "2.6.0", + "version_normalized": "2.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/overtrue/easy-sms.git", + "reference": "bb88b244f0de8d1f74bc50c4c08414f4c5f30281" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/overtrue/easy-sms/zipball/bb88b244f0de8d1f74bc50c4c08414f4c5f30281", + "reference": "bb88b244f0de8d1f74bc50c4c08414f4c5f30281", + "shasum": "", + "mirrors": [ + { + "url": "https://mirrors.aliyun.com/composer/dists/%package%/%reference%.%type%", + "preferred": true + } + ] + }, + "require": { + "ext-json": "*", + "guzzlehttp/guzzle": "^6.2 || ^7.0", + "php": ">=5.6" + }, + "require-dev": { + "brainmaestro/composer-git-hooks": "^2.8", + "jetbrains/phpstorm-attributes": "^1.0", + "mockery/mockery": "~1.3.3 || ^1.4.2", + "phpunit/phpunit": "^5.7 || ^7.5 || ^8.5.19 || ^9.5.8" + }, + "time": "2024-03-08T06:36:45+00:00", + "type": "library", + "extra": { + "hooks": { + "pre-commit": [ + "composer check-style", + "composer psalm", + "composer test" + ], + "pre-push": [ + "composer check-style" + ] + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Overtrue\\EasySms\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "overtrue", + "email": "i@overtrue.me" + } + ], + "description": "The easiest way to send short message.", + "support": { + "issues": "https://github.com/overtrue/easy-sms/issues", + "source": "https://github.com/overtrue/easy-sms/tree/2.6.0" + }, + "funding": [ + { + "url": "https://github.com/overtrue", + "type": "github" + } + ], + "install-path": "../overtrue/easy-sms" + }, { "name": "overtrue/socialite", "version": "4.10.1", diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 83a57723..b1b2bed4 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -376,6 +376,15 @@ 'aliases' => array(), 'dev_requirement' => false, ), + 'overtrue/easy-sms' => array( + 'pretty_version' => '2.6.0', + 'version' => '2.6.0.0', + 'reference' => 'bb88b244f0de8d1f74bc50c4c08414f4c5f30281', + 'type' => 'library', + 'install_path' => __DIR__ . '/../overtrue/easy-sms', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'overtrue/socialite' => array( 'pretty_version' => '4.10.1', 'version' => '4.10.1.0', diff --git a/vendor/overtrue/easy-sms/.editorconfig b/vendor/overtrue/easy-sms/.editorconfig new file mode 100644 index 00000000..df55cd78 --- /dev/null +++ b/vendor/overtrue/easy-sms/.editorconfig @@ -0,0 +1,20 @@ +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = false + +[*.{vue,js,scss}] +charset = utf-8 +indent_style = space +indent_size = 2 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false \ No newline at end of file diff --git a/vendor/overtrue/easy-sms/.github/FUNDING.yml b/vendor/overtrue/easy-sms/.github/FUNDING.yml new file mode 100644 index 00000000..273b78ee --- /dev/null +++ b/vendor/overtrue/easy-sms/.github/FUNDING.yml @@ -0,0 +1,3 @@ +# These are supported funding model platforms + +github: [overtrue] diff --git a/vendor/overtrue/easy-sms/.github/workflows/tests.yml b/vendor/overtrue/easy-sms/.github/workflows/tests.yml new file mode 100644 index 00000000..263a7d03 --- /dev/null +++ b/vendor/overtrue/easy-sms/.github/workflows/tests.yml @@ -0,0 +1,24 @@ +name: Tests + +on: + push: + branches: [ master ] + pull_request: + +jobs: + phpunit: + strategy: + matrix: + php_version: [5.6, 7.0, 7.1, 7.2, 7.3, 7.4, 8.0, 8.1] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup PHP environment + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php_version }} + coverage: xdebug + - name: Install dependencies + run: composer install + - name: PHPUnit check + run: ./vendor/bin/phpunit --coverage-text diff --git a/vendor/overtrue/easy-sms/.php-cs-fixer.dist.php b/vendor/overtrue/easy-sms/.php-cs-fixer.dist.php new file mode 100644 index 00000000..e755e859 --- /dev/null +++ b/vendor/overtrue/easy-sms/.php-cs-fixer.dist.php @@ -0,0 +1,49 @@ +setRules([ + '@PSR12' => true, + 'binary_operator_spaces' => true, + 'blank_line_after_opening_tag' => true, + 'compact_nullable_typehint' => true, + 'declare_equal_normalize' => true, + 'lowercase_cast' => true, + 'lowercase_static_reference' => true, + 'new_with_braces' => true, + 'no_blank_lines_after_class_opening' => true, + 'no_leading_import_slash' => true, + 'no_whitespace_in_blank_line' => true, + 'no_unused_imports' => true, + 'ordered_class_elements' => [ + 'order' => [ + 'use_trait', + ], + ], + 'ordered_imports' => [ + 'imports_order' => [ + 'class', + 'function', + 'const', + ], + 'sort_algorithm' => 'none', + ], + 'return_type_declaration' => true, + 'short_scalar_cast' => true, + 'single_blank_line_before_namespace' => true, + 'single_trait_insert_per_statement' => true, + 'ternary_operator_spaces' => true, + 'unary_operator_spaces' => true, + 'visibility_required' => [ + 'elements' => [ +// 'const', + 'method', + 'property', + ], + ], + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->exclude('vendor') + ->in([__DIR__.'/src/', __DIR__.'/tests/']) + ) +; diff --git a/vendor/overtrue/easy-sms/LICENSE b/vendor/overtrue/easy-sms/LICENSE new file mode 100644 index 00000000..8afd9ead --- /dev/null +++ b/vendor/overtrue/easy-sms/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 overtrue + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/overtrue/easy-sms/README.md b/vendor/overtrue/easy-sms/README.md new file mode 100644 index 00000000..eb8ffe05 --- /dev/null +++ b/vendor/overtrue/easy-sms/README.md @@ -0,0 +1,1019 @@ +

Easy SMS

+ +

:calling: 一款满足你的多种发送需求的短信发送组件

+ +

+Build Status +Latest Stable Version +Latest Unstable Version +Code Coverage +Total Downloads +License +

+ +

+Sponsor me +

+ + +## 特点 + +1. 支持目前市面多家服务商 +1. 一套写法兼容所有平台 +1. 简单配置即可灵活增减服务商 +1. 内置多种服务商轮询策略、支持自定义轮询策略 +1. 统一的返回值格式,便于日志与监控 +1. 自动轮询选择可用的服务商 +1. 更多等你去发现与改进... + +## 平台支持 + +- [腾讯云 SMS](https://cloud.tencent.com/product/sms) +- [Ucloud](https://www.ucloud.cn) +- [七牛云](https://www.qiniu.com/) +- [SendCloud](http://www.sendcloud.net/) +- [阿里云](https://www.aliyun.com/) +- [云片](https://www.yunpian.com) +- [Submail](https://www.mysubmail.com) +- [螺丝帽](https://luosimao.com/) +- [容联云通讯](http://www.yuntongxun.com) +- [互亿无线](http://www.ihuyi.com) +- [聚合数据](https://www.juhe.cn) +- [百度云](https://cloud.baidu.com/) +- [华信短信平台](http://www.ipyy.com/) +- [253云通讯(创蓝)](https://www.253.com/) +- [创蓝云智](https://www.chuanglan.com/) +- [融云](http://www.rongcloud.cn) +- [天毅无线](http://www.85hu.com/) +- [阿凡达数据](http://www.avatardata.cn/) +- [华为云](https://www.huaweicloud.com/product/msgsms.html) +- [网易云信](https://yunxin.163.com/sms) +- [云之讯](https://www.ucpaas.com/index.html) +- [凯信通](http://www.kingtto.cn/) +- [UE35.net](http://uesms.ue35.cn/) +- [短信宝](http://www.smsbao.com/) +- [Tiniyo](https://tiniyo.com/) +- [摩杜云](https://www.moduyun.com/) +- [融合云(助通)](https://www.ztinfo.cn/products/sms) +- [蜘蛛云](https://zzyun.com/) +- [融合云信](https://maap.wo.cn/) +- [天瑞云](http://cms.tinree.com/) +- [时代互联](https://www.now.cn/) +- [火山引擎](https://console.volcengine.com/sms/) + +## 环境需求 + +- PHP >= 5.6 + +## 安装 + +```shell +$ composer require "overtrue/easy-sms" +``` + +**For Laravel notification** + +如果你喜欢使用 [Laravel Notification](https://laravel.com/docs/5.8/notifications), 可以考虑直接使用朋友封装的拓展包: + +https://github.com/yl/easysms-notification-channel + +## 使用 + +```php +use Overtrue\EasySms\EasySms; + +$config = [ + // HTTP 请求的超时时间(秒) + 'timeout' => 5.0, + + // 默认发送配置 + 'default' => [ + // 网关调用策略,默认:顺序调用 + 'strategy' => \Overtrue\EasySms\Strategies\OrderStrategy::class, + + // 默认可用的发送网关 + 'gateways' => [ + 'yunpian', 'aliyun', + ], + ], + // 可用的网关配置 + 'gateways' => [ + 'errorlog' => [ + 'file' => '/tmp/easy-sms.log', + ], + 'yunpian' => [ + 'api_key' => '824f0ff2f71cab52936axxxxxxxxxx', + ], + 'aliyun' => [ + 'access_key_id' => '', + 'access_key_secret' => '', + 'sign_name' => '', + ], + //... + ], +]; + +$easySms = new EasySms($config); + +$easySms->send(13188888888, [ + 'content' => '您的验证码为: 6379', + 'template' => 'SMS_001', + 'data' => [ + 'code' => 6379 + ], +]); +``` + +## 短信内容 + +由于使用多网关发送,所以一条短信要支持多平台发送,每家的发送方式不一样,但是我们抽象定义了以下公用属性: + +- `content` 文字内容,使用在像云片类似的以文字内容发送的平台 +- `template` 模板 ID,使用在以模板ID来发送短信的平台 +- `data` 模板变量,使用在以模板ID来发送短信的平台 + +所以,在使用过程中你可以根据所要使用的平台定义发送的内容。 + +```php +$easySms->send(13188888888, [ + 'content' => '您的验证码为: 6379', + 'template' => 'SMS_001', + 'data' => [ + 'code' => 6379 + ], +]); +``` + +你也可以使用闭包来返回对应的值: + +```php +$easySms->send(13188888888, [ + 'content' => function($gateway){ + return '您的验证码为: 6379'; + }, + 'template' => function($gateway){ + return 'SMS_001'; + }, + 'data' => function($gateway){ + return [ + 'code' => 6379 + ]; + }, +]); +``` + +你可以根据 `$gateway` 参数类型来判断返回值,例如: + +```php +$easySms->send(13188888888, [ + 'content' => function($gateway){ + if ($gateway->getName() == 'yunpian') { + return '云片专用验证码:1235'; + } + return '您的验证码为: 6379'; + }, + 'template' => function($gateway){ + if ($gateway->getName() == 'aliyun') { + return 'TP2818'; + } + return 'SMS_001'; + }, + 'data' => function($gateway){ + return [ + 'code' => 6379 + ]; + }, +]); +``` + +## 发送网关 + +默认使用 `default` 中的设置来发送,如果某一条短信你想要覆盖默认的设置。在 `send` 方法中使用第三个参数即可: + +```php +$easySms->send(13188888888, [ + 'content' => '您的验证码为: 6379', + 'template' => 'SMS_001', + 'data' => [ + 'code' => 6379 + ], + ], ['yunpian', 'juhe']); // 这里的网关配置将会覆盖全局默认值 +``` + +## 返回值 + +由于使用多网关发送,所以返回值为一个数组,结构如下: +```php +[ + 'yunpian' => [ + 'gateway' => 'yunpian', + 'status' => 'success', + 'result' => [...] // 平台返回值 + ], + 'juhe' => [ + 'gateway' => 'juhe', + 'status' => 'failure', + 'exception' => \Overtrue\EasySms\Exceptions\GatewayErrorException 对象 + ], + //... +] +``` + +如果所选网关列表均发送失败时,将会抛出 `Overtrue\EasySms\Exceptions\NoGatewayAvailableException` 异常,你可以使用 `$e->results` 获取发送结果。 + +你也可以使用 `$e` 提供的更多便捷方法: + +```php +$e->getResults(); // 返回所有 API 的结果,结构同上 +$e->getExceptions(); // 返回所有调用异常列表 +$e->getException($gateway); // 返回指定网关名称的异常对象 +$e->getLastException(); // 获取最后一个失败的异常对象 +``` + +## 自定义网关 + +本拓展已经支持用户自定义网关,你可以很方便的配置即可当成与其它拓展一样的使用: + +```php +$config = [ + ... + 'default' => [ + 'gateways' => [ + 'mygateway', // 配置你的网站到可用的网关列表 + ], + ], + 'gateways' => [ + 'mygateway' => [...], // 你网关所需要的参数,如果没有可以不配置 + ], +]; + +$easySms = new EasySms($config); + +// 注册 +$easySms->extend('mygateway', function($gatewayConfig){ + // $gatewayConfig 来自配置文件里的 `gateways.mygateway` + return new MyGateway($gatewayConfig); +}); + +$easySms->send(13188888888, [ + 'content' => '您的验证码为: 6379', + 'template' => 'SMS_001', + 'data' => [ + 'code' => 6379 + ], +]); +``` + +## 国际短信 + +国际短信与国内短信的区别是号码前面需要加国际码,但是由于各平台对国际号码的写法不一致,所以在发送国际短信的时候有一点区别: + +```php +use Overtrue\EasySms\PhoneNumber; + +// 发送到国际码为 31 的国际号码 +$number = new PhoneNumber(13188888888, 31); + +$easySms->send($number, [ + 'content' => '您的验证码为: 6379', + 'template' => 'SMS_001', + 'data' => [ + 'code' => 6379 + ], +]); +``` + +## 定义短信 + +你可以根据发送场景的不同,定义不同的短信类,从而实现一处定义多处调用,你可以继承 `Overtrue\EasySms\Message` 来定义短信模型: + +```php +order = $order; + } + + // 定义直接使用内容发送平台的内容 + public function getContent(GatewayInterface $gateway = null) + { + return sprintf('您的订单:%s, 已经完成付款', $this->order->no); + } + + // 定义使用模板发送方式平台所需要的模板 ID + public function getTemplate(GatewayInterface $gateway = null) + { + return 'SMS_003'; + } + + // 模板参数 + public function getData(GatewayInterface $gateway = null) + { + return [ + 'order_no' => $this->order->no + ]; + } +} +``` + +> 更多自定义方式请参考:[`Overtrue\EasySms\Message`](Overtrue\EasySms\Message;) + +发送自定义短信: + +```php +$order = ...; +$message = new OrderPaidMessage($order); + +$easySms->send(13188888888, $message); +``` + +## 各平台配置说明 + +### [阿里云](https://www.aliyun.com/) + +短信内容使用 `template` + `data` + +```php + 'aliyun' => [ + 'access_key_id' => '', + 'access_key_secret' => '', + 'sign_name' => '', + ], +``` + +### [阿里云Rest](https://www.aliyun.com/) + +短信内容使用 `template` + `data` + +```php + 'aliyunrest' => [ + 'app_key' => '', + 'app_secret_key' => '', + 'sign_name' => '', + ], +``` + +### [阿里云国际](https://www.alibabacloud.com/help/zh/doc-detail/160524.html) + +短信内容使用 `template` + `data` + +```php + 'aliyunintl' => [ + 'access_key_id' => '', + 'access_key_secret' => '', + 'sign_name' => '', + ], +``` + +发送示例: + +```php +use Overtrue\EasySms\PhoneNumber; + +$easySms = new EasySms($config); +$phone_number = new PhoneNumber(18888888888, 86); + +$easySms->send($phone_number, [ + 'content' => '您好:先生/女士!您的验证码为${code},有效时间是5分钟,请及时验证。', + 'template' => 'SMS_00000001', // 模板ID + 'data' => [ + "code" => 521410, + ], +]); +``` + +### [云片](https://www.yunpian.com) + +短信内容使用 `content` + +```php + 'yunpian' => [ + 'api_key' => '', + 'signature' => '【默认签名】', // 内容中无签名时使用 + ], +``` + +### [Submail](https://www.mysubmail.com) + +短信内容使用 `data` + +```php + 'submail' => [ + 'app_id' => '', + 'app_key' => '', + 'project' => '', // 默认 project,可在发送时 data 中指定 + ], +``` + +### [螺丝帽](https://luosimao.com/) + +短信内容使用 `content` + +```php + 'luosimao' => [ + 'api_key' => '', + ], +``` + +### [容联云通讯](http://www.yuntongxun.com) + +短信内容使用 `template` + `data` + +```php + 'yuntongxun' => [ + 'app_id' => '', + 'account_sid' => '', + 'account_token' => '', + 'is_sub_account' => false, + ], +``` + +### [互亿无线](http://www.ihuyi.com) + +短信内容使用 `content` + +```php + 'huyi' => [ + 'api_id' => '', + 'api_key' => '', + 'signature' => '', + ], +``` + +### [聚合数据](https://www.juhe.cn) + +短信内容使用 `template` + `data` + +```php + 'juhe' => [ + 'app_key' => '', + ], +``` + +### [SendCloud](http://www.sendcloud.net/) + +短信内容使用 `template` + `data` + +```php + 'sendcloud' => [ + 'sms_user' => '', + 'sms_key' => '', + 'timestamp' => false, // 是否启用时间戳 + ], +``` +### [百度云](https://cloud.baidu.com/) + +短信内容使用 `template` + `data` + +```php + 'baidu' => [ + 'ak' => '', + 'sk' => '', + 'invoke_id' => '', + 'domain' => '', + ], +``` + +### [华信短信平台](http://www.ipyy.com/) + +短信内容使用 `content` + +```php + 'huaxin' => [ + 'user_id' => '', + 'password' => '', + 'account' => '', + 'ip' => '', + 'ext_no' => '', + ], +``` + +### [253云通讯(创蓝)](https://www.253.com/) + +短信内容使用 `content` + +```php + 'chuanglan' => [ + 'account' => '', + 'password' => '', + + // 国际短信时必填 + 'intel_account' => '', + 'intel_password' => '', + + // \Overtrue\EasySms\Gateways\ChuanglanGateway::CHANNEL_VALIDATE_CODE => 验证码通道(默认) + // \Overtrue\EasySms\Gateways\ChuanglanGateway::CHANNEL_PROMOTION_CODE => 会员营销通道 + 'channel' => \Overtrue\EasySms\Gateways\ChuanglanGateway::CHANNEL_VALIDATE_CODE, + + // 会员营销通道 特定参数。创蓝规定:api提交营销短信的时候,需要自己加短信的签名及退订信息 + 'sign' => '【通讯云】', + 'unsubscribe' => '回TD退订', + ], +``` + +### [创蓝云智](https://www.chuanglan.com/) + +普通短信发送内容使用 `content` + +```php + 'chuanglanv1' => [ + 'account' => '', + 'password' => '', + 'needstatus' => false, + 'channel' => \Overtrue\EasySms\Gateways\Chuanglanv1Gateway::CHANNEL_NORMAL_CODE, + ], +``` +发送示例: + +```php +$easySms->send(18888888888, [ + 'content' => xxxxxxx +]); +``` + +变量短信发送内容使用 `template` + `data` + +```php + 'chuanglanv1' => [ + 'account' => '', + 'password' => '', + 'needstatus' => false, + 'channel' => \Overtrue\EasySms\Gateways\Chuanglanv1Gateway::CHANNEL_VARIABLE_CODE, + ], +``` +发送示例: + +```php +$easySms->send(18888888888, [ + 'template' => xxxxxx, // 模板内容 + 'data' => 'phone":"15800000000,1234;15300000000,4321', +]); +``` + +### [融云](http://www.rongcloud.cn) + +短信分为两大类,验证类和通知类短信。 发送验证类短信使用 `template` + `data` + +```php + 'rongcloud' => [ + 'app_key' => '', + 'app_secret' => '', + ] +``` + +### [天毅无线](http://www.85hu.com/) + +短信内容使用 `content` + +```php + 'tianyiwuxian' => [ + 'username' => '', //用户名 + 'password' => '', //密码 + 'gwid' => '', //网关ID + ] +``` + +### [twilio](https://www.twilio.com) + +短信使用 `content` +发送对象需要 使用`+`添加区号 + +```php + 'twilio' => [ + 'account_sid' => '', // sid + 'from' => '', // 发送的号码 可以在控制台购买 + 'token' => '', // apitoken + ], +``` + +### [tiniyo](https://www.tiniyo.com) + +短信使用 `content` +发送对象需要 使用`+`添加区号 + +```php + 'tiniyo' => [ + 'account_sid' => '', // auth_id from https://tiniyo.com + 'from' => '', // 发送的号码 可以在控制台购买 + 'token' => '', // auth_secret from https://tiniyo.com + ], +``` + + +### [腾讯云 SMS](https://cloud.tencent.com/product/sms) + +短信内容使用 `template` + `data` + +```php + 'qcloud' => [ + 'sdk_app_id' => '', // 短信应用的 SDK APP ID + 'secret_id' => '', // SECRET ID + 'secret_key' => '', // SECRET KEY + 'sign_name' => '腾讯CoDesign', // 短信签名 + ], +``` + +发送示例: + +```php +$easySms->send(18888888888, [ + 'template' => 101234, // 模板ID + 'data' => [ + "a", 'b', 'c', 'd', //按占位顺序给值 + ], +]); +``` + +### [阿凡达数据](http://www.avatardata.cn/) + +短信内容使用 `template` + `data` + +```php + 'avatardata' => [ + 'app_key' => '', // APP KEY + ], +``` + +### [华为云 SMS](https://www.huaweicloud.com/product/msgsms.html) + +短信内容使用 `template` + `data` + +```php + 'huawei' => [ + 'endpoint' => '', // APP接入地址 + 'app_key' => '', // APP KEY + 'app_secret' => '', // APP SECRET + 'from' => [ + 'default' => '1069012345', // 默认使用签名通道号 + 'custom' => 'csms12345', // 其他签名通道号 可以在 data 中定义 from 来指定 + 'abc' => 'csms67890', // 其他签名通道号 + ... + ], + 'callback' => '' // 短信状态回调地址 + ], +``` + +使用默认签名通道 `default` + +```php +$easySms->send(13188888888, [ + 'template' => 'SMS_001', + 'data' => [ + 6379 + ], +]); +``` + +使用指定签名通道 + +```php +$easySms->send(13188888888, [ + 'template' => 'SMS_001', + 'data' => [ + 6379, + 'from' => 'custom' // 对应 config 中的 from 数组中 custom + ], +]); +``` + +### [网易云信](https://yunxin.163.com/sms) + +短信内容使用 `template` + `data` + +```php + 'yunxin' => [ + 'app_key' => '', + 'app_secret' => '', + 'code_length' => 4, // 随机验证码长度,范围 4~10,默认为 4 + 'need_up' => false, // 是否需要支持短信上行 + ], +``` + +```php +$easySms->send(18888888888, [ + 'template' => 'SMS_001', // 不填则使用默认模板 + 'data' => [ + 'code' => 8946, // 如果设置了该参数,则 code_length 参数无效 + 'action' => 'sendCode', // 默认为 `sendCode`,校验短信验证码使用 `verifyCode` + ], +]); +``` +通知模板短信 + +```php +$easySms->send(18888888888, [ + 'template' => 'templateid', // 模板编号(由客户顾问配置之后告知开发者) + 'data' => [ + 'action' => 'sendTemplate', // 默认为 `sendCode`,校验短信验证码使用 `verifyCode` + 'params' => [1,2,3], //短信参数列表,用于依次填充模板 + ], +]); +``` + + +### [云之讯](https://www.ucpaas.com/index.html) + +短信内容使用 `template` + `data` + +```php + 'yunzhixun' => [ + 'sid' => '', + 'token' => '', + 'app_id' => '', + ], +``` + +```php +$easySms->send(18888888888, [ + 'template' => 'SMS_001', + 'data' => [ + 'params' => '8946,3', // 模板参数,多个参数使用 `,` 分割,模板无参数时可为空 + 'uid' => 'hexianghui', // 用户 ID,随状态报告返回,可为空 + 'mobiles' => '18888888888,188888888889', // 批量发送短信,手机号使用 `,` 分割,不使用批量发送请不要设置该参数 + ], +]); +``` + +### [凯信通](http://www.kingtto.cn/) + +短信内容使用 `content` + +```php + 'kingtto' => [ + 'userid' => '', + 'account' => '', + 'password' => '', + ], +``` + +```php +$easySms->send(18888888888, [ + 'content' => '您的验证码为: 6379', +]); +``` + +### [七牛云](https://www.qiniu.com/) + +短信内容使用 `template` + `data` + +```php + 'qiniu' => [ + 'secret_key' => '', + 'access_key' => '', + ], +``` + +```php +$easySms->send(18888888888, [ + 'template' => '1231234123412341234', + 'data' => [ + 'code' => 1234, + ], +]); +``` +### [Ucloud](https://www.ucloud.cn/) +短信使用 `template` + `data` + +```php + 'ucloud' => [ + 'private_key' => '', //私钥 + 'public_key' => '', //公钥 + 'sig_content' => '', // 短信签名, + 'project_id' => '', //项目ID,子账号才需要该参数 + ], +``` + +```php +$easySms->send(18888888888, [ + 'template' => 'UTAXXXXX', //短信模板 + 'data' => [ + 'code' => 1234, //模板参数,模板没有参数不用则填写,有多个参数请用数组,[1111,1111] + 'mobiles' =>'', //同时发送多个手机短信,请用数组[xxx,xxx] + ], +]); + +``` + + +### [短信宝](http://www.smsbao.com/) +短信使用 `content` + +```php + 'smsbao' => [ + 'user' => '', //账号 + 'password' => '' //密码 + ], +``` + +```php +$easySms->send(18888888888, [ + 'content' => '您的验证码为: 6379', //短信模板 +]); + +``` + +### [摩杜云](https://www.moduyun.com/) +短信使用 `template` + `data` + +```php + 'moduyun' => [ + 'accesskey' => '', //必填 ACCESS KEY + 'secretkey' => '', //必填 SECRET KEY + 'signId' => '', //选填 短信签名,如果使用默认签名,该字段可缺省 + 'type' => 0, //选填 0:普通短信;1:营销短信 + ], +``` + +```php +$easySms->send(18888888888, [ + 'template' => '5a95****b953', //短信模板 + 'data' => [ + 1234, //模板参数,对应模板的{1} + 30 //模板参数,对应模板的{2} + //... + ], +]); + +``` + +### [融合云(助通)](https://www.ztinfo.cn/products/sms) + +短信使用 `template` + `data` + +```php + 'rongheyun' => [ + 'username' => '', //必填 用户名 + 'password' => '', //必填 密码 + 'signature'=> '', //必填 已报备的签名 + ], +``` + +```php +$easySms->send(18888888888, [ + 'template' => '31874', //短信模板 + 'data' => [ + 'valid_code' => '888888', //模板参数,对应模板的{valid_code} + //... + ], +]); + +``` + +### [蜘蛛云](https://zzyun.com/) + +短信使用 `template` + `data` + +```php + 'zzyun' => [ + 'user_id' => '', //必填 会员ID + 'secret' => '', //必填 接口密钥 + 'sign_name'=> '', //必填 短信签名 + ], +``` + +```php +$easySms->send(18888888888, [ + 'template' => 'SMS_210317****', //短信模板 + 'data' => [ + 'code' => '888888', //模板参数,对应模板的{code} + //... + ], +]); + +``` + +### [融合云信](https://maap.wo.cn/) + +短信使用 `template` + `data` + +```php + 'maap' => [ + 'cpcode' => '', //必填 商户编码 + 'key' => '', //必填 接口密钥 + 'excode'=> '', //选填 扩展名 + ], +``` + +```php +$easySms->send(18888888888, [ + 'template' => '356120', //短信模板 + 'data' => [ + '123465' + ],//模板参数 +]); + +``` + +### [天瑞云](http://cms.tinree.com/) + +短信内容使用 `template` + `data` + +```php + 'tinree' => [ + 'accesskey' => '', // 平台分配给用户的accesskey + 'secret' => '', // 平台分配给用户的secret + 'sign' => '', // 平台上申请的接口短信签名或者签名ID + ], +``` + +发送示例: + +```php +$easySms->send(18888888888, [ + 'template' => '123456', // 模板ID + 'data' => [ + "a", 'b', 'c', //按模板变量占位顺序 + ], +]); +``` + +### [时代互联](https://www.now.cn/) + +短信使用 `content` + +```php + 'nowcn' => [ + 'key' => '', //用户ID + 'secret' => '', //开发密钥 + 'api_type' => '', // 短信通道, + ], +``` + +发送示例: +```php +$easySms->send(18888888888, [ + 'content' => '您的验证码为: 6379', +]); +``` + +### [火山引擎](https://console.volcengine.com/sms/) + +短信内容使用 `template` + `data` + +```php + 'volcengine' => [ + 'access_key_id' => '', // 平台分配给用户的access_key_id + 'access_key_secret' => '', // 平台分配给用户的access_key_secret + 'region_id' => 'cn-north-1', // 国内节点 cn-north-1,国外节点 ap-singapore-1,不填或填错,默认使用国内节点 + 'sign_name' => '', // 平台上申请的接口短信签名或者签名ID,可不填,发送短信时data中指定 + 'sms_account' => '', // 消息组帐号,火山短信页面右上角,短信应用括号中的字符串,可不填,发送短信时data中指定 + ], +``` + +发送示例1: + +```php +$easySms->send(18888888888, [ + 'template' => 'SMS_123456', // 模板ID + 'data' => [ + "code" => 1234 // 模板变量 + ], +]); +``` + +发送示例2: +```php +$easySms->send(18888888888, [ + 'template' => 'SMS_123456', // 模板ID + 'data' => [ + "template_param" => ["code" => 1234], // 模板变量参数 + "sign_name" => "yoursignname", // 签名,覆盖配置文件中的sign_name + "sms_account" => "yoursmsaccount", // 消息组帐号,覆盖配置文件中的sms_account + "phone_numbers" => "18888888888,18888888889", // 手机号,批量发送,英文的逗号连接多个手机号,覆盖发送方法中的填入的手机号 + ], +]); +``` + +## :heart: 支持我 + +[![Sponsor me](https://github.com/overtrue/overtrue/blob/master/sponsor-me.svg?raw=true)](https://github.com/sponsors/overtrue) + +如果你喜欢我的项目并想支持它,[点击这里 :heart:](https://github.com/sponsors/overtrue) + +## Project supported by JetBrains + +Many thanks to Jetbrains for kindly providing a license for me to work on this and other open-source projects. + +[![](https://resources.jetbrains.com/storage/products/company/brand/logos/jb_beam.svg)](https://www.jetbrains.com/?from=https://github.com/overtrue) + + +## PHP 扩展包开发 + +> 想知道如何从零开始构建 PHP 扩展包? +> +> 请关注我的实战课程,我会在此课程中分享一些扩展开发经验 —— [《PHP 扩展包实战教程 - 从入门到发布》](https://learnku.com/courses/creating-package) + +## License + +MIT diff --git a/vendor/overtrue/easy-sms/composer.json b/vendor/overtrue/easy-sms/composer.json new file mode 100644 index 00000000..716c5163 --- /dev/null +++ b/vendor/overtrue/easy-sms/composer.json @@ -0,0 +1,62 @@ +{ + "name": "overtrue/easy-sms", + "description": "The easiest way to send short message.", + "type": "library", + "require": { + "guzzlehttp/guzzle": "^6.2 || ^7.0", + "php": ">=5.6", + "ext-json": "*" + }, + "require-dev": { + "phpunit/phpunit": "^5.7 || ^7.5 || ^8.5.19 || ^9.5.8", + "mockery/mockery": "~1.3.3 || ^1.4.2", + "brainmaestro/composer-git-hooks": "^2.8", + "jetbrains/phpstorm-attributes": "^1.0" + }, + "autoload": { + "psr-4": { + "Overtrue\\EasySms\\": "src" + } + }, + "autoload-dev": { + "psr-4": { + "Overtrue\\EasySms\\Tests\\": "tests" + } + }, + "license": "MIT", + "authors": [{ + "name": "overtrue", + "email": "i@overtrue.me" + }], + "extra": { + "hooks": { + "pre-commit": [ + "composer check-style", + "composer psalm", + "composer test" + ], + "pre-push": [ + "composer check-style" + ] + } + }, + "scripts": { + "post-update-cmd": [ + "cghooks remove", + "cghooks add --ignore-lock", + "cghooks update" + ], + "post-merge": "composer install", + "post-install-cmd": [ + "cghooks remove", + "cghooks add --ignore-lock", + "cghooks update" + ], + "phpstan": "phpstan analyse", + "check-style": "php-cs-fixer fix --using-cache=no --diff --config=.php-cs-fixer.dist.php --dry-run --allow-risky=yes --ansi", + "fix-style": "php-cs-fixer fix --using-cache=no --config=.php-cs-fixer.dist.php --allow-risky=yes --ansi", + "test": "phpunit --colors", + "psalm": "psalm --show-info=true --no-cache", + "psalm-fix": "psalm --no-cache --alter --issues=MissingReturnType,MissingParamType" + } +} diff --git a/vendor/overtrue/easy-sms/psalm.xml b/vendor/overtrue/easy-sms/psalm.xml new file mode 100644 index 00000000..f8edfd3a --- /dev/null +++ b/vendor/overtrue/easy-sms/psalm.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/vendor/overtrue/easy-sms/src/Contracts/GatewayInterface.php b/vendor/overtrue/easy-sms/src/Contracts/GatewayInterface.php new file mode 100644 index 00000000..34246770 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Contracts/GatewayInterface.php @@ -0,0 +1,38 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Contracts; + +use Overtrue\EasySms\Support\Config; + +/** + * Class GatewayInterface. + */ +interface GatewayInterface +{ + /** + * Get gateway name. + * + * @return string + */ + public function getName(); + + /** + * Send a short message. + * + * @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); +} diff --git a/vendor/overtrue/easy-sms/src/Contracts/MessageInterface.php b/vendor/overtrue/easy-sms/src/Contracts/MessageInterface.php new file mode 100644 index 00000000..7c17f100 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Contracts/MessageInterface.php @@ -0,0 +1,63 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Contracts; + +/** + * Interface MessageInterface. + */ +interface MessageInterface +{ + const TEXT_MESSAGE = 'text'; + + const VOICE_MESSAGE = 'voice'; + + /** + * Return the message type. + * + * @return string + */ + public function getMessageType(); + + /** + * Return message content. + * + * @param \Overtrue\EasySms\Contracts\GatewayInterface|null $gateway + * + * @return string + */ + public function getContent(GatewayInterface $gateway = null); + + /** + * Return the template id of message. + * + * @param \Overtrue\EasySms\Contracts\GatewayInterface|null $gateway + * + * @return string + */ + public function getTemplate(GatewayInterface $gateway = null); + + /** + * Return the template data of message. + * + * @param \Overtrue\EasySms\Contracts\GatewayInterface|null $gateway + * + * @return array + */ + public function getData(GatewayInterface $gateway = null); + + /** + * Return message supported gateways. + * + * @return array + */ + public function getGateways(); +} diff --git a/vendor/overtrue/easy-sms/src/Contracts/PhoneNumberInterface.php b/vendor/overtrue/easy-sms/src/Contracts/PhoneNumberInterface.php new file mode 100644 index 00000000..7d646480 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Contracts/PhoneNumberInterface.php @@ -0,0 +1,53 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Contracts; + +/** + * Interface PhoneNumberInterface. + * + * @author overtrue + */ +interface PhoneNumberInterface extends \JsonSerializable +{ + /** + * 86. + * + * @return int + */ + public function getIDDCode(); + + /** + * 18888888888. + * + * @return int + */ + public function getNumber(); + + /** + * +8618888888888. + * + * @return string + */ + public function getUniversalNumber(); + + /** + * 008618888888888. + * + * @return string + */ + public function getZeroPrefixedNumber(); + + /** + * @return string + */ + public function __toString(); +} diff --git a/vendor/overtrue/easy-sms/src/Contracts/StrategyInterface.php b/vendor/overtrue/easy-sms/src/Contracts/StrategyInterface.php new file mode 100644 index 00000000..1cda2c71 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Contracts/StrategyInterface.php @@ -0,0 +1,27 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Contracts; + +/** + * Interface StrategyInterface. + */ +interface StrategyInterface +{ + /** + * Apply the strategy and return result. + * + * @param array $gateways + * + * @return array + */ + public function apply(array $gateways); +} diff --git a/vendor/overtrue/easy-sms/src/EasySms.php b/vendor/overtrue/easy-sms/src/EasySms.php new file mode 100644 index 00000000..9fc68e74 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/EasySms.php @@ -0,0 +1,326 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms; + +use Closure; +use Overtrue\EasySms\Contracts\GatewayInterface; +use Overtrue\EasySms\Contracts\MessageInterface; +use Overtrue\EasySms\Contracts\PhoneNumberInterface; +use Overtrue\EasySms\Contracts\StrategyInterface; +use Overtrue\EasySms\Exceptions\InvalidArgumentException; +use Overtrue\EasySms\Gateways\Gateway; +use Overtrue\EasySms\Strategies\OrderStrategy; +use Overtrue\EasySms\Support\Config; + +/** + * Class EasySms. + */ +class EasySms +{ + /** + * @var \Overtrue\EasySms\Support\Config + */ + protected $config; + + /** + * @var string + */ + protected $defaultGateway; + + /** + * @var array + */ + protected $customCreators = []; + + /** + * @var array + */ + protected $gateways = []; + + /** + * @var \Overtrue\EasySms\Messenger + */ + protected $messenger; + + /** + * @var array + */ + protected $strategies = []; + + /** + * Constructor. + * + * @param array $config + */ + public function __construct(array $config) + { + $this->config = new Config($config); + } + + /** + * Send a message. + * + * @param string|array $to + * @param \Overtrue\EasySms\Contracts\MessageInterface|array $message + * @param array $gateways + * + * @return array + * + * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException + * @throws \Overtrue\EasySms\Exceptions\NoGatewayAvailableException + */ + public function send($to, $message, array $gateways = []) + { + $to = $this->formatPhoneNumber($to); + $message = $this->formatMessage($message); + $gateways = empty($gateways) ? $message->getGateways() : $gateways; + + if (empty($gateways)) { + $gateways = $this->config->get('default.gateways', []); + } + + return $this->getMessenger()->send($to, $message, $this->formatGateways($gateways)); + } + + /** + * Create a gateway. + * + * @param string|null $name + * + * @return \Overtrue\EasySms\Contracts\GatewayInterface + * + * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException + */ + public function gateway($name) + { + if (!isset($this->gateways[$name])) { + $this->gateways[$name] = $this->createGateway($name); + } + + return $this->gateways[$name]; + } + + /** + * Get a strategy instance. + * + * @param string|null $strategy + * + * @return \Overtrue\EasySms\Contracts\StrategyInterface + * + * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException + */ + public function strategy($strategy = null) + { + if (\is_null($strategy)) { + $strategy = $this->config->get('default.strategy', OrderStrategy::class); + } + + if (!\class_exists($strategy)) { + $strategy = __NAMESPACE__.'\Strategies\\'.\ucfirst($strategy); + } + + if (!\class_exists($strategy)) { + throw new InvalidArgumentException("Unsupported strategy \"{$strategy}\""); + } + + if (empty($this->strategies[$strategy]) || !($this->strategies[$strategy] instanceof StrategyInterface)) { + $this->strategies[$strategy] = new $strategy($this); + } + + return $this->strategies[$strategy]; + } + + /** + * Register a custom driver creator Closure. + * + * @param string $name + * @param \Closure $callback + * + * @return $this + */ + public function extend($name, Closure $callback) + { + $this->customCreators[$name] = $callback; + + return $this; + } + + /** + * @return \Overtrue\EasySms\Support\Config + */ + public function getConfig() + { + return $this->config; + } + + /** + * @return \Overtrue\EasySms\Messenger + */ + public function getMessenger() + { + return $this->messenger ?: $this->messenger = new Messenger($this); + } + + /** + * Create a new driver instance. + * + * @param string $name + * + * @throws \InvalidArgumentException + * + * @return GatewayInterface + * + * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException + */ + protected function createGateway($name) + { + $config = $this->config->get("gateways.{$name}", []); + + if (!isset($config['timeout'])) { + $config['timeout'] = $this->config->get('timeout', Gateway::DEFAULT_TIMEOUT); + } + + $config['options'] = $this->config->get('options', []); + + if (isset($this->customCreators[$name])) { + $gateway = $this->callCustomCreator($name, $config); + } else { + $className = $this->formatGatewayClassName($name); + $gateway = $this->makeGateway($className, $config); + } + + if (!($gateway instanceof GatewayInterface)) { + throw new InvalidArgumentException(\sprintf('Gateway "%s" must implement interface %s.', $name, GatewayInterface::class)); + } + + return $gateway; + } + + /** + * Make gateway instance. + * + * @param string $gateway + * @param array $config + * + * @return \Overtrue\EasySms\Contracts\GatewayInterface + * + * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException + */ + protected function makeGateway($gateway, $config) + { + if (!\class_exists($gateway) || !\in_array(GatewayInterface::class, \class_implements($gateway))) { + throw new InvalidArgumentException(\sprintf('Class "%s" is a invalid easy-sms gateway.', $gateway)); + } + + return new $gateway($config); + } + + /** + * Format gateway name. + * + * @param string $name + * + * @return string + */ + protected function formatGatewayClassName($name) + { + if (\class_exists($name) && \in_array(GatewayInterface::class, \class_implements($name))) { + return $name; + } + + $name = \ucfirst(\str_replace(['-', '_', ''], '', $name)); + + return __NAMESPACE__."\\Gateways\\{$name}Gateway"; + } + + /** + * Call a custom gateway creator. + * + * @param string $gateway + * @param array $config + * + * @return mixed + */ + protected function callCustomCreator($gateway, $config) + { + return \call_user_func($this->customCreators[$gateway], $config); + } + + /** + * @param string|\Overtrue\EasySms\Contracts\PhoneNumberInterface $number + * + * @return \Overtrue\EasySms\Contracts\PhoneNumberInterface|string + */ + protected function formatPhoneNumber($number) + { + if ($number instanceof PhoneNumberInterface) { + return $number; + } + + return new PhoneNumber(\trim($number)); + } + + /** + * @param array|string|\Overtrue\EasySms\Contracts\MessageInterface $message + * + * @return \Overtrue\EasySms\Contracts\MessageInterface + */ + protected function formatMessage($message) + { + if (!($message instanceof MessageInterface)) { + if (!\is_array($message)) { + $message = [ + 'content' => $message, + 'template' => $message, + ]; + } + + $message = new Message($message); + } + + return $message; + } + + /** + * @param array $gateways + * + * @return array + * + * @throws \Overtrue\EasySms\Exceptions\InvalidArgumentException + */ + protected function formatGateways(array $gateways) + { + $formatted = []; + + foreach ($gateways as $gateway => $setting) { + if (\is_int($gateway) && \is_string($setting)) { + $gateway = $setting; + $setting = []; + } + + $formatted[$gateway] = $setting; + $globalSettings = $this->config->get("gateways.{$gateway}", []); + + if (\is_string($gateway) && !empty($globalSettings) && \is_array($setting)) { + $formatted[$gateway] = new Config(\array_merge($globalSettings, $setting)); + } + } + + $result = []; + + foreach ($this->strategy()->apply($formatted) as $name) { + $result[$name] = $formatted[$name]; + } + + return $result; + } +} diff --git a/vendor/overtrue/easy-sms/src/Exceptions/Exception.php b/vendor/overtrue/easy-sms/src/Exceptions/Exception.php new file mode 100644 index 00000000..079d4153 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Exceptions/Exception.php @@ -0,0 +1,21 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Exceptions; + +/** + * Class Exception. + * + * @author overtrue + */ +class Exception extends \Exception +{ +} diff --git a/vendor/overtrue/easy-sms/src/Exceptions/GatewayErrorException.php b/vendor/overtrue/easy-sms/src/Exceptions/GatewayErrorException.php new file mode 100644 index 00000000..e183deff --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Exceptions/GatewayErrorException.php @@ -0,0 +1,37 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Exceptions; + +/** + * Class GatewayErrorException. + */ +class GatewayErrorException extends Exception +{ + /** + * @var array + */ + public $raw = []; + + /** + * GatewayErrorException constructor. + * + * @param string $message + * @param int $code + * @param array $raw + */ + public function __construct($message, $code, array $raw = []) + { + parent::__construct($message, intval($code)); + + $this->raw = $raw; + } +} diff --git a/vendor/overtrue/easy-sms/src/Exceptions/InvalidArgumentException.php b/vendor/overtrue/easy-sms/src/Exceptions/InvalidArgumentException.php new file mode 100644 index 00000000..c2be998d --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Exceptions/InvalidArgumentException.php @@ -0,0 +1,19 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Exceptions; + +/** + * Class InvalidArgumentException. + */ +class InvalidArgumentException extends Exception +{ +} diff --git a/vendor/overtrue/easy-sms/src/Exceptions/NoGatewayAvailableException.php b/vendor/overtrue/easy-sms/src/Exceptions/NoGatewayAvailableException.php new file mode 100644 index 00000000..7c804d94 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Exceptions/NoGatewayAvailableException.php @@ -0,0 +1,81 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Exceptions; + +use Throwable; + +/** + * Class NoGatewayAvailableException. + * + * @author overtrue + */ +class NoGatewayAvailableException extends Exception +{ + /** + * @var array + */ + public $results = []; + + /** + * @var array + */ + public $exceptions = []; + + /** + * NoGatewayAvailableException constructor. + * + * @param array $results + * @param int $code + * @param \Throwable|null $previous + */ + public function __construct(array $results = [], $code = 0, Throwable $previous = null) + { + $this->results = $results; + $this->exceptions = \array_column($results, 'exception', 'gateway'); + + parent::__construct('All the gateways have failed. You can get error details by `$exception->getExceptions()`', $code, $previous); + } + + /** + * @return array + */ + public function getResults() + { + return $this->results; + } + + /** + * @param string $gateway + * + * @return mixed|null + */ + public function getException($gateway) + { + return isset($this->exceptions[$gateway]) ? $this->exceptions[$gateway] : null; + } + + /** + * @return array + */ + public function getExceptions() + { + return $this->exceptions; + } + + /** + * @return mixed + */ + public function getLastException() + { + return end($this->exceptions); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/AliyunGateway.php b/vendor/overtrue/easy-sms/src/Gateways/AliyunGateway.php new file mode 100644 index 00000000..c061dd2f --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/AliyunGateway.php @@ -0,0 +1,107 @@ + + * + * 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 + * + * @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)); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/AliyunIntlGateway.php b/vendor/overtrue/easy-sms/src/Gateways/AliyunIntlGateway.php new file mode 100644 index 00000000..dcd7880e --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/AliyunIntlGateway.php @@ -0,0 +1,97 @@ +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)); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/AliyunrestGateway.php b/vendor/overtrue/easy-sms/src/Gateways/AliyunrestGateway.php new file mode 100644 index 00000000..d4359d2a --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/AliyunrestGateway.php @@ -0,0 +1,107 @@ + + * + * 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)); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/AvatardataGateway.php b/vendor/overtrue/easy-sms/src/Gateways/AvatardataGateway.php new file mode 100644 index 00000000..1958cd49 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/AvatardataGateway.php @@ -0,0 +1,60 @@ + + * + * 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; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/BaiduGateway.php b/vendor/overtrue/easy-sms/src/Gateways/BaiduGateway.php new file mode 100644 index 00000000..5c26b0a5 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/BaiduGateway.php @@ -0,0 +1,174 @@ + + * + * 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)); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/ChuanglanGateway.php b/vendor/overtrue/easy-sms/src/Gateways/ChuanglanGateway.php new file mode 100644 index 00000000..f22839ad --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/ChuanglanGateway.php @@ -0,0 +1,156 @@ + + * + * 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; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/Chuanglanv1Gateway.php b/vendor/overtrue/easy-sms/src/Gateways/Chuanglanv1Gateway.php new file mode 100644 index 00000000..a81fb6aa --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/Chuanglanv1Gateway.php @@ -0,0 +1,147 @@ + + * + * 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; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/ErrorlogGateway.php b/vendor/overtrue/easy-sms/src/Gateways/ErrorlogGateway.php new file mode 100644 index 00000000..d27b40d9 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/ErrorlogGateway.php @@ -0,0 +1,50 @@ + + * + * 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'); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/Gateway.php b/vendor/overtrue/easy-sms/src/Gateways/Gateway.php new file mode 100644 index 00000000..c6791fc9 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/Gateway.php @@ -0,0 +1,120 @@ + + * + * 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))); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/HuaweiGateway.php b/vendor/overtrue/easy-sms/src/Gateways/HuaweiGateway.php new file mode 100644 index 00000000..16060ca1 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/HuaweiGateway.php @@ -0,0 +1,148 @@ + + * + * 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 + ); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/HuaxinGateway.php b/vendor/overtrue/easy-sms/src/Gateways/HuaxinGateway.php new file mode 100644 index 00000000..3e5c9fec --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/HuaxinGateway.php @@ -0,0 +1,73 @@ + + * + * 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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/HuyiGateway.php b/vendor/overtrue/easy-sms/src/Gateways/HuyiGateway.php new file mode 100644 index 00000000..bcdb91b3 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/HuyiGateway.php @@ -0,0 +1,77 @@ + + * + * 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']); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/JuheGateway.php b/vendor/overtrue/easy-sms/src/Gateways/JuheGateway.php new file mode 100644 index 00000000..e94d7075 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/JuheGateway.php @@ -0,0 +1,76 @@ + + * + * 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)); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/KingttoGateway.php b/vendor/overtrue/easy-sms/src/Gateways/KingttoGateway.php new file mode 100644 index 00000000..c74795a3 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/KingttoGateway.php @@ -0,0 +1,61 @@ + + * + * 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; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/LuosimaoGateway.php b/vendor/overtrue/easy-sms/src/Gateways/LuosimaoGateway.php new file mode 100644 index 00000000..07ba2b76 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/LuosimaoGateway.php @@ -0,0 +1,74 @@ + + * + * 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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/MaapGateway.php b/vendor/overtrue/easy-sms/src/Gateways/MaapGateway.php new file mode 100644 index 00000000..1ca44bea --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/MaapGateway.php @@ -0,0 +1,72 @@ + + * + * 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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/ModuyunGateway.php b/vendor/overtrue/easy-sms/src/Gateways/ModuyunGateway.php new file mode 100644 index 00000000..7a6d1521 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/ModuyunGateway.php @@ -0,0 +1,99 @@ + + * + * 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'] + )); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/NowcnGateway.php b/vendor/overtrue/easy-sms/src/Gateways/NowcnGateway.php new file mode 100644 index 00000000..1a41d8a5 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/NowcnGateway.php @@ -0,0 +1,38 @@ +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; + } +} \ No newline at end of file diff --git a/vendor/overtrue/easy-sms/src/Gateways/QcloudGateway.php b/vendor/overtrue/easy-sms/src/Gateways/QcloudGateway.php new file mode 100644 index 00000000..2aaad189 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/QcloudGateway.php @@ -0,0 +1,137 @@ + + * + * 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; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/QiniuGateway.php b/vendor/overtrue/easy-sms/src/Gateways/QiniuGateway.php new file mode 100644 index 00000000..9a9ad315 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/QiniuGateway.php @@ -0,0 +1,148 @@ + + * + * 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)); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/RongcloudGateway.php b/vendor/overtrue/easy-sms/src/Gateways/RongcloudGateway.php new file mode 100644 index 00000000..8464a1cf --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/RongcloudGateway.php @@ -0,0 +1,134 @@ + + * + * 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 + * + * @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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/RongheyunGateway.php b/vendor/overtrue/easy-sms/src/Gateways/RongheyunGateway.php new file mode 100644 index 00000000..aa63c655 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/RongheyunGateway.php @@ -0,0 +1,69 @@ + + * + * 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; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/SendcloudGateway.php b/vendor/overtrue/easy-sms/src/Gateways/SendcloudGateway.php new file mode 100644 index 00000000..5da3a4fc --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/SendcloudGateway.php @@ -0,0 +1,95 @@ + + * + * 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)); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/SmsbaoGateway.php b/vendor/overtrue/easy-sms/src/Gateways/SmsbaoGateway.php new file mode 100644 index 00000000..b202d4cc --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/SmsbaoGateway.php @@ -0,0 +1,77 @@ + + * + * 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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/SubmailGateway.php b/vendor/overtrue/easy-sms/src/Gateways/SubmailGateway.php new file mode 100644 index 00000000..7d4678ff --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/SubmailGateway.php @@ -0,0 +1,88 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Gateways; + +use Overtrue\EasySms\Contracts\MessageInterface; +use Overtrue\EasySms\Contracts\PhoneNumberInterface; +use Overtrue\EasySms\Exceptions\GatewayErrorException; +use Overtrue\EasySms\Support\Config; +use Overtrue\EasySms\Traits\HasHttpRequest; + +/** + * Class SubmailGateway. + * + * @see https://www.mysubmail.com/chs/documents/developer/index + */ +class SubmailGateway extends Gateway +{ + use HasHttpRequest; + + const ENDPOINT_TEMPLATE = 'https://api.mysubmail.com/%s.%s'; + + const ENDPOINT_FORMAT = 'json'; + + /** + * @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to + * @param \Overtrue\EasySms\Contracts\MessageInterface $message + * @param \Overtrue\EasySms\Support\Config $config + * + * @return array + * + * @throws \Overtrue\EasySms\Exceptions\GatewayErrorException ; + */ + public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config) + { + $endpoint = $this->buildEndpoint($this->inChineseMainland($to) ? 'message/xsend' : 'internationalsms/xsend'); + + $data = $message->getData($this); + + $result = $this->post($endpoint, [ + 'appid' => $config->get('app_id'), + 'signature' => $config->get('app_key'), + 'project' => !empty($data['project']) ? $data['project'] : $config->get('project'), + 'to' => $to->getUniversalNumber(), + 'vars' => json_encode($data, JSON_FORCE_OBJECT), + ]); + + if ('success' != $result['status']) { + throw new GatewayErrorException($result['msg'], $result['code'], $result); + } + + return $result; + } + + /** + * Build endpoint url. + * + * @param string $function + * + * @return string + */ + protected function buildEndpoint($function) + { + return sprintf(self::ENDPOINT_TEMPLATE, $function, self::ENDPOINT_FORMAT); + } + + /** + * Check if the phone number belongs to chinese mainland. + * + * @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to + * + * @return bool + */ + protected function inChineseMainland($to) + { + $code = $to->getIDDCode(); + + return empty($code) || 86 === $code; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/TianyiwuxianGateway.php b/vendor/overtrue/easy-sms/src/Gateways/TianyiwuxianGateway.php new file mode 100644 index 00000000..a30758cc --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/TianyiwuxianGateway.php @@ -0,0 +1,84 @@ + + * + * 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 + */ +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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/TiniyoGateway.php b/vendor/overtrue/easy-sms/src/Gateways/TiniyoGateway.php new file mode 100644 index 00000000..f3faff35 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/TiniyoGateway.php @@ -0,0 +1,85 @@ + + * + * 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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/TinreeGateway.php b/vendor/overtrue/easy-sms/src/Gateways/TinreeGateway.php new file mode 100644 index 00000000..7a72dd96 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/TinreeGateway.php @@ -0,0 +1,77 @@ + + * + * 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; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/TwilioGateway.php b/vendor/overtrue/easy-sms/src/Gateways/TwilioGateway.php new file mode 100644 index 00000000..95e572c6 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/TwilioGateway.php @@ -0,0 +1,91 @@ + + * + * 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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/UcloudGateway.php b/vendor/overtrue/easy-sms/src/Gateways/UcloudGateway.php new file mode 100644 index 00000000..fddfd3c8 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/UcloudGateway.php @@ -0,0 +1,130 @@ + + * + * 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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/Ue35Gateway.php b/vendor/overtrue/easy-sms/src/Gateways/Ue35Gateway.php new file mode 100644 index 00000000..9e05e1d4 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/Ue35Gateway.php @@ -0,0 +1,77 @@ + + * + * 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; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/VolcengineGateway.php b/vendor/overtrue/easy-sms/src/Gateways/VolcengineGateway.php new file mode 100644 index 00000000..536e7c5d --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/VolcengineGateway.php @@ -0,0 +1,311 @@ + + * + * 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 '/'; + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/YunpianGateway.php b/vendor/overtrue/easy-sms/src/Gateways/YunpianGateway.php new file mode 100644 index 00000000..e60942f2 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/YunpianGateway.php @@ -0,0 +1,101 @@ + + * + * 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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/YuntongxunGateway.php b/vendor/overtrue/easy-sms/src/Gateways/YuntongxunGateway.php new file mode 100644 index 00000000..9554434d --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/YuntongxunGateway.php @@ -0,0 +1,123 @@ + + * + * 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); + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/YunxinGateway.php b/vendor/overtrue/easy-sms/src/Gateways/YunxinGateway.php new file mode 100644 index 00000000..8db2784c --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/YunxinGateway.php @@ -0,0 +1,188 @@ + + * + * 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 + * + * @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) + ]; + + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/YunzhixunGateway.php b/vendor/overtrue/easy-sms/src/Gateways/YunzhixunGateway.php new file mode 100644 index 00000000..0e213e4d --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/YunzhixunGateway.php @@ -0,0 +1,121 @@ + + * + * 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 + * + * @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()); + } + } +} diff --git a/vendor/overtrue/easy-sms/src/Gateways/ZzyunGateway.php b/vendor/overtrue/easy-sms/src/Gateways/ZzyunGateway.php new file mode 100644 index 00000000..878a6a75 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Gateways/ZzyunGateway.php @@ -0,0 +1,63 @@ + + * + * 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; + } +} diff --git a/vendor/overtrue/easy-sms/src/Message.php b/vendor/overtrue/easy-sms/src/Message.php new file mode 100644 index 00000000..7b76ca8c --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Message.php @@ -0,0 +1,187 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms; + +use Overtrue\EasySms\Contracts\GatewayInterface; +use Overtrue\EasySms\Contracts\MessageInterface; + +/** + * Class Message. + */ +class Message implements MessageInterface +{ + /** + * @var array + */ + protected $gateways = []; + + /** + * @var string + */ + protected $type; + + /** + * @var string + */ + protected $content; + + /** + * @var string + */ + protected $template; + + /** + * @var array + */ + protected $data = []; + + /** + * Message constructor. + * + * @param array $attributes + * @param string $type + */ + public function __construct(array $attributes = [], $type = MessageInterface::TEXT_MESSAGE) + { + $this->type = $type; + + foreach ($attributes as $property => $value) { + if (property_exists($this, $property)) { + $this->$property = $value; + } + } + } + + /** + * Return the message type. + * + * @return string + */ + public function getMessageType() + { + return $this->type; + } + + /** + * Return message content. + * + * @param \Overtrue\EasySms\Contracts\GatewayInterface|null $gateway + * + * @return string + */ + public function getContent(GatewayInterface $gateway = null) + { + return is_callable($this->content) ? call_user_func($this->content, $gateway) : $this->content; + } + + /** + * Return the template id of message. + * + * @param \Overtrue\EasySms\Contracts\GatewayInterface|null $gateway + * + * @return string + */ + public function getTemplate(GatewayInterface $gateway = null) + { + return is_callable($this->template) ? call_user_func($this->template, $gateway) : $this->template; + } + + /** + * @param $type + * + * @return $this + */ + public function setType($type) + { + $this->type = $type; + + return $this; + } + + /** + * @param mixed $content + * + * @return $this + */ + public function setContent($content) + { + $this->content = $content; + + return $this; + } + + /** + * @param mixed $template + * + * @return $this + */ + public function setTemplate($template) + { + $this->template = $template; + + return $this; + } + + /** + * @param \Overtrue\EasySms\Contracts\GatewayInterface|null $gateway + * + * @return array + */ + public function getData(GatewayInterface $gateway = null) + { + return is_callable($this->data) ? call_user_func($this->data, $gateway) : $this->data; + } + + /** + * @param array|callable $data + * + * @return $this + */ + public function setData($data) + { + $this->data = $data; + + return $this; + } + + /** + * @return array + */ + public function getGateways() + { + return $this->gateways; + } + + /** + * @param array $gateways + * + * @return $this + */ + public function setGateways(array $gateways) + { + $this->gateways = $gateways; + + return $this; + } + + /** + * @param $property + * + * @return string + */ + public function __get($property) + { + if (property_exists($this, $property)) { + return $this->$property; + } + } +} diff --git a/vendor/overtrue/easy-sms/src/Messenger.php b/vendor/overtrue/easy-sms/src/Messenger.php new file mode 100644 index 00000000..97b704c5 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Messenger.php @@ -0,0 +1,92 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms; + +use Overtrue\EasySms\Contracts\MessageInterface; +use Overtrue\EasySms\Contracts\PhoneNumberInterface; +use Overtrue\EasySms\Exceptions\NoGatewayAvailableException; + +/** + * Class Messenger. + */ +class Messenger +{ + const STATUS_SUCCESS = 'success'; + + const STATUS_FAILURE = 'failure'; + + /** + * @var \Overtrue\EasySms\EasySms + */ + protected $easySms; + + /** + * Messenger constructor. + * + * @param \Overtrue\EasySms\EasySms $easySms + */ + public function __construct(EasySms $easySms) + { + $this->easySms = $easySms; + } + + /** + * Send a message. + * + * @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to + * @param \Overtrue\EasySms\Contracts\MessageInterface $message + * @param array $gateways + * + * @return array + * + * @throws \Overtrue\EasySms\Exceptions\NoGatewayAvailableException + */ + public function send(PhoneNumberInterface $to, MessageInterface $message, array $gateways = []) + { + $results = []; + $isSuccessful = false; + + foreach ($gateways as $gateway => $config) { + try { + $results[$gateway] = [ + 'gateway' => $gateway, + 'status' => self::STATUS_SUCCESS, + 'template' => $message->getTemplate($this->easySms->gateway($gateway)), + 'result' => $this->easySms->gateway($gateway)->send($to, $message, $config), + ]; + $isSuccessful = true; + + break; + } catch (\Exception $e) { + $results[$gateway] = [ + 'gateway' => $gateway, + 'status' => self::STATUS_FAILURE, + 'template' => $message->getTemplate($this->easySms->gateway($gateway)), + 'exception' => $e, + ]; + } catch (\Throwable $e) { + $results[$gateway] = [ + 'gateway' => $gateway, + 'status' => self::STATUS_FAILURE, + 'template' => $message->getTemplate($this->easySms->gateway($gateway)), + 'exception' => $e, + ]; + } + } + + if (!$isSuccessful) { + throw new NoGatewayAvailableException($results); + } + + return $results; + } +} diff --git a/vendor/overtrue/easy-sms/src/PhoneNumber.php b/vendor/overtrue/easy-sms/src/PhoneNumber.php new file mode 100644 index 00000000..cde95f5d --- /dev/null +++ b/vendor/overtrue/easy-sms/src/PhoneNumber.php @@ -0,0 +1,126 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms; + +/** + * Class PhoneNumberInterface. + * + * @author overtrue + */ +class PhoneNumber implements \Overtrue\EasySms\Contracts\PhoneNumberInterface +{ + /** + * @var int + */ + protected $number; + + /** + * @var int + */ + protected $IDDCode; + + /** + * PhoneNumberInterface constructor. + * + * @param int $numberWithoutIDDCode + * @param string $IDDCode + */ + public function __construct($numberWithoutIDDCode, $IDDCode = null) + { + $this->number = $numberWithoutIDDCode; + $this->IDDCode = $IDDCode ? intval(ltrim($IDDCode, '+0')) : null; + } + + /** + * 86. + * + * @return int + */ + public function getIDDCode() + { + return $this->IDDCode; + } + + /** + * 18888888888. + * + * @return int + */ + public function getNumber() + { + return $this->number; + } + + /** + * +8618888888888. + * + * @return string + */ + public function getUniversalNumber() + { + return $this->getPrefixedIDDCode('+').$this->number; + } + + /** + * 008618888888888. + * + * @return string + */ + public function getZeroPrefixedNumber() + { + return $this->getPrefixedIDDCode('00').$this->number; + } + + /** + * @param string $prefix + * + * @return string|null + */ + public function getPrefixedIDDCode($prefix) + { + return $this->IDDCode ? $prefix.$this->IDDCode : null; + } + + /** + * @return string + */ + public function __toString() + { + return $this->getUniversalNumber(); + } + + /** + * Specify data which should be serialized to JSON. + * + * @see http://php.net/manual/en/jsonserializable.jsonserialize.php + * + * @return mixed data which can be serialized by json_encode, + * which is a value of any type other than a resource + * + * @since 5.4.0 + */ + #[\ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getUniversalNumber(); + } + + /** + * Check if the phone number belongs to chinese mainland. + * + * @return bool + */ + public function inChineseMainland() + { + return empty($this->IDDCode) || $this->IDDCode === 86; + } +} diff --git a/vendor/overtrue/easy-sms/src/Strategies/OrderStrategy.php b/vendor/overtrue/easy-sms/src/Strategies/OrderStrategy.php new file mode 100644 index 00000000..f44b29ef --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Strategies/OrderStrategy.php @@ -0,0 +1,32 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Strategies; + +use Overtrue\EasySms\Contracts\StrategyInterface; + +/** + * Class OrderStrategy. + */ +class OrderStrategy implements StrategyInterface +{ + /** + * Apply the strategy and return result. + * + * @param array $gateways + * + * @return array + */ + public function apply(array $gateways) + { + return array_keys($gateways); + } +} diff --git a/vendor/overtrue/easy-sms/src/Strategies/RandomStrategy.php b/vendor/overtrue/easy-sms/src/Strategies/RandomStrategy.php new file mode 100644 index 00000000..605c5d9f --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Strategies/RandomStrategy.php @@ -0,0 +1,34 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Strategies; + +use Overtrue\EasySms\Contracts\StrategyInterface; + +/** + * Class RandomStrategy. + */ +class RandomStrategy implements StrategyInterface +{ + /** + * @param array $gateways + * + * @return array + */ + public function apply(array $gateways) + { + uasort($gateways, function () { + return mt_rand() - mt_rand(); + }); + + return array_keys($gateways); + } +} diff --git a/vendor/overtrue/easy-sms/src/Support/Config.php b/vendor/overtrue/easy-sms/src/Support/Config.php new file mode 100644 index 00000000..3b914234 --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Support/Config.php @@ -0,0 +1,147 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Support; + +use ArrayAccess; + +/** + * Class Config. + */ +class Config implements ArrayAccess +{ + /** + * @var array + */ + protected $config; + + /** + * Config constructor. + * + * @param array $config + */ + public function __construct(array $config = []) + { + $this->config = $config; + } + + /** + * Get an item from an array using "dot" notation. + * + * @param string $key + * @param mixed $default + * + * @return mixed + */ + public function get($key, $default = null) + { + $config = $this->config; + + if (isset($config[$key])) { + return $config[$key]; + } + + if (false === strpos($key, '.')) { + return $default; + } + + foreach (explode('.', $key) as $segment) { + if (!is_array($config) || !array_key_exists($segment, $config)) { + return $default; + } + $config = $config[$segment]; + } + + return $config; + } + + /** + * Whether a offset exists. + * + * @see http://php.net/manual/en/arrayaccess.offsetexists.php + * + * @param mixed $offset

+ * An offset to check for. + *

+ * + * @return bool true on success or false on failure. + *

+ *

+ * The return value will be casted to boolean if non-boolean was returned + * + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetExists($offset) + { + return array_key_exists($offset, $this->config); + } + + /** + * Offset to retrieve. + * + * @see http://php.net/manual/en/arrayaccess.offsetget.php + * + * @param mixed $offset

+ * The offset to retrieve. + *

+ * + * @return mixed Can return all value types + * + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->get($offset); + } + + /** + * Offset to set. + * + * @see http://php.net/manual/en/arrayaccess.offsetset.php + * + * @param mixed $offset

+ * The offset to assign the value to. + *

+ * @param mixed $value

+ * The value to set. + *

+ * + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetSet($offset, $value) + { + if (isset($this->config[$offset])) { + $this->config[$offset] = $value; + } + } + + /** + * Offset to unset. + * + * @see http://php.net/manual/en/arrayaccess.offsetunset.php + * + * @param mixed $offset

+ * The offset to unset. + *

+ * + * @since 5.0.0 + */ + #[\ReturnTypeWillChange] + public function offsetUnset($offset) + { + if (isset($this->config[$offset])) { + unset($this->config[$offset]); + } + } +} diff --git a/vendor/overtrue/easy-sms/src/Traits/HasHttpRequest.php b/vendor/overtrue/easy-sms/src/Traits/HasHttpRequest.php new file mode 100644 index 00000000..d861dedf --- /dev/null +++ b/vendor/overtrue/easy-sms/src/Traits/HasHttpRequest.php @@ -0,0 +1,136 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Traits; + +use GuzzleHttp\Client; +use Psr\Http\Message\ResponseInterface; + +/** + * Trait HasHttpRequest. + */ +trait HasHttpRequest +{ + /** + * Make a get request. + * + * @param string $endpoint + * @param array $query + * @param array $headers + * + * @return ResponseInterface|array|string + */ + protected function get($endpoint, $query = [], $headers = []) + { + return $this->request('get', $endpoint, [ + 'headers' => $headers, + 'query' => $query, + ]); + } + + /** + * Make a post request. + * + * @param string $endpoint + * @param array $params + * @param array $headers + * + * @return ResponseInterface|array|string + */ + protected function post($endpoint, $params = [], $headers = []) + { + return $this->request('post', $endpoint, [ + 'headers' => $headers, + 'form_params' => $params, + ]); + } + + /** + * Make a post request with json params. + * + * @param $endpoint + * @param array $params + * @param array $headers + * + * @return ResponseInterface|array|string + */ + protected function postJson($endpoint, $params = [], $headers = []) + { + return $this->request('post', $endpoint, [ + 'headers' => $headers, + 'json' => $params, + ]); + } + + /** + * Make a http request. + * + * @param string $method + * @param string $endpoint + * @param array $options http://docs.guzzlephp.org/en/latest/request-options.html + * + * @return ResponseInterface|array|string + */ + protected function request($method, $endpoint, $options = []) + { + return $this->unwrapResponse($this->getHttpClient($this->getBaseOptions())->{$method}($endpoint, $options)); + } + + /** + * Return base Guzzle options. + * + * @return array + */ + protected function getBaseOptions() + { + $options = method_exists($this, 'getGuzzleOptions') ? $this->getGuzzleOptions() : []; + + return \array_merge($options, [ + 'base_uri' => method_exists($this, 'getBaseUri') ? $this->getBaseUri() : '', + 'timeout' => method_exists($this, 'getTimeout') ? $this->getTimeout() : 5.0, + ]); + } + + /** + * Return http client. + * + * @param array $options + * + * @return \GuzzleHttp\Client + * + * @codeCoverageIgnore + */ + protected function getHttpClient(array $options = []) + { + return new Client($options); + } + + /** + * Convert response contents to json. + * + * @param \Psr\Http\Message\ResponseInterface $response + * + * @return ResponseInterface|array|string + */ + protected function unwrapResponse(ResponseInterface $response) + { + $contentType = $response->getHeaderLine('Content-Type'); + $contents = $response->getBody()->getContents(); + + if (false !== stripos($contentType, 'json') || stripos($contentType, 'javascript')) { + return json_decode($contents, true); + } elseif (false !== stripos($contentType, 'xml')) { + return json_decode(json_encode(simplexml_load_string($contents)), true); + } + + return $contents; + } +}