# Paywall providers

We have created a system to allow developers to implement paywall providers into Marfeel in an easy way. The way this system works is similar to the current ad server or analytics providers.

# How to create a paywall provider


  • The new Paywall providers are not compatible with legacy tenants (non-XPlib).

# Preliminary steps

# Scaffolding

In order to create a new paywall provider, we need to create a folder inside the packages folder with the name of the new provider. This one needs to have the following structure. You can always copy a folder from an already existing provider and afterward make all the necessary changes.


├─── schema
│   │   index.json
│   ├─── test
│       └──── valid
│            │   index.test.json
│       └──── wrong
│            │   index.test.json
├─── src
│   │   index.js
│   │   index.test.js
└── package.json
└── package-lock.json
└── .eslintignore

# Implementation

When implementing a paywall provider, only the logic needs to be implemented, most of the UI is already implemented in Marfeel. So, in order to properly organize each providers logic, follow this structure:

export default class NewProvider {
	constructor(json, marfeelEnvironment) {
		// set the parameters given in the config of the paywall.json

	static buildFromJson(json, marfeelEnvironment) {
		return new NewProvider(json, marfeel);

	login() {
		return Promise.resolve();

	isUserLoggedIn() {
		return Promise.resolve();

	logout() {
		return Promise.resolve();

	onNavigationChange() {
		//do something

	getActionItems() {
		return Promise.resolve([]);

In the constructor we receive 2 arguments:

  • json object: is the configuration set in the paywall.json file in the tenant's repository
  • marfeelEnvironment contains several utilities that can be used to install scripts and retrieve context information from Marfeel. They are injected into the provider to avoid exposing Marfeel source code and to keep the provider's implementation decoupled from Marfeel core. It contains a hideAds function that can be called with NONE, CLIENT, ALL, TYPE or PERCENTAGE, based on the ads that we want to hide. Find below some use cases:
//Ads running normally

//All ads disabled

//Client ads disabled, only Marfeel ads are showing

//Marfeel ads disabled, only client ads are showing

//Taboola and Teads ads type are disabled (ad type as you find in inventory.json)
marfeelEnvironment.paywall.hideAds('TYPE', ['taboola', 'teads'])

//75% of the ads (client and Marfeel) are disabled. null value is required for second parameter as the percentage is not affected by ad types.
marfeelEnvironment.paywall.hideAds('PERCENTAGE', null, 0.75)

The methods login, isUserLoggedIn, logout, onNavigationChange and getActionItems are mandatory. If any of them are missing, acid tests fail.

# Paywall action menu

In order to have the paywall actions like login, logout, subscribe, etc. closer to the user we have created a separate menu that is placed on the top right corner of the header. This menu is already implemented within Marfeel through a class called PaywallActionsBuilder (opens new window) in MarfeelXP. The only thing this class needs to know is which items should the menu have.

Define those items inside the getActionItems method as an array of json objects.

An item can have different properties:

  • text: the name of the button. [string]
  • link: if it needs to redirect the user to a specific link. [string]
  • class: a css class for the button. [string]
  • id: an id for the button. [string]
  • action: a function to be executed when clicking the item/button. [function]
  • cta: if you want it to be a Call To Action item. Those appear in the top of the sections when the user is not logged in (maximum amount: 2). [boolean, default false]

Take into consideration that you should also distinguish the items returned based on if the user is logged in or not. Here's an example of how it could look like:

if (this.isUserLoggedIn) {
  return [
      text: 'My account',
      link: 'www.example.com/myaccount'
      text: 'Logout',
      action: this.logout,
      class: 'logoutClass'
} else {
  return [
      text: 'Subscribe',
      class: 'subscribeClass',
      cta: true
      text: 'Login',
      action: this.login,
      class: 'loginClass',
      cta: true

Example of the CTA buttons:


Example of the action menu:


# Unit tests

A Paywall Provider must contain unit tests to be deployed. Providers' unit tests must be written using JEST (opens new window).

The test for the provider's implementation entry point is mandatory and can be found at src/index.test.ts, but you can create as many test files as necessary:

  • src/helpers.test.ts
  • src/utils.test.ts
  • ...

Run the test suite by executing the following command from the provider's root folder:

npm run test

If you want to debug, you can use:

npm run test:debug

To check if your provider passes the acid-tests execute:

npm run acid-tests

# JSON Schema

In addition to its implementation and unit tests, every provider must contain a JSON schema (opens new window) The schema must be at schema/index.json and describes which properties and values can be used to configure the paywall provider in paywall.json files.

Validate the provider's schema with at least one valid and one wrong test:

  • schema/tests/valid/index.test.json
    "type": "newProviderName",
        "xxx": "xxx",
	"options": {}
  • schema/tests/wrong/index.test.json
    "type": "newProviderName",
        "xxx": "xxx",

Each folder, valid and wrong, can contain as many *.test.json files as necessary.

# Deployment

  1. If the Paywall Provider has already been deployed before, make sure you've increased its version in the package.json file, following the SemVer spec (opens new window). Otherwise, the build fails, blocking deployment.
  2. Open a Pull Request and merge your commit to the master branch.
  3. The build process will start immediately after the merge. You can follow the deployment status at the Actions tab of the Paywall Provider repository.
  4. If everything goes well, the Paywall Provider package will be published in the Marfeel releases repository (opens new window) within 4 minutes approx.


If the build is broken, review the logs from GitHub and fix all the failures before trying again.