서버 측 통합

게시자는 주로 리더 및 사용 권한을 관리하는 데 서버 측 통합을 사용합니다. 기본적으로 게시자는 UpdateReaderEntitlements를 사용하여 PPID의 제품 ID 사용 권한에 관한 Google의 기록을 업데이트합니다.

Google Cloud 설정

Google Cloud에서 구독 연결 구성에는 두 가지 주요 구성요소가 포함됩니다.

  1. 지정된 프로젝트에 API 사용 설정
  2. API 액세스를 위한 서비스 계정 만들기

Subscription Linking API 사용 설정

서비스 계정을 사용하고 리더의 사용 권한을 관리하려면 Google Cloud 프로젝트에 Subscription Linking API가 사용 설정되어 있고 OAuth 서비스 계정이 올바르게 구성되어 있어야 합니다. 프로젝트에 Subscription Linking API를 사용 설정하려면 메뉴 -> API 및 서비스 -> 라이브러리로 이동하여 Subscription Linking를 검색하거나 페이지를 직접 방문하세요.


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

api

그림 1. API 라이브러리로 이동하여 Google Cloud 프로젝트에 API 사용 설정

서비스 계정 만들기

서비스 계정은 애플리케이션에서 Subscription Linking API로 액세스하는 데 사용됩니다.

  1. 프로젝트 콘솔에서 서비스 계정을 만듭니다.
  2. 서비스 계정의 사용자 인증 정보를 만들고 애플리케이션에서 액세스할 수 있는 안전한 위치에 credentials.json 파일을 저장합니다.
  3. 생성한 서비스 계정에 '구독 연결 관리자' IAM 역할을 부여합니다. 서비스 계정의 기능을 세부적으로 제어하려면 다음 표에서 적절한 역할을 할당하면 됩니다.
역량 / 역할 구독 연결 관리자 구독 연결 뷰어 구독 연결 사용 권한 뷰어
독자 사용 권한 가져오기
독자 확보
독자 사용 권한 업데이트
독자 삭제

Subscription Linking API로 서비스 계정 사용

서비스 계정을 사용하여 googleapis 클라이언트 라이브러리를 사용하거나 REST API로 요청에 서명하여 Subscription Linking 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
}