Informal documentation

Issue
Please give your comments and feedback at this issue.
Eventually this should be merged into MDN. Adjust to more formal documentation.

This page explains how the latest version of web monetization, finalized in January 2022, works. There was an earlier version but it has been deprecated in favor of the one described here.

If you implemented web monetization before and your script uses document.monetization or the monetizationprogress event you're using the old version, and you should switch to this new one.

Web monetization has only one supported functionality: a continuous payment stream that is either on (site owner gets paid) or off (site owner does not get paid). Other payment functionalities such as tipping are not part of web monetization.

Site owners should add a <link> tag that contains a valid payment pointer to the pages or page elements they wish to monetize. They can listen to a monetization event that fires whenever a receipt for a payment comes in. This allows them to react to the presence or absence of a monetization stream, for instance by showing or hiding content. There are also load and error events.

<link> tag

Site owners can add <link> tags to their sites, such as:

<link rel="monetization" href="https://example.com/video/pay">

Such a <link> tag must have rel="monetization" and its href attribute must contain a valid payment pointer, as explained below.

<link> tags are typically placed in the document’s <head>, but this is not required. If only a certain portion of a page is monetized it makes sense to embed the <link> tag in that portion. For instance, this <link> tag is placed in a video element.

<video src="myvideo">
  <link rel="monetization" href="https://example.com/video/pay">
</video>

This one is placed in a section whose content should only be shown when the user pays the site.

<section id="premiumContent">
  <link rel="monetization" href="https://example.com/premiumcontent/pay">
</section>

If a Web Monetization implementation encounters such a <link> tag it downloads the file at the indicated location, which is a JSON file that contains SPSP instructions. Then the implementation automatically starts up a payment stream by sending out Interledger requests.

Connection limit

However, implementations may set a maximum on the number of payment streams allowed from a single page. Adding dozens of <link> tags is unlikely to work. Besides, they won't net you any more money. The money that arrives is divided equally among the accounts referred to in the <link> tags.

Support detection

Only browsers that support web monetization will support rel="monetization". You can use this fact to detect support for web monetization by using the relList attribute:

let link = document.querySelector('link[rel=monetization]') || document.createElement('link');
if (link.relList.supports('monetization')) {
	// web monetization supported; proceed
}

A browser supporting rel="monetization" might not open a payment stream automatically. The user may not be logged in to a payment provider, for instance, or something may go wrong with the request. Once you have ascertained that the browser supports web monetization you should listen to monetization events to see if any money is actually being transferred. We'll get back to that below.

Changing the payment pointer

You can change the payment pointer in the <link> tag. This redirects the monetization stream from one account to another.

let newJSON = 'https://other.example.com/payme';
let linkTag = document.querySelector('link[rel=monetization]');
linkTag.setAttribute('content',newJSON);

This causes the <link> tag to be reinterpreted.

Reinterpreting the <link> tag

If you change the payment pointer the Web Monetization implementation downloads the JSON again and re-establishes the payment endpoint, i.e. the account that's being paid. This occurs automatically; you don't have to do anything else.

The same happens when a new monetization <link> tag is inserted into the document, the disabled attribute is set to false, the crossorigin attribute is changed, or the rel attribute is set to monetization.

The monetization stream ends when <link> tag's disabled attribute is set to true, the rel="monetization" attribute is changed or removed, or the crossorigin is changed in a way that does not match the current context.

Events

Each <link> tag fires three events:

  1. load: the payment pointer JSON was loaded correctly
  2. error: something went wrong with loading the payment pointer JSON
  3. monetization: an Interledger receipt for a payment came in.

Of the three, monetization is the most important one, since it tells web developers that a monetization stream is actually running. error tells you a stream cannot run, but does not give you a reason. load tells you a stream may start to run in the near future.

monetization

Monetization <link> tags fire monetization events. Web developers can listen to these events and find out if they’re being monetized.

The monetization event fires whenever the payment stream returns a valid receipt, which indicates that a payment has arrived. It fires on the <link> tag and bubbles up. Since receipts may be received every second or so, this event will fire many times. It's up to the web developer to distinguish important from unimportant monetization events.

Right now the monetization event that matters most is the first one. Once it fires you know the payment stream is running, and you can react by showing premium content or taking other actions.

Take this HTML. You want to monetize the video on your page:

<video src="myvideo">
  <link rel="monetization" href="https://example.com/video/pay">
</video>

This simple script does the trick. The monetization event bubbles up, so you can listen to it on the video element itself. As soon as it fires you know a payment has come in and you show the video's controls and start playing it.

let video = document.querySelector('video');
let link = video.querySelector('link');
video.addEventListener('monetization',function(e) {
	this.controls = true;
	this.play();
},{once:true})

You could decide to hide premium content if a monetization event doesn't fire for, say, 10 seconds — this is up to you. The example script shows a few possibilities.

monetization event properties

The monetization event has the following read-only properties:

amount
An integer with the amount of money received according to the last receipt.
assetCode
The three-letter code describing the currency; e.g. USD, EUR.
assetScale
The scale of the amount.
receipt
The base-64 encoded Interledger Stream receipt the browser received.
paymentPointer
The payment pointer.

The first three attributes contain the amount of money the page just received. The assetScale specifies the scale of the amount. So in order to display the amount a single receipt brings in you'd do something like this:

item.addEventListener('monetization',function(e){
	let amount = e.amount;
	let scale = e.assetScale;
	let code = e.assetCode;
	let receivedSum = amount * 10^-scale;
	console.log('Received ' + code + ' ' + receivedSum)
})

Thus, with an amount of 6 and an assetScale of 3 you received 6 * 10 ^-3 or 0.006 of whatever currency is specified in assetCode.

paymentPointer gives the payment pointer specified by the <link> tag at the moment the payment request was sent. If the payment pointer changed between the sending of the request and the arrival of the receipt, paymentPointer still gives the old value.

receipt gives you access to the receipt you received. This is only useful if you wish to verify a receipt.

load and error events

If the JSON linked to by the <link> tag loads correctly a load event fires. If something goes wrong during the loading an error event fires. Unfortunately that last event does not tell you what went wrong; it could be the file is entirely missing, or the JSON is malformed. The only thing that's certain is that you won't receive any money.

iframes

If you have iframes on your page you can allow them to share your payment stream.

iframes have an allow attribute that specifies its feature policy. The WMS defines the monetization value that, if set, allows monetization of the page in the iframe.

If allow="monetization" is set, the iframe will start up a monetization stream as well. If the value is not set the iframe cannot be monetized. Nested iframes also work, provided each of them has allow="monetization". Remember, though, that implementations may set a maximum number of connections, so monetizing dozens of iframes is probably not going to work.

If a page is in an iframe that does not have allow="monetization" any <link> tag will not be used and the monetization stream will not start up.

The specification does not state exactly how the payment stream is divided among parent page and iframe(s). The only thing that is certain is that there will be some sort of division. If each iframe were to have its own independent monetization stream, malicious web developers might include hundreds of iframes on their pages to get hundreds of payment streams. That is undesirable.