Monetize your Dart apps with Web Monetization
Tags: gftwhackathon, webmonetization, dart, showdev
Published: 2020-06-06 03:02:48

###### Dart and the related logo are trademarks of Google LLC. I am not endorsed by or affiliated with Google LLC.


## What I built
`Monetization` is a Dart package for implementing and controlling Web Monetization on your web app. It exposes a straightforward API that resembles and extends the functionality of the Web Monetization's JavaScript API.

### Submission Category: Foundational Technology


## Demo
- [Live demo](https://tomasarias.me/monetization)
- [Demo code](https://github.com/KNawm/monetization/tree/master/example)

## Link to Code
{% github KNawm/monetization %}


## How I built it
For this project, I spent most of the time researching Web Monetization and it's eco-system, analyzing its strengths and where it lacked most.
The provided JavaScript API was the first stop as it's the fundamental part to implement Web Monetization, I wanted to have all of the core functionality as familiar as possible. With this in mind, I began to implement the shared API between the two:

In JavaScript, you can check if a user supports Web Monetization checking if `document.monetization === undefined` but because in Dart there is no use in exposing the `document.monetization` object directly, we can check everything using the `state` getter.

```dart
/// Returns the monetization state provided by the browser.
///
/// **`undefined`**:
/// Monetization is not supported for this user.
///
/// **`pending`**:
/// Streaming has been initiated, yet first non zero packet is
/// "pending". It will normally transition from this `state` to `started`,
/// yet not always.
///
/// **`started`**:
/// Streaming has received a non zero packet and is still active.
///
/// **`stopped`**:
/// Streaming is inactive. This could mean a variety of things:
/// - May not have started yet
/// - May be paused (potentially will be resumed)
/// - Has finished completely (and awaits another request)
/// - The payment request was denied by user intervention
String get state => _state ?? 'undefined';
```

In JavaScript, you can listen to the `monetizationpending | monetizationstart | monetizationstop | monetizationprogress` events, in Dart there are corresponding streams of those events that you can subscribe.

```dart
/// Stream that tracks 'monetizationpending' events.
///
/// This event fires when Web Monetization is enabled.
Stream<Map> onPending;

/// Stream that tracks 'monetizationstart' events.
///
/// This event fires when Web Monetization has started actively paying.
Stream<Map> onStart;

/// Stream that tracks 'monetizationstop' events.
///
/// This event fires when Web Monetization has stopped.
Stream<Map> onStop;

/// Stream that tracks 'monetizationprogress' events.
///
/// This event fires when Web Monetization has streamed a payment.
Stream<Map> onProgress;
```

But all of this functionality only works if you have a payment pointer defined in a `<meta>` tag in the HTML. This can be achieved initializing a `Monetization` object:

```dart
import 'package:monetization/monetization.dart';

main() {
  var monetization = Monetization('\$pay.tomasarias.me');
}
``` 

And that's it, all the set up is done by the package, no need to edit the HTML or do Dart-JS interoperability. This is especially useful on Flutter Web where DOM manipulation or calling JavaScript is very rare.

This is where we began extending beyond the JavaScript API, for example, we can get information about the monetization status:

```dart
monetization.isMonetized; // Returns if the user supports monetization
monetization.isPaying;    // Returns if the user is streaming payments
monetization.pointer;     // Returns the current payment pointer
```

Or even get the revenue paid to the current payment pointer:

```dart
monetization.getTotal(); // 884389
monetization.getTotal(formatted: true); // 0.000884389
monetization.assetCode;  // 'XRP'
monetization.assetScale; // 9
```

Another useful thing we could do is programatically enable or disable the monetization:

```dart
var monetization = Monetization('\$pay.tomasarias.me'); // Monetization enabled on initialization
monetization.disable(); // Stops the monetization
monetization.enable();  // Start the monetization again with the same pointer
```

If you want to share the revenue across different payment pointers, probabilistic revenue sharing is also supported:

```dart
final pointers = {
  'pay.tomasarias.me/usd': 0.5,
  'pay.tomasarias.me/xrp': 0.2,
  'pay.tomasarias.me/ars': 0.3
};

var monetization = Monetization.probabilistic(pointers);
```

This will result in a 50% chance of choosing the first pointer, a 20% chance of choosing the second, and a 30% chance of choosing the third one.

One of the last features we are going to explore is the ability to verify the payment stream and anonymizing the payment pointer.

Verifying the payments is complex and not very straightforward at the moment (at least until STREAM Receipts are fully adopted). Thankfully there is a service that can do that for us, [Vanilla](https://vanilla.so) keeps your information more secure and ensures users don’t cheat their way around the payment mechanism. To learn more about Vanilla check out [this article](https://dev.to/cinnamonvideo/vanilla-by-cinnamon-497).

To use Vanilla with `Monetization` we can initialize it like this:

```dart
// Vanilla API Credentials
final clientId = 'Your Client ID';
final clientSecret = 'Your Client Secret';

var monetization = Monetization.vanilla(clientId, clientSecret);
```

Now you can check if the payment stream is valid in different ways:

```dart
// With Vanilla, this will return true if the user is paying and Vanilla generates a proof of payment.
monetization.isPaying;
// With Vanilla, this will return the current payment rate per second, if the monetization is stopped this will be 0.
monetization.getVanillaRate()
// With Vanilla, this will return the total amount received from the current requestId.
monetization.getVanillaTotal();
```

This gives us much more confidence serving monetized content, ensuring only legit monetized users can access your content.

And last but not least, a debug mode to see all the events in real-time:

![Debug mode](https://tomasarias.me/monetization/dev/debug.png)

Thanks to the Coil team for their [sample projects on Glitch](https://glitch.com/@coil), they were great inspiration for the features.

And a special thanks to Norbert Durcansky for sharing his vision and all of the Cinnamon team for crafting [Vanilla](https://vanilla.so).

That would be all! 🙌 Thanks for reading and please feel free to communicate your thoughts, suggestions, or corrections.