伺服器端整合

發布者主要使用伺服器端整合功能管理讀者及其授權。主要而言,發布商會使用 UpdateReaderEntitlements 更新 Google 的 PPID 產品 ID 授權記錄。

Google Cloud 設定

在 Google Cloud 中設定訂閱連結的程序包含兩個主要元件:

  1. 為指定專案啟用 API
  2. 建立用來存取 API 的服務帳戶

啟用 Subscription Links API

如要使用服務帳戶並管理讀者的授權,Google Cloud 專案必須同時啟用 Subscription Links API,以及設定正確無誤的 OAuth 服務帳戶。如要為專案啟用 Subscription Links API,請依序前往「選單」->「API 和服務」->「程式庫」並搜尋 Subscription Linking,或是直接造訪頁面:


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

api

圖 1 前往 API 程式庫,並為 Google Cloud 專案啟用 API。

建立服務帳戶

服務帳戶可用來允許應用程式存取 Subscription Links API。

  1. 在專案的控制台中建立服務帳戶
  2. 為服務帳戶建立憑證,並將 credentials.json 檔案儲存在應用程式可存取的安全位置。
  3. 為您建立的服務帳戶授予 IAM 角色「訂閱連結管理員」。如要精細控管服務帳戶的功能,請從下表中指派適當的角色。
功能 / 職務 訂閱連結管理員 訂閱連結檢視者 訂閱連結授權檢視者
取得讀者授權
吸引讀者
更新讀者授權
刪除讀者

搭配 Subscription Links API 使用服務帳戶

使用服務帳戶驗證對 Subscription Links API 的呼叫,您可以透過 googleapis 用戶端程式庫或透過 REST API 簽署要求。用戶端程式庫會自動處理要求適當的 access_token,而 REST API 會需要擷取 id_token,然後針對 access_token 交換。

以下用戶端程式庫和 REST API 範例都使用 getReader() 端點。如需所有 API 方法的即時示範,請參閱訂閱連結示範網站或程式碼

具備 node.js googleapis 用戶端程式庫的要求範例

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 api = new SubscriptionLinking()
const client = api.init()

async function getReader (ppid) {
  const publicationId = process.env.PUBLICATION_ID
  return await client.publications.readers.get({
    name: `publications/${publicationId}/readers/${ppid}`,
  })
}

手動簽署 REST API 要求

import fetch from 'node-fetch'
import jwt from 'jsonwebtoken'

function getSignedJwt() {
  /*
    Either store the 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 request = await fetch('https://oauth2.googleapis.com/token', {
    method: 'POST',
    headers: {'Content-Type': 'application/x-www-form-urlencoded'},
    body
  })

  const accessResponse = await accessFetch.json()
  return accessResponse.access_token
}

async function getReader(ppid) {
  const publicationId = process.env.PUBLICATION_ID
  const base_url = 'https://readerrevenuesubscriptionlinking.googleapis.com/v1'
  const endpoint = `${base_url}/publications/${publicationId}/readers/${ppid}`
  const signedJwt = await getSignedJwt()
  const accessToken = await getAccessToken(signedJwt)

  const reader = await fetch(endpoint, {
     method: 'GET',
     headers: {
       Authorization: `Bearer ${accessToken}`,
     },
   }).then((response) => {
    return response.json()
  })

  return reader
}