loading...

Reflex-Based Web Monetization API

oxharris profile image Oxford Updated on ・4 min read

This is a Reflex-based (live) object that wraps the Web Monetization API. Reflex is JavaScript's new observability API that lets us observe JavaScript objects and arrays, combining the magic of the once useful Object.observe() API with other JavaScript reflection APIs - Object and Reflect. Making the Web Monetization API a live, observable object will be opening up new exciting ways to integrate Web Monetization.

Problem Solved

State changes in the Web Monetization API is event-based and this leaves us much engineering work to do. For example, to track changes to the document.monetization.state property, we have to remember three (3) different event names ( monetizationpending, monetizationstart, monetizationstop) and implement three (3) different callbacks. A Reflex-based object would simply offer this property as an observable property.

// The initial state...
console.log(document.monetization.state);

// Subsequent states would now be...
Reflex.observe(document.monetization, 'state', newState => {
    console.log(newState); // pending, started, stopped
});

This is the level of simplicity led by Reflex, underpinning a growing collection of Reflex-based APIs at the Web-Native Project. The long-term promise of this is reactivity without a framework; change-detection in vanilla JavaScript!

The Web Monetization API will now also be reflexive and natively playing with the rest of the Reflex-driven world.

Usage

This library is part of the official Reflex Components package.

import {Money} from '@web-native-js/reflex-components';
import Reflex from '@web-native-js/reflex';

const WebMonetization = Money.WebMonetization;
// Initialize an instance with a payment pointer
let monetization = WebMonetization.init('$ilp.example.com/me');

// Observe state
Reflex.observe(monetization, 'state', newState => {
    // Code here...
});

// Start/stop anytime
monetization.start();
// monetization.stop();

Features

  • Start/Stop Methods - Calling the .start() method would create the appropriate meta tag in the document head, if not already exists. The .stop() method removes it.
  • Multiple Pointer Implementations - Multiple instances could be initialized with different payment pointers. Calling .start() on an instance starts a new payment stream and stops any currently active one.
  • Optional Prompt - Users coming to a monetized site without a supporting browser might need to be informed of the feature. This can be specified in our call to .init().

    let monetization = WebMonetization.init('$ilp.example.com/me', {prompt: true});
    

    The prompt automatically detects the user's browser and asks to take them to the appropriate extension for their browser.

  • Observable Properties - Now we only need to think of properties, not events and callbacks. These properties are as follows:

    • state - (string). This can either be started, pending, stopped. The following three other properties also reflect the state of the instance.
    • pending - (object|boolean). This becomes an object when state goes pending, false otherwise. This object maps to the event.detail property of the underlying monetizationpending event.
    • started - (object|boolean). This becomes an object when state turns started, false otherwise. This object maps to the event.detail property of the underlying monetizationstart event.
    • stopped - (object|boolean). This becomes an object when state gets stopped, false otherwise. This object maps to the event.detail property of the underlying monetizationstopped event.
    • progress - (object). This property reflects the event.detail property of the underlying monetizationprogress event. Each progress update also automatically updates the following other properties.
    • currentTotal - (object). This object represents the totals between starts and stops on the instance. It begins at zero on each call to .start(). The currentTotal.amount property gives the raw sum of micro payment sent during the stream, while currentTotal.value gives the sum in the format of the active currency.
    • sessionTotal - (object). This object represents the overall totals on the instance. The sessionTotal.amount property gives the raw sum, while sessionTotal.value gives the sum in the format of the active currency.
    • currency - (string). The payment currency.

Usage in ScopedJS - the Fun Part!

Using ScopedJS, the Reflex-based, micro runtime for JavaScript, We could declaratively create behaviour over a WebMonetization instance right within an HTML markup.

First we would make the WebMonetization instance available to scoped scripts.

let ENV = window.WebNative.ScopedJS.ENV;
ENV.globals.monetization = WebMonetization.init('$ilp.example.com/me');

Now we would be able to reference the monetization object from any scoped script and build behaviour on the fly with any element.

Below we are toggling the visibility of an exclusive section on the page by simply referencing the monetization.state property.

<body>
    <div id="exclusive">
        This is exclusive content!
        <script type="text/scoped-js">
          // This statement will run as a reflex action
          // to each change in monetization.state
          this.classList.toggle('hidden', monetization.state !== 'started');
        </script>
    </div>
</body>

And we can display a live total; and even implement a threshold for our exclusive content. (E.g to show more exclusive content as payment adds up.)

<body>
    <div id="exclusive">
        This exclusive content!
        ...
        And you've streamed us <span id="total"></span> in total.
        <script type="text/scoped-js">
          let elTotal = this.querySelector('#total');
          elTotal.innerHTML = monetization.sessionTotal.value + ' ' + monetization.sessionTotal.currency;
          this.classList.toggle('hidden', monetization.sessionTotal.amount < 10);
        </script>
    </div>
</body>

We could also add a start button to the page.

<body>
    <div id="start">
        Show exclusive content
        <script type="text/scoped-js">
          this.addEventListener('click', () => {
              monetization.start();
          });
        </script>
    </div>
</body>

Working Demo

The web-native.dev's project support banner featuring Web Monetization was implemented using the Reflex-based Web Monetization API.

What's Next for this Project

  • Good documentation
  • Bug fixes
  • More examples
  • More experiments

Posted on Jun 6 by:

Discussion

markdown guide