Publishers primarily use server-side integration for managing readers and their
entitlements. Primarily, publishers use UpdateReaderEntitlements
to update
Google's record of a Product ID entitlement for a PPID.
Google Cloud setup
Configuring Subscription Linking in Google Cloud includes two major components:
- Enabling the API for a given project
- Creating a service account for accessing the api
Enable the Subscription Linking API
To use a service account and manage a reader's entitlements, a Google Cloud
project must have both the Subscription Linking API enabled, and a properly
configured OAuth service account. To enable the Subscription Linking API for a
project, navigate from the menu -> APIs & Services -> Library and search for
Subscription Linking
, or visit the page directly:
https://console.cloud.google.com/apis/library?project=gcp_project_id
Figure 1. Navigating to the API Library, and enabling the API for a Google Cloud project.
Create a Service Account
Service accounts are used to allow access from your application to the Subscription Linking API.
- Create a service account within your project's console.
- Create credentials for the service account, and store
the
credentials.json
file in a secure location accessible to your application. - Grant the IAM role "Subscription Linking Admin" to the service account you created. For granular control over the capabilities of the service account, you can assign the appropriate role from the following table.
Capability / Role | Subscription Linking Admin | Subscription Linking Viewer | Subscription Linking Entitlements Viewer |
---|---|---|---|
Get reader entitlements | |||
Get readers | |||
Update reader entitlements | |||
Delete readers |
Use service accounts with the Subscription Linking API
Use service accounts to authenticate calls to the Subscription Linking API,
either with the googleapis client library or by signing requests
with the REST API. Client libraries automatically handle requesting the
appropriate access_token
, while the REST API requires retrieving an id_token
and then exchanging it for an access_token
.
Both the following client
library and REST API examples use the getReader()
endpoint. For a live
demonstration of all of the API methods, see the
Subscription Linking Demo site, or its code.
Sample request with the node.js googleapis client library
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}/entitlements`,
requestBody
});
};
Manually signing REST API requests
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
}