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>
tagSite 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.
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.
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.
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.
<link>
tagIf 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.
Each <link>
tag fires three events:
load
: the payment pointer JSON was loaded correctly
error
: something went wrong with loading the payment pointer JSON
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 <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.
The monetization event has the following read-only properties:
amount
assetCode
assetScale
receipt
paymentPointer
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.
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.
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.