Serverseitige Integration

Die serverseitige Integration verwenden Verlage und Webpublisher hauptsächlich, um Leser und ihre Berechtigungen zu verwalten. Mit UpdateReaderEntitlements aktualisieren sie den Google-Eintrag einer Produkt-ID-Berechtigung für eine PPID.

Google Cloud-Einrichtung

Die Konfiguration der Aboverknüpfung in Google Cloud beinhaltet zwei wichtige Schritte:

  1. API für ein Projekt aktivieren
  2. Dienstkonto für den Zugriff auf die API erstellen

Subscription Linking API aktivieren

Damit ein Dienstkonto verwendet und die Berechtigungen eines Lesers verwaltet werden können, müssen in einem Google Cloud-Projekt sowohl die Subscription Linking API als auch ein ordnungsgemäß konfiguriertes OAuth-Dienstkonto aktiviert sein. Wenn du die Subscription Linking API für ein Projekt aktivieren möchtest, öffne das Menü -> „APIs und Dienste“ -> „Bibliothek“ und suche nach Subscription Linking. Du kannst die Seite aber auch direkt aufrufen:


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

API

Abbildung 1: API-Bibliothek aufrufen und die API für ein Google Cloud-Projekt aktivieren

Dienstkonto erstellen

Dienstkonten werden verwendet, um den Zugriff deiner Anwendung auf die Subscription Linking API zu ermöglichen.

  1. Erstelle ein Dienstkonto in der Konsole deines Projekts.
  2. Erstelle Anmeldedaten für das Dienstkonto und speichere die Datei credentials.json an einem sicheren Ort, auf den deine Anwendung zugreifen kann.
  3. Weise dem erstellten Dienstkonto die IAM-Rolle „Administrator von Aboverknüpfungen“ zu. Um detaillierte Kontrolle über die Funktionen des Dienstkontos zu haben, kannst du die entsprechende Rolle aus der folgenden Tabelle zuweisen.
Funktion/Rolle Administrator von Aboverknüpfungen Betrachter von Aboverknüpfungen Betrachter von Berechtigungen für Aboverknüpfungen
Berechtigungen der Leser abrufen
Leser abrufen
Berechtigungen der Leser aktualisieren
Leser löschen

Dienstkonten mit der Subscription Linking API verwenden

Dienstkonten werden verwendet, um Aufrufe der Subscription Linking API zu authentifizieren. Dazu kannst du die googleapis-Clientbibliothek verwenden oder Anfragen über die REST API signieren. Die Clientbibliotheken fordern automatisch das entsprechende access_token an. Die REST API hingegen erfordert das Abrufen eines id_token, das anschließend gegen ein access_token ausgetauscht werden muss.

Die folgende Clientbibliothek und die folgende REST API verwenden den Endpunkt getReader(). Eine Live-Demo aller API-Methoden findest du auf der Demoseite für die Aboverknüpfung oder im zugehörigen Code.

Beispielanfrage mit der googleapis-Clientbibliothek für Node.js

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}`,
  });
};

async function updateEntitlements(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}`,
    requestBody
  });
};

REST API-Anfragen manuell signieren

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
}