From a34fd69add36e9cd8ee4226b542d85e38e9a27d8 Mon Sep 17 00:00:00 2001 From: curder Date: Wed, 5 Jan 2022 17:01:15 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=98=BF=E9=87=8C=E4=BA=91?= =?UTF-8?q?=E5=9B=BD=E9=99=85=E7=9F=AD=E4=BF=A1=E5=8F=91=E9=80=81=E7=BD=91?= =?UTF-8?q?=E5=85=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 29 +++++++ src/Gateways/AliyunIntlGateway.php | 97 ++++++++++++++++++++++++ tests/Gateways/AliyunIntlGatewayTest.php | 86 +++++++++++++++++++++ 3 files changed, 212 insertions(+) create mode 100644 src/Gateways/AliyunIntlGateway.php create mode 100644 tests/Gateways/AliyunIntlGatewayTest.php diff --git a/README.md b/README.md index 08fdfe5..8632be2 100644 --- a/README.md +++ b/README.md @@ -359,6 +359,35 @@ $easySms->send(13188888888, $message); ], ``` +### [阿里云国际](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` diff --git a/src/Gateways/AliyunIntlGateway.php b/src/Gateways/AliyunIntlGateway.php new file mode 100644 index 0000000..e3959ce --- /dev/null +++ b/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): string + { + 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/tests/Gateways/AliyunIntlGatewayTest.php b/tests/Gateways/AliyunIntlGatewayTest.php new file mode 100644 index 0000000..2ae70c7 --- /dev/null +++ b/tests/Gateways/AliyunIntlGatewayTest.php @@ -0,0 +1,86 @@ + 'mock-api-key', + 'access_key_secret' => 'mock-api-secret', + 'sign_name' => 'mock-api-sign-name', + 'template_code' => 'mock-template-code', + ]; + $gateway = \Mockery::mock(AliyunIntlGateway::class.'[get]', [$config])->shouldAllowMockingProtectedMethods(); + + $expected = [ + 'RegionId' => 'ap-southeast-1', + 'AccessKeyId' => 'mock-api-key', + 'Format' => 'JSON', + 'SignatureMethod' => 'HMAC-SHA1', + 'SignatureVersion' => '1.0', + // 'SignatureNonce' => uniqid('', true), + // 'Timestamp' => date('Y-m-d\TH:i:s\Z'), + 'Version' => '2018-05-01', + 'To' => (string) new PhoneNumber(18888888888), + 'Action' => 'SendMessageWithTemplate', + 'From' => 'mock-api-sign-name', + 'TemplateCode' => 'mock-template-code', + 'TemplateParam' => json_encode(['code' => '123456']), + ]; + + $gateway->shouldReceive('get') + ->with(AliyunIntlGateway::ENDPOINT_URL, \Mockery::on(function ($params) use ($expected) { + if (empty($params['Signature'])) { + return false; + } + + unset($params['SignatureNonce'], $params['Timestamp'], $params['Signature']); + + ksort($params); + ksort($expected); + + return $params == $expected; + })) + ->andReturn([ + 'ResponseCode' => 'OK', + 'ResponseDescription' => 'mock-result', + ], [ + 'ResponseCode' => 1234, + 'ResponseDescription' => 'mock-err-msg', + ]) + ->twice(); + + $message = new Message([ + 'template' => 'mock-template-code', + 'data' => ['code' => '123456'], + ]); + + $config = new Config($config); + + $this->assertSame([ + 'ResponseCode' => 'OK', + 'ResponseDescription' => 'mock-result', + ], $gateway->send(new PhoneNumber(18888888888), $message, $config)); + + $this->expectException(GatewayErrorException::class); + $this->expectExceptionCode(1234); + $this->expectExceptionMessage('mock-err-msg'); + + $gateway->send(new PhoneNumber(18888888888), $message, $config); + } +}