Giới thiệu về Zalo API
Zalo là ứng dụng nhắn tin và gọi điện miễn phí hoạt động trên nền tảng di động và máy tính.
Zalo được phát triển bởi đội ngũ kỹ sư của công ty VNG. Phiên bản đầu tiên được ra mắt vào ngày 08/08/2012 không nhận được sự quan tâm nhiều từ người dùng.
Zalo API được tạo ra cho phép ứng dụng của bên thứ 3 có thể tương tác với Zalo Platform và kết nối tới hơn 100 triệu user của Zalo.
Zalo API bao gồm
1. Social API
Cung cấp một cách thức để ứng dụng của bạn có thể truy cập dữ liệu trên nền tảng của Zalo. Thông qua Social API ứng dụng có thể truy vấn dữ liệu người dùng, dữ liệu bạn bè, đăng tin mới và nhiều tác vụ khác.
2. Official Account API
Official Account API cho phép doanh nghiệp quản lý tài khoản OA, quản lý người dùng quan tâm tài khoản OA và tương tác với người quan tâm OA sử dụng các mẫu thông báo được Zalo thiết lập sẵn.
3. Article API
Cung cấp một cách thức để ứng dụng của bạn có thể truy cập dữ liệu article trên nền tảng của Zalo. Thông qua Article API ứng dụng có thể tạo bài viết, chỉnh sửa, quản lý bài viết hoặc broadcast bài viết tới follower.
4. Store API (Zalo Shop)
Cung cấp một cách thức để ứng dụng của bạn có thể truy cập dữ liệu của Zalo Shop. Thông qua Store API ứng dụng có thể tạo tạo danh mục, tạo sản phẩm, quản lý sản phẩm, quản lý đơn hàng và nhiều tác vụ khác.
Tham khảo thêm tại: https://developers.zalo.me/docs
Zalo có hai loại access token, trong đó Official Account Access Token sử dụng cho các dịch vụ liên quan đến Official Account (OA, Article, Shop, ZNS API) và User Access Token dành cho tài khoản cá nhân (Social API).
Việc lấy access token cho Official Account và User là tương tự nhau, chỉ khác URL tới Zalo Service.
Dưới đây là workflow cách lấy Access token của Zalo API
Bước 1 - Lấy Authorization Code
Trước tiên sẽ ta tạo cặp mã code verifier (ngẫu nhiên) và code challenge để phục vụ cho phương thức PKCE theo yêu cầu bên Zalo, kèm theo một giá trị trạng thái ngẫu nhiên state ( do mình chỉnh định) để ngăn ngừa tấn công CSRF.
Giá trị code verifier và state sẽ còn được sử dụng thêm 01 lần nữa trong auth.php ngay sau đó, vì vậy ta sẽ lưu tạm chúng vào PHP session.
Từ index.php redirect tới Permission URL dưới đây để yêu cầu quyền truy cập Official Account.
Loại API | Permission URL |
---|---|
Official Account API Article API Shop API ZNS API |
https://oauth.zaloapp.com/v4/oa/permission |
Social API | https://oauth.zaloapp.com/v4/permission |
Tham số truyền vào query string cho URL nói trên gồm:
Parameter | Ý nghĩa |
---|---|
app_id | ID của ứng dụng |
redirect_uri | Đường dẫn được cấu hình tại phần setting của Official Account đang liên kết với Ứng dụng của bạn. Ở đây là URL đầy đủ tới auth.php. |
code_challenge | Mã code challenge đã tạo trong index.php từ code verifier phục vụ cho phương thức PKCE. Sẽ được trả về nguyên vẹn cho auth.php. |
state | Mã trạng thái state đã tạo trong index.php với mục đích ngăn tấn công CSRF. Sẽ được trả về nguyên vẹn cho auth.php. |
Ở đây giả sử ta sẽ lưu access token trong một biến PHP session là zalo_access_token
, sau đó sẽ được lưu phía client trong một biến như ví dụ dưới đây là initialToken
.
Bạn có thể lưu ở cookie hay local storage tùy theo thiết kế chương trình nhưng hãy thận trọng với XSS.
<?php
/**
* index.php
*/
define( "APP_ID", "1234567890123456789" ); // FILL your Application ID here!!!
define( "APP_AUTH_URL", "https://zalo.example.com/auth.php" ); // URL to auth.php
define( "ZALO_PERMISSION_URL", "https://oauth.zaloapp.com/v4/oa/permission" ); // OA, Article, Shop, ZNS API
//define( "ZALO_PERMISSION_URL", "https://oauth.zaloapp.com/v4/permission" ); // Social API
session_start();
///// Utility functions /////
function base64url_encode($text) {
$base64 = base64_encode($text);
$base64 = trim($base64, "=");
$base64url = strtr($base64, "+/", "-_");
return $base64url;
}
function generate_state_param() {
// a random 8 digit hex, for instance
return bin2hex(openssl_random_pseudo_bytes(4));
}
function generate_pkce_codes() {
$random = bin2hex(openssl_random_pseudo_bytes(32)); // a random 64-digit hex
$code_verifier = base64url_encode(pack('H*', $random));
$code_challenge = base64url_encode(pack('H*', hash('sha256', $code_verifier)));
return array(
"verifier" => $code_verifier,
"challenge" => $code_challenge
);
}
///// Authentication Process /////
if ( !isset($_SESSION["zalo_access_token"]) ) :
$state = generate_state_param(); // for CSRF prevention
// Generate the code verifier and code challenge
$codes = generate_pkce_codes();
// Store the request state to be checked in auth.php
$_SESSION["zalo_auth_state"] = $state;
// Store the code verifier to be used in auth.php
$_SESSION["zalo_code_verifier"] = $codes["verifier"];
$auth_uri = ZALO_PERMISSION_URL . "?" . http_build_query( array(
"app_id" => APP_ID,
"redirect_uri" => APP_AUTH_URL,
"code_challenge" => $codes["challenge"],
"state" => $state, // <- prevent CSRF
) );
header("Location: {$auth_uri}");
exit;
endif;
// Store the INITIAL access token in a JavaScript constant.
echo "<script>const initialToken = " . json_encode( $_SESSION["zalo_access_token"] ) . ";</script>\r\n";
// Remove the access token in PHP session
unset( $_SESSION["zalo_access_token"] );
/***********************************
* START THE MAIN APP *
***********************************/
Sau khi xác nhận quyền truy cập Official Account từ người dùng, Zalo sẽ redirect về auth.php, đồng thời truyền các tham số sau cho auth.php qua method GET.
Parameter | Ý nghĩa |
---|---|
oa_id | ID của Official Account đã cấp quyền cho Ứng dụng |
code | Authorization code mà ta sẽ dùng để xác thực trong bước tiếp theo. Code chỉ có hiệu lực trong vòng 10 phút. |
code_challenge | Code challenge được trả lại. |
state | Mã trạng thái được trả lại. |
Bước 2 - Lấy Access Token
auth.php gửi Authentication code và Code verifier đến Access Token URL dưới đây để yêu cầu tạo access token qua method POST:
Loại API | Access Token URL |
---|---|
Official Account API, Article API, Shop API, ZNS API | https://oauth.zaloapp.com/v4/oa/access_token |
Social API | https://oauth.zaloapp.com/v4/access_token |
Tham số trong HTTP header:
HTTP Header | Ý nghĩa |
---|---|
Content-Type | application/x-www-form-urlencoded để xác định dữ liệu trong POST request là kiểu query string. |
secret_key | Khóa bí mật của ứng dụng lấy từ trang cấu hình ứng dụng của Zalo. |
Tham số trong POST parameter:
Parameter | Ý nghĩa |
---|---|
app_id | ID của ứng dụng lấy từ trang cấu hình ứng dụng của Zalo. |
code | Authorization code mà ta đã lấy được ở bước trước đó. |
grant_type | Giá trị: authorization_code – tạo access token từ authorization code. |
code_verifier | Code verifier đã tạo trong index.php. |
Zalo platform sẽ trả về dữ liệu kiểu JSON bao gồm 3 thông tin sau:
Parameter | Ý nghĩa |
---|---|
access_token | Mã truy cập API của Zalo platform (access token). Hiệu lực trong vòng 1 giờ. |
refresh_token | Mã để cấp lại access token mới khi đáo hạn (sau 25 giờ = 90,000 giây). Hiệu lực trong vòng 3 tháng. Có thể dùng hoặc không tùy thiết kế ứng dụng. |
expires_in | Thời gian hiệu lực của access token = 90000. |
Bạn hãy lưu lại mã access_token để truy cập đến API của Zalo ví dụ ở đây ta sẽ lưu vào một HTTP only cookie là zalo_access_token
.
<?php
/**
* auth.php
*/
define( "APP_ID", "1234567890123456789" ); // FILL your Application ID here!!!
define( "APP_SECRET", "012abcXYZ345defUVW78" ); // FILL your Application Secret Key here!!!
define( "ZALO_ACCESS_TOKEN_URL", "https://oauth.zaloapp.com/v4/oa/access_token" ); // OA, Article, Shop, ZNS API
//define( "ZALO_ACCESS_TOKEN_URL", "https://oauth.zaloapp.com/v4/access_token" ); // Social API
session_start();
// CSRF prevention
$is_valid = isset($_REQUEST["state"]) && isset($_SESSION["zalo_auth_state"])
&& $_SESSION["zalo_auth_state"] == $_REQUEST["state"];
if ( $is_valid ) :
// Obtain the Access Token by performing a POST request to the Access Token URL
$data = http_build_query( array(
"app_id" => APP_ID,
"code" => $_REQUEST["code"],
"code_verifier" => $_SESSION["zalo_code_verifier"],
"grant_type" => "authorization_code"
) );
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => ZALO_ACCESS_TOKEN_URL,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => array(
"Content-Type: application/x-www-form-urlencoded",
"secret_key: " . APP_SECRET
),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_POSTFIELDS => $data,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_FAILONERROR => true,
) );
$response = curl_exec($curl);
curl_close($curl);
$auth = json_decode( $response, true );
// store the Access Token in a temporary PHP session variable
$_SESSION["zalo_access_token"] = array(
"access_token" => $auth["access_token"],
"expires_in" => $auth["expires_in"]
);
// store the Refresh Token in a secured HTTP only cookie
$expr = time() + (3*30*24*60*60); // 3 months?
setcookie( "zalo_refresh_token", $auth["refresh_token"],
$expr, "/refresh",
$_SERVER['HTTP_HOST'], true, true );
// clean up the one-time-use state variables
unset( $_SESSION["zalo_auth_state"] );
unset( $_SESSION["zalo_code_verifier"] );
// Go back to index.php
header("Location: /index.php");
exit;
endif;
// Otherwise response an error
http_response_code(400);
die('Bad Request');
Bước 3 - Tạo access token mới khi hết hạn - refesh token
Do access token có thời gian sống khoảng 25 giờ (90,000 giây) nên ứng dụng cần yêu cầu cấp phát một access token mới mà không phải thông qua nhiều bước như trên. Đây là lý do refresh token tồn tại.
Trong auth.php, refresh token đang được lưu trong một HTTP only cookie là
zalo_refresh_token
.
Khi một request bị hết hạn, Zalo sẽ trả về JSON có chứa mã lỗi (-216 / Access token không hợp lệ). Khi đó, từ phía client có thể request trang refresh.php
dưới đây để lấy access token mới và lưu trữ lại.
<?php
/**
* refresh.php
*/
define( "APP_ID", "1234567890123456789" ); // FILL your Application ID here!!!
define( "APP_SECRET", "012abcXYZ345defUVW78" ); // FILL your Application Secret Key here!!!
define( "ZALO_ACCESS_TOKEN_URL", "https://oauth.zaloapp.com/v4/oa/access_token" ); // OA, Article, Shop, ZNS API
//define( "ZALO_ACCESS_TOKEN_URL", "https://oauth.zaloapp.com/v4/access_token" ); // Social API
if ( isset($_COOKIE["zalo_refresh_token"]) ) :
// Obtain the Access Token by performing a POST request to the Access Token URL
$data = http_build_query( array(
"app_id" => APP_ID,
"refresh_token" => $_COOKIE["zalo_refresh_token"],
"grant_type" => "refresh_token"
) );
$curl = curl_init();
curl_setopt_array($curl, array(
CURLOPT_URL => ZALO_ACCESS_TOKEN_URL,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_HTTPHEADER => array(
"Content-Type: application/x-www-form-urlencoded",
"secret_key: " . APP_SECRET
),
CURLOPT_RETURNTRANSFER => true,
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_POSTFIELDS => $data,
CURLOPT_SSL_VERIFYPEER => true,
CURLOPT_FAILONERROR => true,
) );
$response = curl_exec($curl);
curl_close($curl);
$auth = json_decode( $response, true );
// store the new Refresh Token in a secured HTTP only cookie
$expr = time() + (3*30*24*60*60); // 3 months?
setcookie( "zalo_refresh_token", $auth["refresh_token"], $expr, "/refresh",
$_SERVER['HTTP_HOST'], true, true );
$result = array(
"error" => 0,
"message" => "Success",
"access_token" => $auth["access_token"],
"expires_in" => $auth["expires_in"],
);
else :
$result = array(
"error" => -1,
"message" => "Refresh token not found",
);
endif;
header( "Content-type: application/json; charset=utf-8" );
echo json_encode( $result );
?>
Nguồn icreativ.pro - Nguyễn Hồng Hải