Part 4 - Methods, Properties and Plugin Events

By Richard Davey on 23rd April 2021   @photonstorm

Remember, at any time you can read the full Plugin API Documentation on GitHub.

The plugin will emit four key events:

  1. start
  2. stop
  3. pending
  4. progress

You can listen for each of these from your game code because the Game Web Monetization Plugin is an Event Emitter. This means you can use the following methods directly on the plugin:

once('event-name', eventHandler, context);
on('event-name', eventHandler, context);
off('event-name', eventHandler, context);

If you prefer to be more verbose, you can use addListener instead of on and removeListener instead of off. We will use the short version in the following code.

The START Event

This event is emitted when Web Monetization API is successfully started.

To use it, you can bind your own listener to the GameWebMonetization.START event:

gameWebMonetization.on(GameWebMonetization.START, (event) => {
    // Your handler
});

Tip: Remember to do this before calling start() on the plugin!

To test this, let's make it log out some information to the console when the event fires, so we can observe what it returns in our browser Dev Tools:

Add the following code to your main.js file, before you call gameWebMonetization.start():

gameWebMonetization.on(GameWebMonetization.START, (event) => {
    console.log(event);
});

If you now test this, you should see the following in your Dev Tools console:

Result of Event Start

The event handler is sent an object that contains the following properties:

property details
paymentPointer Your payment account URL. The same value is used as the content in your tag.
requestId This value is identical to the session ID/monetization ID (UUID v4) generated by the user agent .

You may have noticed we've been sent both paymentPointer and a requestId.

You can use the start event to know that your game is being monetized. At this point you could show a message to the player, thanking them, or perhaps unlocking some extra content.

Important: Every time you change the browser window, or swap to another browser tab, the monetization stops. When the player returns to the window, the start event will be fired again. So be aware of this flow in your game code and handle it appropriately.

isMonetized

The start event is useful to know when monetization begins, but what if you want to check if your game is monetized or not somewhere deeper in your code?

For this, you can use the isMonetized boolean property.

This property can be checked at any point in your game and provides a simple true/false response to the question "Has this player monetized my game?"

Let's test this by modifying our main.js to console log the state of the property before and after the start event:

var gameWebMonetization = new GameWebMonetization({
    paymentPointer: '$ilp.uphold.com/zdXzL8aWJ4ii'
});

gameWebMonetization.start();

// New code:
console.log('Is monetized? ', gameWebMonetization.isMonetized);

// New code:
gameWebMonetization.on(GameWebMonetization.START, (event) => {
    console.log('[inside event start] - Is monetized? ', gameWebMonetization.isMonetized);
});

If you test this code you'll see that before the monetization starts the property is false and it switches to true after the start event has fired:

isMonetized result

You you can check the isMonetized property at any point in your game. It is kept up to date internally by the plugin, so is safe to use to perhaps award the player a special prize or in-game benefit.

Knowing the current State

The plugin goes through different states in its life-cycle:

  1. started - The plugin has been successfully started and is monetising your content.
  2. stopped - The plugin is currently stopped and not monetising your content.
  3. pending - The plugin has been asked to start and is currently trying to negotiate the start-up, but hasn't yet completed this step.

To know the current state you can query the state property.

Let's do the same as we did with isMonetized to view the state. Here is an updated main.js to test this:

import { GameWebMonetization } from './GameWebMonetization.js';

var gameWebMonetization = new GameWebMonetization({
    paymentPointer: '$ilp.uphold.com/zdXzL8aWJ4ii'
});

gameWebMonetization.start();

// New code
console.log('The state: ', gameWebMonetization.state);

gameWebMonetization.on(GameWebMonetization.START, (event) => {
    // New code
    console.log('[inside event start] - The state: ', gameWebMonetization.state);
});

And in the console you will see the following:

The state

Access to the plugins state is handy for internal debugging.

The PENDING Event

This event is emitted while the Web Monetization API is preparing to start to monetize your site. This happens after you call the start method on the plugin. The API will enter a state of 'pending', meaning it's currently negotiating to start with your Payment Pointer, but hasn't finished doing so yet. If the negotiation is successful, then the plugin will emit its START event.

Let's edit our main.js to demonstrate this state:

import { GameWebMonetization } from './GameWebMonetization.js';

var gameWebMonetization = new GameWebMonetization({
    paymentPointer: '$ilp.uphold.com/zdXzL8aWJ4ii'
});

gameWebMonetization.start();

console.log('The state: ', gameWebMonetization.state);

// New code
gameWebMonetization.on(GameWebMonetization.PENDING, (event) => {
    console.log('[inside event pending] - The state: ', gameWebMonetization.state);
});

gameWebMonetization.on(GameWebMonetization.START, (event) => {
    console.log('[inside event start] - The state: ', gameWebMonetization.state);
});

If we go to the browser we will see the following:

Pending event

Looking at the console logs you can see the API flow in action.

If there is a problem connecting to your Payment Pointer then the flow would be PENDING followed by the STOP event. If the connection was successful the flow would be PENDING followed by the START event.

If there is a network error, for example wifi drops out, while the request is still PENDING then it will remain in this state indefinitely, never reaching the STOP event. So always use the isMonetized boolean in your game code to be aware of the current state of monetization.

The PROGRESS Event

When the Web Monetization API successfully connects to your Payment Pointer it will start to stream micropayments into your wallet. Each time this happens it will fire a PROGRESS event.

This event contains lots of useful data, including how much was just streamed to your wallet and you can use it in your game to keep track of the payment stream, or perhaps use it to visually show a special animation or similar.

You can use the event like this:

gameWebMonetization.on(GameWebMonetization.PROGRESS, (event) => {
    console.log('Progress: ', event);
});

Add the above into your main.js and check it from a browser. Open the console to view the output:

Progress event

This event gives us lots of useful properties:

property details
paymentPointer Your payment account URL. The same value is used as the content in your tag.
requestId This value is identical to the session ID/monetization ID (UUID v4) generated by the user agent.
amount The destination amount received as specified in the Interledger protocol (ILP) packet.
assetCode The code (typically three characters) identifying the amount's unit. A unit, for example, could be a currency (USD, XRP).
assetScale The number of places past the decimal for the amount. For example, if you have USD with an asset scale of two, then the minimum divisible unit is cents.
receipt base64-encoded STREAM receipt issued by the Web Monetization receiver to the Web Monetization provider as proof of the total amount received in the stream.
totalAmount the sum of what has been received with the current paymentPointer, if the paymentPointer is changed this amount will be reset.

As you can see, that's a lot of handy data!

Perhaps the most interesting properties are assetCode and totalAmount. The assetCode is the type of currency we are receiving, in this case it is the XRP crypto currency. Even though your wallet may not be set for XRP this is the main currency Uphold transfers in. Don't worry, though, you will receive your actual desired currency in your wallet. If you are using a provider other than Uphold, then you should see that the currency matches that set in your wallet.

The totalAmount is the amount of income that we have obtained so far from the player during this play session. This counter is reset if the page containing your game is refreshed. It doesn't persist longer than a single play session.

As its name implies, the PROGRESS event helps you keep track of the monetization process at all times. Because the stream of payments to your wallet is constant this event is fired many times. During testing we saw it fired every 2 seconds that the game was running, but the actual frequency may be higher or lower than this. So be careful with what your game does as a result of this event!

Rather than hooking this event to say an in-game animation, you may be better off aggregating the information within it, then using that from your own timed in-game events.

The total property

As we saw, the PROGRESS event gives us lots of handy data. The plugin offers you a total property that keeps track of the total amount of payments a player has streamed to your wallet during their play session.

This value persists even if they tab out to another application and then come back to your game.

You can access the total at any point via the plugin instance:

const currentTotal = gameWebMonetization.total;

The currency of the total will vary based on the wallet service you are using. Uphold, as we're using here, uses XRP and then converts it into your chosen currency their end. However, other wallet services will transmit via the Interledger protocol in the actual currency of your wallet.

This currency can be obtained via the assetCode property sent by the PROGRESS event. If it doesn't match that of your wallet, it's likely using XRP as an exchange currency. You can work out how much you've actually received, in your chosen currency, by using the assetScale property.

The STOP Event

Finally, we have the STOP event. This event is emitted when the API enters a stopped state. This could be from you calling the plugins stop method, or by the user stopping the payment via their browser by performing an action such as switching to another browser tab or window.

You listen for it in the same way as the other events. Let's modify our main.js to handle this:

gameWebMonetization.on(GameWebMonetization.STOP, (event) => {
    console.log('[inside event stop] - The state: ', gameWebMonetization.state);
});

Once the event handler is created it will be emitted if we call the stop method, or switch to another browser tab.

Try running the above and then swapping in and out of the tab a few times. You should see a STOP event for each time you do this:

Stop Event

Alternatively, we could fake this action by using setTimeout to call the stop method for us, like in the following code:

gameWebMonetization.on(GameWebMonetization.STOP, (receive) => {
    console.log('[inside event stop] - The state: ', gameWebMonetization.state);
});

setTimeout(() => {
    gameWebMonetization.stop();
}, 5000);

Now, if you go to the console you will see that the progress event is emitted and then the plugin stops after 5 seconds.

You can use the STOP event to know when monetization has stopped. Just remember that it's entirely possible it will start again, i.e. if they swapped tab or application.

Change the Payment Pointer

If you need to change the Payment Pointer your game is using, you can call the changePaymentPointer method.

Simply call the method and pass it a new configuration object:

gameWebMonetization.changePaymentPointer({
    paymentPointer: '$ilp.uphold.com/ziW6E7iwKUkp',
    pointerName: "Alice"
});
gameWebMonetization.restart();

Calling changePaymentPointer() only prepares the plugin for the change, but doesn't make it. For that, you need to call the restart() method, as in the example code above.

Payment Pointers can also be provided as a weighted array. This allows you to take advantage of probabilistic revenue sharing, which we'll cover in the next part of the tutorial.