diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..ff27431 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,15 @@ +* text=auto + +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +/tests export-ignore +.editorconfig export-ignore +.gitattributes export-ignore +.gitignore export-ignore +.styleci.yml export-ignore +CHANGELOG.md export-ignore +phpunit.xml.dist export-ignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..660fc15 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +/vendor +composer.lock +/phpunit.xml +.phpunit.result.cache diff --git a/composer.json b/composer.json index c1f4008..6fbcbbf 100755 --- a/composer.json +++ b/composer.json @@ -16,14 +16,28 @@ } ], "require": { - "php": ">=7.1", - "mane-olawale/termii": "^1.0" + "php": ">=7.2", + "mane-olawale/termii": "^1.0", + "illuminate/notifications": "^6.0|^7.0|^8.0", + "illuminate/support": "^6.0|^7.0|^8.0" }, + "require-dev": { + "phpunit/phpunit": "~8.0|~9.0", + "mockery/mockery": "~1.4" + }, "autoload": { "psr-4": { "ManeOlawale\\Laravel\\Termii\\": "src/" } }, + "autoload-dev": { + "psr-4": { + "ManeOlawale\\Laravel\\Termii\\Tests\\": "tests/" + } + }, + "scripts": { + "tests": "./vendor/bin/phpunit --verbose" + }, "extra": { "laravel": { "providers": [ diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 0000000..1a6d00f --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + ./tests + + + diff --git a/src/Channels/TermiiSmsChannel.php b/src/Channels/TermiiSmsChannel.php index 846294e..3a2ad17 100644 --- a/src/Channels/TermiiSmsChannel.php +++ b/src/Channels/TermiiSmsChannel.php @@ -5,6 +5,7 @@ use ManeOlawale\Laravel\Termii\Messages\TermiiMessage; use Illuminate\Notifications\Notification; use ManeOlawale\Laravel\Termii\Termii; +use ManeOlawale\Termii\Client; class TermiiSmsChannel { @@ -53,7 +54,7 @@ public function send($notifiable, Notification $notification) if (is_string($message)) { $message = new TermiiMessage($message); } - + if ($message->client){ $client = $this->termii->client(); $this->termii->usingClient($message->client); @@ -66,20 +67,7 @@ public function send($notifiable, Notification $notification) } return $result; - -/* - $payload = [ - 'type' => $message->type, - 'from' => $message->from ?: $this->from, - 'to' => $to, - 'text' => trim($message->content), - 'client-ref' => $message->clientReference, - ]; - - if ($message->statusCallback) { - $payload['callback'] = $message->statusCallback; - } - return ($message->client ?? $this->termii)->sms->send($to, $message); */ } + } diff --git a/src/Entities/Token.php b/src/Entities/Token.php new file mode 100644 index 0000000..6ef5e3f --- /dev/null +++ b/src/Entities/Token.php @@ -0,0 +1,414 @@ +key = $key; + + if ($signature){ + $this->loadFromSignature($signature); + } else if ( ($session = $this->getRequest()->session()) ) { + $this->loadFromSession($session, $key); + } + } + + /** + * Get the current HTTP request + */ + public function getRequest() + { + return App::make('request'); + } + + /** + * Fetch the payload from an encrypted string + * + * @param string @signature + * @return void + */ + protected function loadFromSignature(string $signature) + { + try { + $json = Crypt::decryptString($signature); + } catch (DecryptException $e) { + return false; + } + + $this->signature = $signature; + + $this->payload = json_decode($json, true) ?? []; + + $this->updateProperties(); + + $this->loaded = ($this->payload)? true : false; + } + + /** + * Fetch the payload from session + * + * @param string @key + * @return void + */ + protected function loadFromSession($session, string $key) + { + + $this->payload = json_decode($session->get($key), true) ?? []; + + $this->updateProperties(); + + $this->loaded = ($this->payload)? true : false; + } + + /** + * Flush all the content and session of the instance + * left only with phone number, key and text + * + * @return self $this; + */ + public function flush() + { + + $this->pin_id = null; + + $this->pin = null; + + $this->expires_at = null; + + $this->generated_at = null; + + $this->loaded = false; + + $this->signature = null; + + $this->payload = []; + + if ( ($session = $this->getRequest()->session())) { + $session->forget($this->key); + } + + return $this; + } + + /** + * Update the instance properties from the payload + * + * @return void + */ + protected function updateProperties() + { + if (!$this->payload){ + return false; + } + + $this->key = $this->payload['key']; + + $this->pin_id = $this->payload['pin_id']; + + $this->pin = $this->payload['pin'] ?? null; + + $this->expires_at = Date::parse($this->payload['expires_at']); + + $this->generated_at = Date::parse($this->payload['generated_at']); + + $this->phonenumber = $this->payload['phonenumber']; + + $this->in_app = $this->payload['in_app']; + } + + /** + * Make the token an in-app token, Only for non-loaded instance of this class + * + * @return self $this + */ + public function inApp() + { + if ($this->loaded){ + return $this; + } + + $this->in_app = true; + + return $this; + } + + /** + * Set the phonenumber of the token + * + * @return self $this + */ + public function to(string $phonenumber) + { + if ($this->loaded){ + return $this; + } + + $this->phonenumber = $phonenumber; + + return $this; + } + + /** + * Set the text of the token + * + * @return self $this + */ + public function text(string $text) + { + if ($this->loaded){ + return $this; + } + + $this->text = $text; + + return $this; + } + + /** + * Dynamic function calls to set the pin option + * + * @return self $this + */ + public function __call(string $name, array $parameters) + { + $this->pin_options[$name] = count($parameters)? $parameters[0] : true; + } + + /** + * Get the pin id + * + * @return string + */ + public function id() + { + if ($this->loaded){ + return false; + } + return $this->pin_id; + } + + /** + * Get the key of the instance + * + * @return string + */ + public function key() + { + return $this->key; + } + + /** + * Get the pin, Only for in-app tokens * + * @return string + */ + public function pin() + { + if ($this->loaded){ + return false; + } + return $this->pin; + } + + /** + * Get the signature + * * + * @return string + */ + public function signature() + { + return $this->signature = Crypt::encryptString(json_encode($this->payload ?? [])); + } + + /** + * This generate a new token resourse from termii + * + * @param array $options + */ + public function start( array $options = []) + { + if ($this->loaded){ + return false; + } + + $token = Termii::token(); + + $options = $this->pin_options + $options; + + if ($this->in_app){ + $data = $token->sendInAppToken($this->phonenumber, $options); + } else { + $data = $token->sendToken($this->phonenumber, $this->text, $options); + } + + + $this->payload['key'] = $this->key; + + if ($this->in_app){ + + $this->payload['pin_id'] = $data['data']['pin_id']; + + $this->payload['pin'] = $data['data']['otp']; + + $this->payload['phonenumber'] = $data['data']['phone_number']; + + }else{ + + $this->payload['pin_id'] = $data['pinId']; + + $this->payload['pin'] = null; + + $this->payload['phonenumber'] = $data['to']; + + } + + $this->payload['expires_at'] = (string)Date::now()->addMinutes($options['time_to_live'] ?? config('pin.time_to_live')); + + $this->payload['generated_at'] = (string)Date::now(); + + $this->payload['in_app'] = $this->in_app; + + + $this->updateProperties(); + + $this->loaded = true; + + if ( ($session = $this->getRequest()->session())) { + $session->put($this->key, json_encode($this->payload)); + } + + + + return $this; + } + + /** + * Change if a + */ + public function isValid() + { + if (!$this->loaded) return false; + + return $this->pin_id && $this->expires_at > Date::now(); + } + + /** + * Verify the token + * + * @param string + */ + public function verify(string $pin) + { + if (!is_int($pin) && !is_string($pin)){ + return false; + } + + if ($this->in_app){ + + return ($this->pin === $pin); + + }else{ + + $token = Termii::token(); + + return $token->verified($this->pin_id, $pin);; + } + } + + public function __serialize(): array + { + return $this->payload; + } + + public function __unserialize(array $data): void + { + $this->payload = $data; + + $this->updateProperties(); + } + + public function __toString() + { + return json_encode($this->payload); + } + + +} diff --git a/src/Messages/Message.php b/src/Messages/Message.php new file mode 100644 index 0000000..1d67daf --- /dev/null +++ b/src/Messages/Message.php @@ -0,0 +1,136 @@ +content = $content; + } + + /** + * Set the message content. + * + * @param string $content + * @return $this + */ + public function content( string $content) + { + $this->content = $content; + + return $this; + } + + /** + * Set the sender id, Device id or phone number the message should be sent from. + * + * @param string $from + * @return $this + */ + public function from($from) + { + $this->from = $from; + + return $this; + } + + /** + * Set the message channel. + * + * @param string $from + * @return $this + */ + public function channel( string $channel) + { + $this->channel = $channel; + + return $this; + } + + /** + * Set the message type. + * + * @return $this + */ + public function unicode() + { + $this->type('unicode'); + + return $this; + } + + /** + * Set the message type. + * + * @param string $type + * @return $this + */ + public function type(string $type) + { + $this->type = $type; + + return $this; + } + + /** + * Set the Termii client instance. + * + * @param \ManeOlawale\Termii\Client $client + * @return $this + */ + public function client(Client $client) + { + $this->client = $client; + + return $this; + } + + public function getContent() : string + { + return $this->content; + } +} diff --git a/src/Messages/TermiiMessage.php b/src/Messages/TermiiMessage.php index be5bb93..2029ac1 100644 --- a/src/Messages/TermiiMessage.php +++ b/src/Messages/TermiiMessage.php @@ -2,76 +2,16 @@ namespace ManeOlawale\Laravel\Termii\Messages; -use ManeOlawale\Termii\Client; - -class TermiiMessage +class TermiiMessage extends Message { - /** - * The message content. - * - * @var string - */ - public $content; /** - * The message content. + * Array of content lines. * * @var array */ public $lines = []; - /** - * The phone number the message should be sent from. - * - * @var string - */ - public $from; - - /** - * The message type. - * - * @var string - */ - public $type = 'text'; - - /** - * The message channel. - * - * @var string - */ - public $channel; - - /** - * The custom Termii client instance. - * - * @var \ManeOlawale\Termii\Client|null - */ - public $client; - - /** - * Create a new message instance. - * - * @param string $content - * @return void - */ - public function __construct( string $content = '') - { - $this->content = $content; - } - - /** - * Set the message content. - * - * @param string $content - * @return $this - */ - public function content( string $content) - { - $this->content = $content; - - return $this; - } - /** * Add a line of text to the message content. * @@ -85,74 +25,11 @@ public function line( string $text = null) return $this; } - /** - * Set the sender id, Device id or phone number the message should be sent from. - * - * @param string $from - * @return $this - */ - public function from($from) - { - $this->from = $from; - - return $this; - } - - /** - * Set the message channel. - * - * @param string $from - * @return $this - */ - public function channel( string $channel) - { - $this->channel = $channel; - - return $this; - } - - /** - * Set the message type. - * - * @return $this - */ - public function unicode() - { - $this->type('unicode'); - - return $this; - } - - /** - * Set the message type. - * - * @param string $type - * @return $this - */ - public function type(string $type) - { - $this->type = $type; - - return $this; - } - - /** - * Set the Termii client instance. - * - * @param \ManeOlawale\Termii\Client $client - * @return $this - */ - public function client(Client $client) - { - $this->client = $client; - - return $this; - } - - public function getContent() + public function getContent() : string { $lines = (($this->content)? "\n" : "").implode( "\n",$this->lines); return $this->content.$lines; } + } diff --git a/src/Termii.php b/src/Termii.php index 273eaeb..9935000 100644 --- a/src/Termii.php +++ b/src/Termii.php @@ -19,12 +19,12 @@ class Termii * * @param \ManeOlawale\Termii\Client $client */ - public function __contruct( Client $client ) + public function __construct( Client $client ) { $this->client = $client; } - public function __get(string $tag) + public function __call(string $tag, array $argv) { return $this->client->api($tag); } @@ -39,6 +39,17 @@ public function client() return $this->client; } + /** + * Make new teken + * + * @param string $key + * @param string $signature + */ + public function verify(string $key, string $signature = null) + { + return new Entities\Token($key, $signature); + } + /** * Change the Termii client instance. * @@ -54,7 +65,7 @@ public function usingClient(Client $client) public function send(string $to, string $message, string $from = null, string $channel = null) { - $this->client->sms->send($to, $message, $from, $channel); + return $this->client->sms->send($to, $message, $from, $channel); } } diff --git a/src/TermiiServiceProvider.php b/src/TermiiServiceProvider.php index cc57724..9e30c6f 100644 --- a/src/TermiiServiceProvider.php +++ b/src/TermiiServiceProvider.php @@ -19,7 +19,7 @@ public function register() $this->app->singleton( Termii::class, function (){ - return (new Termii)->usingClient( new TermiiClient( config('termii.key'), $this->getOptions()) ); + return new Termii( new TermiiClient( config('termii.key'), $this->getOptions()) ) ; });