Skip to content

Latest commit

 

History

History
166 lines (144 loc) · 7.22 KB

entitlements-flow.md

File metadata and controls

166 lines (144 loc) · 7.22 KB

SwG Entitlements Flow

This flow would notify the publication site when SwG believes that the reader is entitled to read the content, e.g. due to previously purchased subscription. See Subscriptions APIs.

It's recommended that the site install entitlements listener as early as practical using setOnEntitlementsResponse method.

For instance:

subscriptions.setOnEntitlementsResponse(function(entitlementsPromise) {
  entitlementsPromise.then(function(entitlements) {
    // Handle the entitlements.
  });
})

This callback will be called whenever the new entitlements have been updated.

In the auto mode, the entitlements are fetched automatically. In the manual mode the site can call start method:

subscriptions.start();

Likewise, at any point, the site can call getEntitlements method to access the entitlements, whether they were previously fetched or not:

subscriptions.getEntitlements().then(function(entitlements) {
  // Handle the entitlements.
});

Entitlement response

Name Type Description
source String
  • When provided by google: "google"
  • When provided by the publisher: the publicationID
products Array of strings Subscribe with Google Product IDs the user can access.
subscriptionToken String
  • When provided by Google this is a quoted string that represents an IN_APP_PURCHASE_DATA JSON object
  • When provided by the publisher: this is an opaque string the publisher provided to Google during the account linking process.The publisher should use this string to lookup the subscription on their backend
  • If you're going to provide JSON in your subscriptionToken, be sure to escape it properly (example below)

An example response:

{
  service: "subscribe.google.com",
  entitlements: [
    {
      source: "google",
      products: ["example.com:entitlement_label1"],
      subscriptionToken: "{purchaseData - see above}",
      detail: "sku_description"
    },
    {
      source: "example.com",
      products: ["example.com:entitlement_label2"],
      subscriptionToken: "subscription token from example.com",
      detail: "sku_description"
    }
  ],
  isReadyToPay: false
}

See the Entitlements object for more detail.

Entitlement acknowledgement

The successful entitlements object should be acknowledged by the publication site to stop it from showing the notification. This is done by calling the entitlements.ack() method.

For instance:

subscriptions.setOnEntitlementsResponse(function(entitlementsPromise) {
  entitlementsPromise.then(function(entitlements) {
    // Handle the entitlements.
    entitlements.ack();
  });
})

Validate entitlements

To verify the entitlements from Google are valid and authorised, check the validity of the JWT representation of the entitlements.

The following registered claims are used:

Claim Value Description
exp NumericDate (e.g. 1543596936 ) When the token expires
iat NumericDate (e.g. 1543595136 ) When the token was issued
iss subscribewithgoogle@system.gserviceaccount.com Issuer of the token
aud URL (e.g. https://example.com ) Audience of the token which will be the publisher's origin

To validate the signature, use these X.509 certificates or these JWKs.

Note: The keys rotate, so make sure to check against each one

Sample code

This example is a skeleton for the following:

  1. Checking if the user has entitlements to the product linked to the content,
  2. Checking if the user's subscription is recognized by the publisher,
  3. Using the login flow functions if so,
  4. Initiating the Deferred Account Creation Flow if not,
  5. Remove the "Subscribed with ... [publication] [Manage Link]" bottom toast.
subscriptions.setOnEntitlementsResponse(entitlementsPromise => {
  entitlementsPromise.then(entitlements => {
    // Handle the entitlements.
    if (entitlements.enablesThis()) {
      // Entitlements grant access to the product linked to this content.
       // Look up the user. Resolve the promise with an account (if it was found).
      const accountPromise = new Promise(...);
       // Notify the user that their account is being looked up. It will resolve
      // when accountPromise resolves.
      subscriptions.waitForSubscriptionLookup(accountPromise).then(account => {
        if (account) {
          // Account was found.
          // Option 1 - notify the user that they're being automatically signed-in.
          subscriptions.showLoginNotification().then(() => {
            // Publisher shows content.
          });
           // Option 2 - prompt the user to sign in.
          subscriptions.showLoginPrompt().then(() => {
            // User clicked 'Yes'.
            // Notify the user that they're being logged in with Google.
            subscriptions.showLoginNotification().then(() => {
              // Publisher shows content.
            });
          }, reason => {
            // User clicked 'No'. Publisher can decide how to handle this
            // situation.
            handleCancellation();
          });
        } else {
          // Account was not found, or existing account has no subscription.
          // Let's create a new one or link to the existing publisher account.
          subscriptions.completeDeferredAccountCreation({
            entitlements: entitlements,
            consent: true
          }).then(response => {
            // 1. The user has consented to account creation. Create account
            // based on the response.
             // 2. Signal that the account creation is complete.
            response.complete().then(() => {
              // 3. The flow is complete.
            });
          });
        }
      });
       // Remove the "Subscribed with... Manage" bottom toast.
      entitlements.ack();
    } else {
      // No access; Your logic here: i.e. meter, show a paywall, etc.
    }
  });
});