npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@financial-times/n-swg

v2.1.1

Published

JS, Styles, Templates and utils for FT.com Subscribe with Google implementation

Downloads

105

Readme

Subscribe with Google (SwG) CircleCI

npm version

This is a public repository.

As well as containing the source code for n-swg this repo acts as a documentation source for SwG on FT.com.

Contents

What is SwG?

Subscribe with Google or SwG (pronounced "swig") is a Google platform that allows users to subscribe (register + pay) using their Google account and payment methods.

The integral parts include:

  • Interfacing with a client side js ("swg-js") library (wrapped by n-swg).
  • Marking up our content with metadata that details its access level (next-json-ld).
  • Several APIs on both the Google side and the FT core (Membership) side as detailed here: "Subscription Linking API", "Entitlements API" & "Subscription Status API".
  • For FT.com we also have a "post-swg-purchase" form which gathers additional consent (next-profile), which we cannot gather in the SwG buy flow itself.

Benefits of SwG include:

  • Frictionless purchase
  • Frictionless sign in
  • SwG users with an FT.com subscription will see prominent placement of FT articles on Google search results
  • The flexibility to place the "buy flow" on any webpage (in article purchases etc)

Subscribe with Google is not "Sign in with Google". The ability to sign in with a Google account was a prerequisite to SwG. All SwG users can "Sign in with Google". But not all users who use "Sign in with Google" are SwG users.

Resources

n-swg client

Importing JS

This repo's module exports js for use on the client side via main-client.js this should be imported via bower, or directly via the origami build service.

bower

// bower.json
"dependencies": {
	"n-swg": "<VERSION>"
}

// example-client-side.js
import nSwg from 'n-swg';

build service

// example.html
<script>
	window._onSwgLoadCallback = function (modules) {
		var nSwg = modules['n-swg'];
		// nSwg is ready to use ...
	}
</script>
<script src="https://www.ft.com/__origami/service/build/v2/bundles/js?modules=n-swg@^<VERSION>&polyfills=true&callback=_onSwgLoadCallback"></script>

Importing styles

The only styles exported are for the "Subscribe with Google" cta button and the overlay message component. Therefore you only need to import the styles if you plan on using either of these.

To import the styles via bower

// example.scss
@import 'n-swg/main';

Via the build service

// example.html
<link rel="stylesheet" href="https://www.ft.com/__origami/service/build/v2/bundles/css?modules=n-swg@<VERSION>"/>

Templates

The only template exported is for the "Subscribe with Google" button.

Button

via bower

{{> n-swg/views/button appName="article" skus="<SKU_ID>" }}
  • appName - The name of the app that's implementing this.
  • skus - A comma separated list of what Google knows as the sku. If only one sku is passed, it will immediately open the subscribe popup, otherwise it will call the showOffers flow. These will likely be something of the form: ft.com_<OFFER-ID>_<PAYMENT-TERM>... described here.

build service (or manual markup)

Just replicate this basic html. Note that it is disabled by default (so we can check entitlements before enabling).

<button class="swg-button" disabled
	data-trackable="swg-<APP_NAME>"
	data-n-swg-button=""
	data-n-swg-button-skus="<EXAMPLE_SKU_1>,<EXAMPLE_SKU_2>">
	title="Subscribe with Google">
</button>

n-swg API

Initialising

Options Object

{
manualInitDomain: 'ft.com',
/* [optional] if provided swg will be imported in manual mode and inited
with the provided domain (it only works for ft.com at the time of writing).
If not passed the config will be pulled by swg-js from JSON-LD markup.
It is important to note that SwG will not work if it cannot find config */

sandbox: false,
/* [optional] if true imports the sandbox version of swg.js */

subscribeFromButton: true,
/* [optional] if true checkout flow will be initiated by clicking on
[data-n-swg-button] elements */

M_SWG_SUB_SUCCESS_ENDPOINT: 'www.ft.com/some-ft-com-url'
/* [optional] if provided this is the CORS endpoint n-swg will call upon a
successful swg subscription event response */
},

M_SWG_ENTITLED_SUCCESS_ENDPOINT: 'www.ft.com/some-ft-com-url'
/* [optional] if provided this is the CORS endpoint n-swg will call upon a
successful entitlements check event response */
},

POST_SUBSCRIBE_URL: 'www.ft.com/some-ft-com-url'
/* [optional] if provided this is the endpoint n-swg will call upon a
successful subscription confirmation */
},

handlers: {
	onPaymentResponse: (args) => console.log('Welcome to the FT')
}
/* [optional] An object containing custom callback functions. See the
SwgController constructor for available handlers */

Simple / Convenience method (recommended)

import { swgLoader } from 'n-swg';

swgLoader(options).then(swg => {
	swg.init();
});

// async alternative
const swg = await swgLoader(options);
swg.init();

Manual

Note that the swg client must be loaded in first from Google and relevant handlers set up before exposing the class.

import { SwgController } from 'n-swg';

// only required when initialising manually
const loadOptions = {
	manual: true // this should be true if passing options.manualInitDomain
};

SwgController.load(loadOptions).then(client => {
	const swg = new SwgController(client, options);
	swg.init();
});

SwG Client Library

Should you encounter the case where you only require the swg-js client library, you can do so by calling the following:

const loadOptions = {
	manual: true
}
nSwG.importClient(loadOptions);

Methods

swgLoader(options).then(swg => {
	// use swg methods
});

swg.init({ disableEntitlementsCheck: <BOOLEAN> })

Initialise swg. This will ensure the swg-js client is loaded and inited according to the load options provided via swgLoader.

And if disableEntitlementsCheck=false (default)

  • check for user entitlements
    • if none are found, init any swg buttons on the page
    • if entitlements are found then it will resolve the users session and either prompt them to log in or automatically log them in. If this is a new user they will be prompted to complete the /profile consent form after successful login.

If disableEntitlementsCheck=true

  • init any swg buttons on the page

swg.swgClient

This exposes the swg-js client for direct usage should you want.

JSON-LD Markup

Please see next-json-ld README for a detailed description of the markup required on our articles and barriers.

The swg library requires markup to work. So if there is no markup available (e.g a standalone page) then the client should be initialised in "manual" mode.

SwG on AMP

This is implemented in Financial-Times/google-amp and is not using this component, but rather uses the amp-subscriptions extension for AMP.

SwG on mobile apps

It is important that any user with a SwG subscription is able to access content they are entitled to. This should always be possible via "Sign in with Google", assuming they created their account on desktop.

Aside from this we do not currently actively support SwG flows on mobile. If we were to there are two main parts.

Entitlements checking

If an app user lands on a barrier then ideally we can follow the entitlement check and account resolution flows. This could not use the swg-js solution as the android app uses webview and so the sandboxed environment would render the swg-js client un-usable. It is a similar story for iOS.

Therefore if we want to pursue this we must wait for Google to document how we can do native android app entitlement checking (and iOs).

SwG purchase flows

Users who purchase via their web browser will be able to use SwG and sign in with Google.

We are not able to sell subscriptions directly on our iOS app. So it is a non issue.

On android we currently link off to the barrier page in webview, swg-js would not work in this scenario due to the sandboxed nature. So again we would need to wait for Google to document how we can do this natively on android.

SKUs

Think of SKUs as the Google equivalent of our offers.

Each FT.com offer has a unique id. e.g 713f1e28-0bc5-8261-f1e6-eebab6f7600e, within this offer there may be data about a monthly payment option (p1m) or an annual payment option (p1y), (or both). If the offer has a "trial" period there would also be data about the trial payment and term.

For our Google SKUs we base the pricing upon an existing offer. This task is completed by the pricing team, setting the price for each supported currency / region as well as the pre-tax default GBP price. These are likely exported as a spreadsheet and the SKUs will need to be manually configured via the "in-app products" section of the play store console.

It is important for tracking purposes that SKU ids follow a strict structure:

ft.com_OFFER.ID_TERM_NAME.TRIAL_DATE

[ domain, offerId, termCode, name, date ]

Each section is delimited by _ and data with that section is delimited with .

  • domain - 'ft.com', this never changes (although perhaps we will have markets.ft.com packages in the future etc).
  • offerId - the offerId with the - replaced by . e.g 713f1e28-0bc5-8261-f1e6-eebab6f7600e becomes 713f1e28.0bc5.8261.f1e6.eebab6f7600e.
  • termCode - the payment term code. p1m = "pay 1 month", p1y = "pay 1 year". other examples p3m = "pay 3 weeks" etc. Main two use cases are p1m aka monthly and p1y aka annual.
  • name - the name of the package, usually standard or premium, if you want a longer name with spaces use . e.g standard.digital.package -> "standard digital package".
    • it is important to include either standard or premium
    • if the sku has a trial attatched to it then trial should feature in the name. e.g standard.trial
  • date - the date on which the sku was set up, taking the format: YYYY.MM.DD. This helps keep the names unique and easy to reason about

Examples:

ft.com_abcd38.efg89_p1m_premium.trial_2018.05.31
ft.com_abcd38.efg89_p1y_standard_2018.05.31

We extract tracking data from the sku id. example ft.com_41218b9e.c8ae.c934.43ad.71b13fcb4465_p1m_premium.trial_DATE would have the following tracking properties extracted:

{
  offerId: '41218b9e-c8ae-c934-43ad-71b13fcb4465',
  skuId: 'ft.com_41218b9e.c8ae.c934.43ad.71b13fcb4465_p1m_premium.trial_DATE',
  productName: 'premium trial',
  term: 'trial',
  productType: 'Digital',
  isTrial: true,
  isPremium: true
}