Издатели в основном используют интеграцию на стороне сервера для управления читателями и их правами. В первую очередь издатели используют UpdateReaderEntitlements для обновления записи Google о разрешении идентификатора продукта для PPID.
Настройка Google Cloud
Настройка привязки подписки в Google Cloud включает два основных компонента:
- Включение API для данного проекта
- Создание сервисной учетной записи для доступа к API
Включите API привязки подписки
 Чтобы использовать учетную запись службы и управлять правами читателя, в проекте Google Cloud должен быть включен API привязки подписки и правильно настроенная учетная запись службы OAuth. Чтобы включить API привязки подписки для проекта, перейдите в меню -> API и службы -> Библиотека и найдите Subscription Linking или посетите страницу напрямую:
https://console.cloud.google.com/apis/library?project=gcp_project_id

Рисунок 1. Переход к библиотеке API и включение API для проекта Google Cloud.
Создать учетную запись службы
Учетные записи служб используются для разрешения доступа вашего приложения к API привязки подписки.
- Создайте учетную запись службы в консоли вашего проекта.
-  Создайте учетные данные для учетной записи службы и сохраните файл credentials.jsonв безопасном месте, доступном для вашего приложения.
- Предоставьте роль IAM «Администратор привязки подписки» созданному вами сервисному аккаунту. Для детального контроля над возможностями учетной записи службы вы можете назначить соответствующую роль из следующей таблицы.
| Возможности/Роль | Администратор привязки подписки | Средство просмотра привязки подписки | Средство просмотра прав на привязку подписки | 
|---|---|---|---|
| Получите права читателя | |||
| Получить читателей | |||
| Обновить права читателя | |||
| Удалить читателей | 
Используйте сервисные учетные записи с API привязки подписки.
 Для аутентификации вызовов API связывания подписки с учетными записями служб либо используйте клиентскую библиотеку клиентской библиотеки googleapis (которая автоматически обрабатывает запросы access_token ), либо подписывайте запросы напрямую с помощью REST API. При использовании REST API необходимо сначала получить access_token (через библиотеку Google Auth или с помощью сервисной учетной записи JWT ), а затем включить его в заголовок Authorization
 В следующей клиентской библиотеке и примерах REST API есть пример кода для вызова getReader() , getReaderEntitlements() , updateReaderEntitlements() и deleteReader() .
Клиентская библиотека
В этом разделе объясняется, как использовать клиентскую библиотеку googleapis в Node.js.
Образец запроса
 Для поля keyFile в конструкторе Auth.GoogleAuth укажите путь к ключу вашего сервисного аккаунта . Если вы не можете экспортировать ключ служебной учетной записи из-за политики вашей организации, вы можете использовать метод учетных данных по умолчанию (ADC) . Если вы используете метод ADC, вам не нужно предоставлять поле keyFile , поскольку ADC будет искать учетные данные самостоятельно.
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 API, вы можете использовать любой из методов для получения accessToken для установки в заголовок Authorization .
 1. Используйте библиотеку GoogleAuth
 В качестве ключа credentials вы можете использовать ключ служебной учетной записи или учетные данные учетной записи по умолчанию (ADC) . Если вы используете метод ADC, вам не нужно предоставлять поле credentials , поскольку ADC будет искать учетные данные самостоятельно.
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. Создайте access_token , используя JWT учетной записи службы. 
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;
}
Пример кода для вызовов REST API с помощью библиотеки 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_LINKING_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_LINKING_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_LINKING_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_LINKING_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_LINKING_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;
}