diff --git a/README.md b/README.md index 87f892c..e1bb2d7 100644 --- a/README.md +++ b/README.md @@ -49,6 +49,7 @@ - [Ucloud](https://www.ucloud.cn) - [短信宝](http://www.smsbao.com/) - [Tiniyo](https://tiniyo.com/) +- [摩杜云](https://www.moduyun.com/) ## 环境需求 @@ -732,6 +733,30 @@ $easySms->send(18888888888, [ ``` +### [摩杜云](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} + //... + ], +]); + +``` + ## PHP 扩展包开发 > 想知道如何从零开始构建 PHP 扩展包? diff --git a/src/Gateways/MoDuYunGateway.php b/src/Gateways/MoDuYunGateway.php new file mode 100644 index 0000000..a3defea --- /dev/null +++ b/src/Gateways/MoDuYunGateway.php @@ -0,0 +1,106 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Gateways; + +use Overtrue\EasySms\Contracts\MessageInterface; +use Overtrue\EasySms\Contracts\PhoneNumberInterface; +use Overtrue\EasySms\Exceptions\GatewayErrorException; +use Overtrue\EasySms\Support\Config; +use Overtrue\EasySms\Traits\HasHttpRequest; + +/** + * Class MoDuYunGateway. + * + * @see https://www.moduyun.com/doc/index.html#10002 + */ +class MoDuYunGateway extends Gateway +{ + use HasHttpRequest; + + const ENDPOINT_URL = 'https://live.moduyun.com/sms/v2/sendsinglesms'; + + /** + * @param \Overtrue\EasySms\Contracts\PhoneNumberInterface $to + * @param \Overtrue\EasySms\Contracts\MessageInterface $message + * @param \Overtrue\EasySms\Support\Config $config + * + * @return array + * + * @throws \Overtrue\EasySms\Exceptions\GatewayErrorException ; + */ + public function send(PhoneNumberInterface $to, MessageInterface $message, Config $config) + { + $urlParams = [ + 'accesskey' => $config->get('accesskey'), + 'random' => rand(100000, 999999), + ]; + + $params = [ + 'tel' => [ + 'mobile' => $to->getNumber(), + 'nationcode' => $to->getIDDCode() ?: '86', + ], + 'signId' => $config->get('signId', ''), + 'templateId' => $message->getTemplate($this), + 'time' => time(), + 'type' => $config->get('type', 0), + 'params' => array_values($message->getData($this)), + 'ext' => '', + 'extend' => '', + ]; + $params['sig'] = $this->generateSign($params, $urlParams['random']); + + $result = $this->request('post', $this->getEndpointUrl($urlParams), [ + 'headers' => [ + 'Content-Type' => 'application/json', + 'Accept' => 'application/json' + ], + 'json' => $params, + ]); + + $result = is_string($result) ? json_decode($result, true) : $result; + if (0 != $result['result']) { + throw new GatewayErrorException($result['errmsg'], $result['result'], $result); + } + + return $result; + } + + /** + * @param array $params + * + * @return string + */ + protected function getEndpointUrl($params) + { + return self::ENDPOINT_URL . '?' . http_build_query($params); + } + + /** + * Generate Sign. + * + * @param array $params + * @param string $random + * + * @return string + */ + protected function generateSign($params, $random) + { + return hash('sha256', sprintf( + 'secretkey=%s&random=%d&time=%d&mobile=%s', + $this->config->get('secretkey'), + $random, + $params['time'], + $params['tel']['mobile'] + )); + } +} diff --git a/tests/Gateways/MoDuYunGatewayTest.php b/tests/Gateways/MoDuYunGatewayTest.php new file mode 100644 index 0000000..94769a5 --- /dev/null +++ b/tests/Gateways/MoDuYunGatewayTest.php @@ -0,0 +1,71 @@ + + * + * This source file is subject to the MIT license that is bundled + * with this source code in the file LICENSE. + */ + +namespace Overtrue\EasySms\Tests\Gateways; + +use Overtrue\EasySms\Exceptions\GatewayErrorException; +use Overtrue\EasySms\Gateways\MoDuYunGateway; +use Overtrue\EasySms\Message; +use Overtrue\EasySms\PhoneNumber; +use Overtrue\EasySms\Support\Config; +use Overtrue\EasySms\Tests\TestCase; + +class MoDuYunGatewayTest extends TestCase +{ + public function testSend() + { + $config = [ + 'accesskey' => 'mock-accesskey', + 'secretkey' => 'mock-secretkey', + ]; + $gateway = \Mockery::mock(MoDuYunGateway::class . '[request]', [$config])->shouldAllowMockingProtectedMethods(); + + $gateway->shouldReceive('request') + ->andReturn( + [ + 'result' => 0, + 'errmsg' => 'OK', + 'ext' => '', + 'sid' => "mock-sid", + 'surplus' => 4, + 'balance' => 0, + ], + [ + 'result' => 1001, + 'errmsg' => 'accesskey not exist.', + ] + )->twice(); + + $message = new Message([ + 'template' => 'mock-template', + 'data' => [ + 'code' => 1234, + ], + ]); + + $config = new Config($config); + + $this->assertSame([ + 'result' => 0, + 'errmsg' => 'OK', + 'ext' => '', + 'sid' => "mock-sid", + 'surplus' => 4, + 'balance' => 0, + ], $gateway->send(new PhoneNumber(18888888888), $message, $config)); + + $this->expectException(GatewayErrorException::class); + $this->expectExceptionCode(1001); + $this->expectExceptionMessage('accesskey not exist.'); + + $gateway->send(new PhoneNumber(18888888888), $message, $config); + } +}