Monetize.js is an Event-driven library that serves as a wrapper and simulator for Web Monetization API.
- Flexible pointers management (Static and Dynamic).
- Promise-like syntax to watch events.
- Customizable cycle-through list of pointers.
- Support probabilistic cycle-through list of pointers.
- Calculate the total amount grouped by currency.
- Powerful simulator that works without iframe or installed extension.
Using npm
npm install monetize
// Using it
const monetize = require('monetize');
Using unpkg CDN:
<script src="https://unpkg.com/monetize@latest/dist/monetize.js"></script>
Multiple examples has been created using monetize.js you can find them in this examples folder.
As soon as you import Monetize.js it will start observing the head tag to see if there's a pointer to pick.
You can simply start using static mode by manually adding the payment pointer to the head tag:
<head>
...
<meta
name="monetization"
content="$wallet.example.com/alice"
>
...
</head>
Then in your code you can listen to the different event using
monetize.when('start').then((event) => {
// Your event listener logic here.
});
monetize.when('progress').then((event) => {
// Your event listener logic here.
});
Note: This is not a promise.The chained then
here will be called multiple time as long as the monetizationstart
event is fired.
Monetize.js event | Original event |
---|---|
start | monetizationstart |
progress | monetizationprogress |
pending | monetizationpending |
stop | monetizationstop |
You can learn more about the event object passed to each event from the list by exploring Monetization Javascript API documentation.
The library is aimed to support custom event too. For now it supports the following custom event:
Event | Description |
---|---|
pointer_changed | Fired when a new pointer has been detected in the head. |
The library provide multiple ways for dynamic pointer configuration:
// Encapsulated mode
const p = '$alice.example';
monetize.pointer(p).then((watcher) => {
// monetizationstart event has been fired.
// Get the `monetizationstart` event object detail
const { paymentPointer, requestId } = watcher.event.detail
// If you would like to verify the session id in the back end. You will need to do it here
verifySession(paymentPointer, requestId)
watcher.when('progress').then((event) => {
// You can verify the payment here
if (! verifyPayment(event.detail)) {
doNotVerifiedLogic();
return;
}
// Your event listener logic.
});
});
// Free mode
monetize.when('start').then((event) => {
// your logic here
});
monetize.when('progress').then((event) => {
// your logic here
});
monetize.pointer(p);
if no pointer has been provided or the Monetization API is disabled an error will be thrown that you can catch via catch
method:
monetize.pointer('')
.then(...)
.catch((error) => {
console.log(error.message);
});
Note: You can omit then
and shortcut to catch directly.
Only add the meta tag
if you wish to change or just add a pointer to the meta tag without the extra encapsulation you can just call set
method.
monetize.set('$example');
Note: this method won't perform any extra validation it will add the passed value as it is. Passing invalid value would interrupt the payment process.
If you have a list of pointer to chose from you can use the `pluck' method to randomly pick a pointer from the list.
const pointers = [
'$alice.example',
'$connie.example',
'$bob.example'
];
// Randomly picking a pointer.
monetize.pluck(pointers)
// Or using encapsulation.
monetize.pluck(pointers).then(...)
// Error aware encapsulation.
monetize.pluck(pointers).then(...).catch(...);
If you would like to assign a probability for each pointer you can do that by passing an object
to pluck
function like the example below:
// Probability for each pointer. Must sum to 1
const pointers = {
'$alice.example': 0.6,
'$bob.example': 0.05,
'$connie.example': 0.30,
};
// Randomly picking a pointer.
monetize.pluck(pointers)
// Or using encapsulation.
monetize.pluck(pointers).then(...)
// Error aware encapsulation.
monetize.pluck(pointers).then(...).catch(...);
Sometimes picking a single pointer on page load isn't enough. For that, you may consider using cycle
and probabilisticCycle
to cycle through a list of pointers
for a given timeout
Cycle
/**
* Sequentially cycle through the given pointers.
* @param {string[]} pointers list to pick from.
* @param {number} timeout how often the pointer should be changed.
* @param {function} callback an optional callback to control how a pointer is chosen. It must return a pointer otherwise it will be ignored.
* @returns {PromiseLoop}
*/
cycle(pointers, timeout = 3000, callback);
the following example illustrate how to use it:
const pointers = [
'$wallet',
'$wallet2',
'$wallet3',
];
// Simple usage.
monetize.cycle(pointers, 5000);
// Use `then` to listen for the first pointer `monetizationstart` event.
monetize.cycle(pointers, 5000).then(...).catch(...);
probabilisticCycle
probabilisticCycle(pointers [, timeout])
the following example illustrate how to use it:
const pointers = {
'$alice.example': 0.6,
'$bob.example': 0.05,
'$connie.example': 0.30,
};
// Simple usage.
monetize.probabilisticCycle(pointers, 5000);
// Use `then` to listen for the first pointer `monetizationstart` event.
monetize.probabilisticCycle(pointers, 5000).then(...).catch(...);
⚠ Important
Only one cycle-through is allowed. Hence always the last list will be used.
Consider the following code in a single page:
monetize.cycle(pointers1, 2000);
monetize.probabilisticCycle(pointers2);
monetize.cycle(pointers3);
monetize.probabilisticCycle(pointers4, 4000);
The library will only cycle through pointers4
list.
Example
You can at any time check for the monetization status by calling:
Method | Description |
---|---|
isEnabled() | Check if the Monetization API is supported by the current browser. |
isSending() | Check if there's a monetization stream going on. |
isStopped() | Check if the service is idle. |
isPending() | Check if the service is waiting for the first payment. |
You can use monetize.js to add different classes to the body
tag. To do so you need to use the setup
method to override the default config and enable the classes.
monetize.setup({
addClasses: true,
});
// it's possible to chain it with other methods
monetize.setup({ ... }).pointer('$test').then(...);
Available classes
Class name | Description |
---|---|
js-monetization-disabled | Added when the API is not supported in the browser. |
js-monetization-enabled | Added when the API is supported in the browser. |
js-monetization-pending | Added when the state is pending |
js-monetization-stopped | Added when the state is stopped |
js-monetization-sending | Added when the state is started |
Override the default classes
To override the default classes you will need to send an extra property for the setup method:
const config = {
addClasses: true,
classes: {
disabled: 'js-monetization-disabled',
enabled: 'js-monetization-enabled',
pending: 'js-monetization-pending',
stopped: 'js-monetization-stopped',
sending: 'js-monetization-sending',
},
}
monetize.setup(config);
The monetize
object leverage an extra API that keeps track of the streamed amount.
The following is basic example on how to get the total streamed amount and currency for a given pointer.
Note: to use Amount API you must use one of these methods first: pointer
, pluck
, cycle
or probabilisticCycle
to setup the basic infrastructure for the API.
const pointer = '$example';
monetize.pointer(pointer);
// Get the Raw amount sent.
const amount = monetize.amount.getPointerTotal(pointer);
// => output: 5258
// Or get the Formatted amount
const amount = monetize.amount.getPointerTotal(pointer, true);
// => output: 0.035
// Get the currency
const currency = monetize.amount.getPointerCurrency(pointer);
If your page is using multiple pointers at the same time you can get the total streamed amount grouped by currency using total
method:
monetize.cycle(...);
// Raw total
const total = monetize.amount.total()
// Formatted total
const total = monetize.amount.total(true)
/*
Example returned value.
{
usd: 5825,
XRP: 8875,
}
*/
Note: if all pointers have the same currency the amount of that currency will be returned instead.
Beside the core library, a powerful Web Monetization simulator is shipped. It does not require any extension installed or to be executed inside an iFrame.
An example for simulator in action.
If installed using npm
const monetize = require('monetize');
const simulator = require('monetize/dist/simulator');
Or using a CDN
<!-- Core library -->
<script src="https://unpkg.com/monetize@latest/dist/monetize.js"></script>
<!-- Simulator -->
<!-- The simulator uses the core library hence it must be called after it. -->
<script src="https://unpkg.com/monetize@latest/dist/simulator.js"></script>
All what you need is calling the simulator script. It will take care of the rest.
The simulator has 4 states showed in the dollar sign icon.
- Gray: Unknown state.
- Orange: Pending state.
- Green: Sending payments sate.
- Red: Stopped state.
The reason why the simulator it does not require a payment provider extension or be run in a iFrame is due the fact that it mock the Monetization API when it's not supported.
To contribute to Monetize.js, follow these steps:
- Fork this repository.
- Create a branch:
git checkout -b <branch_name>
. - Install all the dependencies:
npm install
- Make your changes and commit them:
npm run commit
[ see below ] - Push the changes to your fork:
git push
- Create the pull request.
The project uses semantic releases to automatically push new releases. Hence a set of convention has been established. Committing your changes through the npm run commit
command will walk you through the commit content structure that you need to create.
Before adding a commit two verification will take place:
- Linting using Eslint.
- Testing using Jest.
Both of them have to succeed in order to validate the commit.
This project is licensed under the MIT Licence.
Copyright (c) 2020 Mazen Touati