Nhà xuất bản chủ yếu sử dụng tính năng tích hợp phía máy chủ để quản lý độc giả và quyền của họ. Chủ yếu, nhà xuất bản sử dụng UpdateReaderEntitlements
để cập nhật bản ghi của Google về quyền sử dụng Mã sản phẩm cho một PPID.
Thiết lập Google Cloud
Việc định cấu hình tính năng Liên kết gói thuê bao trong Google Cloud bao gồm hai thành phần chính:
- Bật API cho một dự án cụ thể
- Tạo tài khoản dịch vụ để truy cập vào API
Bật Subscription Linking API
Để sử dụng tài khoản dịch vụ và quản lý quyền của người đọc, dự án Google Cloud phải bật cả API Liên kết gói thuê bao và tài khoản dịch vụ OAuth được định cấu hình đúng cách. Để bật Subscription Linking API cho một dự án, hãy chuyển từ trình đơn -> API và Dịch vụ -> Thư viện rồi tìm Subscription Linking
hoặc truy cập trực tiếp vào trang:
https://console.cloud.google.com/apis/library?project=gcp_project_id
Hình 1. Chuyển đến Thư viện API và bật API cho một dự án Google Cloud.
Tạo tài khoản dịch vụ
Tài khoản dịch vụ được dùng để cho phép ứng dụng của bạn truy cập vào API Liên kết gói thuê bao.
- Tạo tài khoản dịch vụ trong bảng điều khiển của dự án.
- Tạo thông tin xác thực cho tài khoản dịch vụ và lưu trữ tệp
credentials.json
ở một vị trí an toàn mà ứng dụng của bạn có thể truy cập. - Cấp vai trò IAM "Quản trị viên liên kết gói thuê bao" cho tài khoản dịch vụ mà bạn đã tạo. Để kiểm soát chi tiết các chức năng của tài khoản dịch vụ, bạn có thể chỉ định vai trò thích hợp trong bảng sau.
Chức năng / Vai trò | Quản trị viên liên kết gói thuê bao | Trình xem liên kết gói thuê bao | Trình xem quyền trong tính năng Liên kết gói thuê bao |
---|---|---|---|
Nhận quyền của người đọc | |||
Thu hút người đọc | |||
Cập nhật quyền của người đọc | |||
Xoá trình đọc |
Sử dụng tài khoản dịch vụ với API Liên kết gói thuê bao
Để xác thực các lệnh gọi đến Subscription Linking API bằng tài khoản dịch vụ, hãy sử dụng thư viện ứng dụng googleapis (tự động xử lý các yêu cầu access_token
) hoặc ký trực tiếp các yêu cầu bằng API REST. Nếu sử dụng API REST, trước tiên, bạn phải lấy access_token
(thông qua thư viện Xác thực của Google hoặc sử dụng JWT tài khoản dịch vụ) rồi đưa vào tiêu đề Authorization
Cả ví dụ về thư viện ứng dụng và API REST sau đây đều có mã mẫu về cách gọi getReader()
, getReaderEntitlements()
, updateReaderEntitlements()
và deleteReader()
.
Thư viện ứng dụng
Phần này giải thích cách sử dụng thư viện ứng dụng googleapis trong Node.js.
Yêu cầu mẫu
Đối với trường keyFile
trong hàm khởi tạo Auth.GoogleAuth
, hãy đặt đường dẫn đến khoá tài khoản dịch vụ.
Nếu không thể xuất khoá tài khoản dịch vụ do chính sách của tổ chức, bạn có thể sử dụng phương thức thông tin xác thực mặc định của tài khoản (ADC). Nếu sử dụng phương thức ADC, bạn không cần cung cấp trường keyFile
vì ADC sẽ tự tìm thông tin xác thực.
import {readerrevenuesubscriptionlinking_v1, Auth} from 'googleapis';
const subscriptionLinking = readerrevenuesubscriptionlinking_v1.Readerrevenuesubscriptionlinking;
class SubscriptionLinking {
constructor() {
this.auth = new Auth.GoogleAuth({
keyFile: process.env.KEY_FILE,
scopes: [
'https://www.googleapis.com/auth/readerrevenue.subscriptionlinking.manage'
],
})
}
init() {
return new subscriptionLinking(
{version: 'v1', auth: this.auth})
}
}
const subscriptionLinkingApi = new SubscriptionLinking();
const client = subscriptionLinkingApi.init();
/**
* Retrieves details for a specific reader associated with the publication.
* @async
* @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
* @return {Promise<object>} A promise that resolves with the reader's details
* from the API.
*/
async function getReader(ppid) {
const publicationId = process.env.PUBLICATION_ID;
return await client.publications.readers.get({
name: `publications/${publicationId}/readers/${ppid}`,
});
};
/**
* Updates the entitlements for a specific reader.
* @async
* @param {string} ppid - The Publisher Provided ID (ppid) for the reader whose
* entitlements are being updated.
* @return {Promise<object>} A promise that resolves with the result of the
* updated entitlements object.
*/
async function updateReaderEntitlements(ppid) {
const publicationId = process.env.PUBLICATION_ID;
const requestBody = {
/*
Refer to
https://developers.google.com/news/subscribe/subscription-linking/appendix/glossary#entitlements_object
*/
entitlements : [{
product_id: `${publicationId}:basic`,
subscription_token: 'abc1234',
detail: 'This is our basic plan',
expire_time: '2025-10-21T03:05:08.200564Z'
}]
};
return await client.publications.readers.updateEntitlements({
name: `publications/${publicationId}/readers/${ppid}/entitlements`,
requestBody
});
};
/**
* Retrieves the current entitlements for a specific reader.
* @async
* @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
* @return {Promise<object>} A promise that resolves with the reader's entitlements object.
*/
async function getReaderEntitlements(ppid) {
const publicationId = process.env.PUBLICATION_ID;
return await client.publications.readers.getEntitlements({
name: `publications/${publicationId}/readers/${ppid}/entitlements`
});
};
/**
* Deletes a specific Subscription Linkikng reader record associated with the publication.
* @async
* @param {string} ppid - The Publisher Provided ID (ppid) for the reader to be deleted.
* @param {boolean=} forceDelete - If true, delete the user even if their
* entitelements are not empty
* @return {Promise<object>} A promise that resolves upon successful deletion
* with an empty object ({})
*/
async function deleteReader(ppid, forceDelete = false) {
const publicationId = process.env.PUBLICATION_ID;
return await client.publications.readers.delete({
name: `publications/${publicationId}/readers/${ppid}`
force: forceDelete
});
};
API REST
Nếu muốn gọi các điểm cuối API REST, bạn có thể sử dụng một trong hai phương thức để lấy accessToken
nhằm đặt thành tiêu đề Authorization
.
1. Sử dụng thư viện GoogleAuth
Đối với khoá credentials
, bạn có thể sử dụng khoá tài khoản dịch vụ hoặc thông tin xác thực mặc định của tài khoản (ADC).
Nếu sử dụng phương thức ADC, bạn không cần cung cấp trường credentials
vì ADC sẽ tự tìm thông tin xác thực.
import { GoogleAuth } from 'google-auth-library';
import credentialJson from 'path_to_your_json_file' with { type: 'json' };
const auth = new GoogleAuth({
credentials: credential_json,
scopes: [
'https://www.googleapis.com/auth/readerrevenue.subscriptionlinking.manage'
]
});
async function getAccessToken() {
const accessToken = await auth.getAccessToken();
return accessToken;
}
2. Tạo access_token
bằng JWT Tài khoản dịch vụ
import fetch from 'node-fetch';
import jwt from 'jsonwebtoken';
function getSignedJwt() {
/*
Either store the service account credentials string in an environmental variable
Or implement logic to fetch it.
*/
const key_file = process.env.CREDENTIALS_STRING
const issueDate = new Date();
const expireMinutes = 60;
const offsetInSeconds = issueDate.getTimezoneOffset() * 60000;
const expireDate = new Date(issueDate.getTime() + (expireMinutes * 60000));
const iat = Math.floor((issueDate.getTime() + offsetInSeconds) / 1000);
const exp = Math.floor((expireDate.getTime() + offsetInSeconds) / 1000);
const token = {
iss: key_file.client_email,
iat,
exp,
aud: 'https://oauth2.googleapis.com/token',
scope:'https://www.googleapis.com/auth/readerrevenue.subscriptionlinking.manage',
}
return jwt.sign(token, key_file.private_key, {
algorithm: 'RS256',
keyid: key_file.private_key_id,
})
}
async function getAccessToken(signedJwt) {
let body = new URLSearchParams();
body.set('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer');
body.set('assertion', signedJwt);
const response = await fetch('https://oauth2.googleapis.com/token', {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body
})
const accessResponse = await response.json();
return accessResponse.access_token;
}
Mã mẫu cho các lệnh gọi API REST bằng thư viện Google Auth
import { GoogleAuth } from 'google-auth-library';
import fetch from 'node-fetch'
import credentialJson from 'path_to_your_json_file' with { type: 'json' };
const BASE_SUBSCRIPTION_LINIING_API_URL='https://readerrevenuesubscriptionlinking.googleapis.com/v1';
const publicationId = process.env.PUBLICATION_ID
const auth = new GoogleAuth({
credentials: credentialJson,
scopes: [
'https://www.googleapis.com/auth/readerrevenue.subscriptionlinking.manage'
]
});
async function getAccessToken() {
const accessToken = await auth.getAccessToken();
return accessToken;
}
/**
* Retrieves details for a specific reader associated with the publication.
* @async
* @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
* @return {object} reader json for the given ppid
*/
async function getReader(ppid) {
const endpoint = `${BASE_SUBSCRIPTION_LINIING_API_URL}/publications/${publicationId}/readers/${ppid}`;
const accessToken = await getAccessToken();
const response = await fetch(endpoint, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const reader = await response.json();
return reader;
}
/**
* Updates the entitlements for a specific reader.
* @async
* @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
* @return {object} the updated entitlements object in json.
*/
async function updateReaderEntitlements(ppid) {
const endpoint = `${BASE_SUBSCRIPTION_LINIING_API_URL}/publications/${publicationId}/readers/${ppid}/entitlements`;
const requestBody = {
/*
Refer to
https://developers.google.com/news/subscribe/subscription-linking/appendix/glossary#entitlements_object
*/
entitlements : [{
product_id: `${publicationId}:basic`,
subscription_token: 'abc1234',
detail: 'This is our basic plan',
expire_time: '2025-10-21T03:05:08.200564Z'
}]
};
const response = await fetch(endpoint, {
method: 'PATCH',
headers: {
Authorization: `Bearer ${accessToken}`,
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody)
})
const updatedEntitlements = await response.json();
return updatedEntitlements;
}
/**
* Retrieves the current entitlements for a specific reader.
* @async
* @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
* @return {object} the reader's entitlements object in json.
*/
async function getReaderEntitlements(ppid) {
const endpoint = `${BASE_SUBSCRIPTION_LINIING_API_URL}/publications/${publicationId}/readers/${ppid}/entitlements`;
const accessToken = await getAccessToken();
const response = await fetch(endpoint, {
method: 'GET',
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const entitlements = await response.json();
return entitlements;
}
/**
* Deletes a specific Subscription Linkikng reader record associated with the publication.
* @async
* @param {string} ppid - The Publisher Provided ID (ppid) for the reader.
* @param {boolean=} forceDelete - If true, delete the user even if their
* entitelements are not empty
* @return {object} returns an empty object ({}) if the delete operation is successful
*/
async function deleteReader(ppid, forceDelete = false) {
const endpoint = `${BASE_SUBSCRIPTION_LINIING_API_URL}/publications/${publicationId}/readers/${ppid}?force=${forceDelete}`;
const response = await fetch(endpoint, {
method: 'DELETE',
headers: {
Authorization: `Bearer ${accessToken}`,
}
});
const result = await response.json();
return result;
}