ইউজার এনটাইটেলমেন্ট সিঙ্ক্রোনাইজ করুন (সার্ভার-সাইড ইন্টিগ্রেশন)

প্রকাশকরা প্রাথমিকভাবে পাঠক এবং তাদের এনটাইটেলমেন্ট পরিচালনার জন্য সার্ভার-সাইড ইন্টিগ্রেশন ব্যবহার করে। প্রাথমিকভাবে, প্রকাশকরা PPID-এর জন্য Google-এর প্রোডাক্ট আইডি এনটাইটেলমেন্টের রেকর্ড আপডেট করতে UpdateReaderEntitlements ব্যবহার করে।

গুগল ক্লাউড সেটআপ

Google ক্লাউডে সাবস্ক্রিপশন লিঙ্কিং কনফিগার করা দুটি প্রধান উপাদান অন্তর্ভুক্ত করে:

  1. একটি প্রদত্ত প্রকল্পের জন্য API সক্রিয় করা হচ্ছে
  2. এপিআই অ্যাক্সেস করার জন্য একটি পরিষেবা অ্যাকাউন্ট তৈরি করা

সদস্যতা লিঙ্ক API সক্ষম করুন

একটি পরিষেবা অ্যাকাউন্ট ব্যবহার করতে এবং পাঠকের এনটাইটেলমেন্টগুলি পরিচালনা করতে, একটি Google ক্লাউড প্রকল্পে সাবস্ক্রিপশন লিঙ্কিং API উভয়ই সক্রিয় থাকতে হবে এবং একটি সঠিকভাবে কনফিগার করা OAuth পরিষেবা অ্যাকাউন্ট থাকতে হবে৷ একটি প্রকল্পের জন্য সাবস্ক্রিপশন লিঙ্কিং API সক্ষম করতে, মেনু থেকে নেভিগেট করুন -> APIs এবং পরিষেবাগুলি -> লাইব্রেরি এবং Subscription Linking অনুসন্ধান করুন বা সরাসরি পৃষ্ঠাটি দেখুন:


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

api

চিত্র 1. API লাইব্রেরিতে নেভিগেট করা, এবং Google ক্লাউড প্রকল্পের জন্য API সক্রিয় করা।

একটি পরিষেবা অ্যাকাউন্ট তৈরি করুন

পরিষেবা অ্যাকাউন্টগুলি আপনার অ্যাপ্লিকেশন থেকে সাবস্ক্রিপশন লিঙ্কিং API এ অ্যাক্সেসের অনুমতি দেওয়ার জন্য ব্যবহার করা হয়।

  1. আপনার প্রকল্পের কনসোলের মধ্যে একটি পরিষেবা অ্যাকাউন্ট তৈরি করুন
  2. পরিষেবা অ্যাকাউন্টের জন্য শংসাপত্র তৈরি করুন এবং credentials.json ফাইলটি আপনার অ্যাপ্লিকেশনে অ্যাক্সেসযোগ্য একটি নিরাপদ স্থানে সংরক্ষণ করুন৷
  3. আপনার তৈরি করা পরিষেবা অ্যাকাউন্টে IAM ভূমিকা "সাবস্ক্রিপশন লিঙ্কিং অ্যাডমিন" প্রদান করুন । পরিষেবা অ্যাকাউন্টের ক্ষমতার উপর দানাদার নিয়ন্ত্রণের জন্য, আপনি নিম্নলিখিত টেবিল থেকে উপযুক্ত ভূমিকা নির্ধারণ করতে পারেন।
সামর্থ্য/ ভূমিকা সাবস্ক্রিপশন লিঙ্কিং অ্যাডমিন সাবস্ক্রিপশন লিঙ্কিং ভিউয়ার সাবস্ক্রিপশন লিঙ্কিং এনটাইটেলমেন্ট ভিউয়ার
পাঠক এনটাইটেলমেন্ট পান
পাঠক পান
পাঠকের এনটাইটেলমেন্ট আপডেট করুন
পাঠকদের মুছুন

সাবস্ক্রিপশন লিঙ্কিং API সহ পরিষেবা অ্যাকাউন্টগুলি ব্যবহার করুন৷

পরিষেবা অ্যাকাউন্টগুলির সাথে সাবস্ক্রিপশন লিঙ্কিং API-তে কলগুলিকে প্রমাণীকরণ করতে, হয় googleapis ক্লায়েন্ট লাইব্রেরি ক্লায়েন্ট লাইব্রেরি ব্যবহার করুন (যা স্বয়ংক্রিয়ভাবে access_token অনুরোধগুলি পরিচালনা করে) অথবা REST API-এর সাথে সরাসরি অনুরোধগুলি স্বাক্ষর করুন৷ REST API ব্যবহার করলে, আপনাকে প্রথমে একটি access_token ( Google Auth লাইব্রেরির মাধ্যমে বা একটি পরিষেবা অ্যাকাউন্ট JWT ব্যবহার করে) পেতে হবে এবং তারপর এটি Authorization শিরোনামে অন্তর্ভুক্ত করতে হবে

নিম্নলিখিত ক্লায়েন্ট লাইব্রেরি এবং REST API উভয় উদাহরণেই getReader() , getReaderEntitlements() , updateReaderEntitlements() এবং deleteReader() কল করার জন্য নমুনা কোড রয়েছে।

ক্লায়েন্ট লাইব্রেরি

এই বিভাগটি ব্যাখ্যা করে কিভাবে Node.js-এ googleapis ক্লায়েন্ট লাইব্রেরি ব্যবহার করতে হয়।

নমুনা অনুরোধ

Auth.GoogleAuth কনস্ট্রাক্টরের keyFile ক্ষেত্রের জন্য, আপনার পরিষেবা অ্যাকাউন্ট কী-এর পথ সেট করুন। আপনি যদি আপনার প্রতিষ্ঠানের নীতির কারণে একটি পরিষেবা অ্যাকাউন্ট কী রপ্তানি করতে না পারেন, আপনি অ্যাকাউন্ট ডিফল্ট শংসাপত্র (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 এন্ডপয়েন্টে কল করতে চান, তাহলে আপনি Authorization হেডারে সেট করার জন্য accessToken পাওয়ার জন্য যেকোনো একটি পদ্ধতি ব্যবহার করতে পারেন।

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. একটি পরিষেবা অ্যাকাউন্ট JWT ব্যবহার করে একটি access_token তৈরি করুন

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

Google Auth লাইব্রেরির সাথে REST API কলের নমুনা কোড

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