همگام سازی حقوق کاربر (ادغام سمت سرور)

ناشران در درجه اول از ادغام سمت سرور برای مدیریت خوانندگان و حقوق آنها استفاده می کنند. در درجه اول، ناشران از UpdateReaderEntitlements برای به‌روزرسانی سابقه Google در مورد حق شناسه محصول برای یک PPID استفاده می‌کنند.

راه اندازی Google Cloud

پیکربندی پیوند اشتراک در Google Cloud شامل دو جزء اصلی است:

  1. فعال کردن API برای یک پروژه معین
  2. ایجاد یک حساب کاربری برای دسترسی به api

API پیوند اشتراک را فعال کنید

برای استفاده از یک حساب سرویس و مدیریت حقوق خواننده، یک پروژه Google Cloud باید هم API پیوند اشتراک را فعال کرده و هم یک حساب سرویس OAuth به درستی پیکربندی شده باشد. برای فعال کردن API پیوند اشتراک برای یک پروژه، از منو -> APIs & Services -> Library پیمایش کنید و Subscription Linking را جستجو کنید، یا مستقیماً از صفحه بازدید کنید:


https://console.cloud.google.com/apis/library?project=gcp_project_id

api

شکل 1. پیمایش به کتابخانه API و فعال کردن API برای پروژه Google Cloud.

یک حساب خدمات ایجاد کنید

حساب‌های سرویس برای اجازه دسترسی از برنامه شما به API پیوند اشتراک استفاده می‌شوند.

  1. یک حساب سرویس در کنسول پروژه خود ایجاد کنید .
  2. اعتبارنامه ها را برای حساب سرویس ایجاد کنید و فایل credentials.json را در مکانی امن و قابل دسترسی برای برنامه خود ذخیره کنید.
  3. نقش IAM "Subscription Linking Admin" را به حساب سرویسی که ایجاد کردید اعطا کنید . برای کنترل دقیق بر روی قابلیت های اکانت سرویس، می توانید نقش مناسب را از جدول زیر اختصاص دهید.
قابلیت / نقش مدیریت پیوند اشتراک مشاهده کننده پیوند اشتراک مشاهده کننده حقوق پیوند اشتراک
حقوق خواننده را دریافت کنید
خوانندگان را دریافت کنید
به روز رسانی حقوق خواننده
خوانندگان را حذف کنید

از حساب‌های سرویس با 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
  });
};

REST 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_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;
}