Prepare

  1. Laravel
  2. Redis
  3. Apply Nexmo account

Install nexmo/client

install nexmo/client

1
composer require nexmo/client

config/services.php

1
2
3
4
5
6
7
'nexmo' => [
    'key' => env('NEXMO_KEY'),
    'secret' => env('NEXMO_SECRET'),
    'sms_from' => env('NEXMO_FROM'),
    'ttl' => 600,                       //otp存活時間
    'retry_after' => 120,               //下次傳送等待時間(避免濫發)
],

.env

1
2
3
NEXMO_KEY=[your nexmo key]
NEXMO_SECRET=[your nexmo secret]
NEXMO_FROM=[your nexmo register/buy phone]

you can get nexmo keys when logging

NEXMO_FROM : 我是使用註冊時所登入的號碼,預設會是Test numbers
注意Test Number是有使用上限

Create route, controller

routes/api.php

1
Route::get('/otp/{phone}', 'SMSController@demo');

app/Http/Controllers/SMSController.php

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
namespace App\Http\Controllers;

use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Redis;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class SMSController extends Controller
{
    /**
     * Send demo OTP 
     *
     * @param string $phone 
     * 
     * @return json user object
     */
    public function demo(string $phone)
    {
        $otp = mt_rand(1000000, 9999999);

        $key = "NEXMO:SMS:" . $phone;
        $key_retry = $key . ':RETRY';
        $ttl_retry = Redis::ttl($key_retry);
        if ($ttl_retry > 0) {
            throw new BadRequestHttpException(
                "Please wait $ttl_retry sec and retry", 
                null, 
                0, 
                ['Retry-After' => $ttl_retry]
            );
        }
        Redis::setex($key_retry, $retry_ttl, 1);
        Redis::setex($key, $key_ttl, $value);

        //create client with api key and secret
        $client = new \Nexmo\Client(
            new \Nexmo\Client\Credentials\Basic(config('services.nexmo.key'), config('services.nexmo.secret'))
        );

        //send message using simple api params
        $message = $client->message()->send(
            [
            'to' => $phone,
            'from' => config('services.nexmo.sms_from'),
            'text' => sprintf('Your OTP code is %s, please activite this in %d mins', $otp, (config('services.nexmo.ttl') / 60))
            ]
        );
        
        return response()->json(
            [
            "message" => "Successfully send demo sms"
            ], 200
        );
    }
}

Run Test

1
php artisan serve

http://localhost:8000/api/otp/demo/886912345678

Conclusion & Feature

  1. nexmo傳入電話號碼為全部數字,沒有+ 號
  2. OTP產生7碼數字
  3. Redis有TTL特性,可以設定每個key的存活時間,這邊設定2種ttl。1是otp ttl;2是retry otp ttl 。 20~32行代碼中提到
  4. 34~46行待碼為傳簡訊方法,可以將它方封裝成job queue方式在背景執行,當大量request時減少api回傳時間
  5. 此Redis TTL應用方法也可以用在傳Email上