[Laravel] Tích hợp thông báo Line với Line Notify

[Laravel] Tích hợp thông báo Line với Line Notify

Hello everybody, trong bài viết này mình sẽ hướng dẫn cách tích hợp dịch vụ thông báo tới tài khoản Line với service Line Notify của LINE 😉.

Line Notify là một dịch vụ do Line cung cấp để đẩy thông báo đến tài khoản Line. Chúng ta có thể sử dụng Line Notify để đẩy các notification từ ứng dụng của mình đến tài khoản Line của người dùng đăng ký nhận thông báo.

Cơ chế hoạt động

Cơ chế hoạt động của Line Notify là khi người dùng đăng ký nhận thông tin từ một hệ thống ngoài, phía Line sẽ gửi cho các hệ thống ngoài access_token, token này sử dụng trong quá trình đẩy các thông báo đến người dùng Line cho đến khi người dùng hủy đăng ký nhận thông báo.

Các thông báo của hệ thống ngoài sẽ được gửi đến người dùng Line thông qua một con bot của Line.

Cơ chế để lấy access_token của Line luôn gồm 2 bước.

  • Bước 1: Người dùng login tài khoản Line và đăng ký sử dụng dịch vụ Line Notify. Phía Line trả về codestate
  • Bước 2: Sử dụng code ở bước 1 để lấy access_token. Token này sẽ được sử dụng nhiều lần để đẩy thông báo đến người dùng và sẽ bị mất tác dụng khi người dùng hủy đăng ký nhận thông báo.

Tích hợp vào project Laravel

Step 1: Đăng ký sử dụng dịch vụ Line Notify tại đây

Đây là form đăng ký sử dụng dịch vụ, các thông tin đăng ký sẽ dùng để phân biệt notification từ hệ thống của mình với các hệ thống khác.

Lưu ý:

  • Nên sử dụng email thật để nhận link xác nhận từ Line.
  • Callback url: chưa cần set khi lần đầu đăng ký.

Sau khi đăng ký xong dịch vụ thì chúng ta sẽ cần lấy hai thông tin sau.

Đưa thông tin sau trên vào file .env

LINE_NOTIFY_CLIENT_ID = '****'
LINE_NOTIFY_CLIENT_SECRET = '*****'
LINE_NOTIFY_CALLBACK = ''

Tạo file config/line.php riêng cho phần dịch vụ Line.

Đưa thông tin .env vào trong file config giúp dữ liệu được load chính xác khi sử dụng.

<?php 
return [
  'client_id' => env('LINE_NOTIFY_CLIENT_ID', 'default-value'),
  'client_secret' => env('LINE_NOTIFY_CLIENT_SECRET', 'default-value'),
  'callback' => env('LINE_NOTIFY_CALLBACK', 'default-value')
];

Callback url:

Step 2:

Tạo form để người dùng đăng ký nhận thông báo từ hệ thống của mình.

<form action= "https://notify-bot.line.me/oauth/authorize"
          method="GET" class="hidden">
    <input type = "hidden" name = "response_type" value="code">
    <input type = "hidden" name = "client_id" value="{{config("line.client_id")}}">
    <input type = "hidden" name = "redirect_uri" value="{{config("line.callback")}}">
    <input type = "hidden" name = "scope" value="notify">
    <input type = "hidden" name = "state" value="NO_STATE">
</form>

Sau khi submit form hệ thống sẽ chuyển sang trang login của dịch vụ Line và yêu cầu nguời dùng sử đồng ý sử dụng Line notify. Sau khi xử lý xong phía bên Line, hệ thống quay trở lại theo đường dẫn đã set ở callback url.

Trường hợp người dùng đồng ý, trên callback sẽ có dạng như sau:

https://callback_url?code=DTOkTpI8M4mKPtctftsjou&state=NO_STATE

Trường hợp người dùng không đồng ý, trên callback sẽ có dạng như sau:

https://callback_url?error=access_denied&error_description=user+canceled&state=NO_STATE

Sử dụng param code từ callback để lấy access_token của Line notify. Access_token sẽ được sử dụng để đẩy các thông báo đến Line app cho đến khi người dùng hủy kết nối đến Line hoặc token bị hủy đi.

Step 3: Tạo controller xử lý callback phía Line phản hồi.

Step 4: Tạo service xử lý Line Notify

<?php

namespace App\Services;

use App\Model\LineAccessToken;
use GuzzleHttp\Client;

class LineNotifyService {

    const NOTIFY_BOT_LINE = 'https://notify-bot.line.me/';
    const NOTIFY_API_LINE = 'https://notify-api.line.me/';
    protected $client;

    public function __construct() {
        $this->client = new Client();
    }

    public function getAccessToken($code) {
        $header =  [
            'Content-Type' => 'application/x-www-form-urlencoded'
        ];

        $body = [
            'grant_type' => 'authorization_code',
            'code' => $code,
            'redirect_uri' => config("line.callback"),
            'client_id' => config("line.client_id"),
            'client_secret' => config("line.client_secret")
        ];

        $response = $this->client->post(self::NOTIFY_BOT_LINE . 'oauth/token',
            ['headers' => $header, 'form_params' => $body]);

        return json_decode($response->getBody()->getContents(), true);
    }

    public function disconnectLineNotify($accessToken) {
        $header = [
            'Authorization' => 'Bearer ' . $accessToken,
            'Content-Type' => 'application/x-www-form-urlencoded'
        ];

        $response = $this->client->post(self::NOTIFY_API_LINE . 'api/revoke', ['headers' => $header]);

        return json_decode($response->getBody()->getContents(), true);
    }

    public function sendMessage($accessToken, $message) {
        $header = [
            'Authorization' => 'Bearer ' . $accessToken,
            'Content-Type' => 'application/x-www-form-urlencoded'
        ];

        $body = [
            'message' => $message
        ];

        $response = $this->client->post(self::NOTIFY_API_LINE . 'api/notify',
            ['headers' => $header, 'form_params' => $body]);

        return json_decode($response->getBody()->getContents(), true);
    }
}

getAccessToken($code)

  • Lấy access_token từ mã code nhận được từ phía Line, mã access_token sẽ được sử dụng nhiều lần, nên lưu lại trong database để sử dụng để gửi thông báo cho người dùng.
  • API tham khảo: The OAuth2 token endpoint (oauth/token)

disconnectLineNotify($accessToken)

sendMessage($accessToken, $message)