🔥 Introducing PayTrackr - an easy way to keep track of all your micropayments!
Tags: gftwhackathon, webmonetization, javascript
Published: 2020-05-25 07:51:38

Currently, web monetization providers doesn't have a feature to show subscribers a breakdown of where their money went. This is due to the fact that providers don't track or know where their subscribers go, [in the name of privacy](https://coil.com/p/sharafian/Doubling-Down-on-Privacy/cD_ZiwT2J). It's a tough balance between privacy and data.

## What I built

I built a secure browser extension that lets you track and manage your micropayments to Web-Monetized websites, having a web monetization provider membership (i.e. Coil).

PayTrackr stores all of your micropayments locally on your device. Only you have access to your data. Not even the web monetization providers can read your data.

PayTrackr is 100% open source software. The source code for PayTrackr is hosted on GitHub and everyone is free to review, audit, and contribute to the PayTrackr codebase.

PayTrackr is currently in beta testing so there will be changes anytime soon.

### Submission Category: 

Creative Catalyst / Exciting Experiments

### Download links:

- [Chrome](https://chrome.google.com/webstore/detail/paytrackr/pcfbnmieeijeahdbbjefgkcbkfnccpei)
- [Firefox](https://addons.mozilla.org/en-US/firefox/addon/paytrackr/)
- Edge - Follow [this](https://www.theverge.com/2020/1/16/21068646/microsoft-edge-browser-extensions-install-how-to-chrome-google) to allow Google Chrome extensions

## Demo

**Dashboard** - Aggregated breakdown on how much went to each site in total

![Dashboard](https://i.imgur.com/XBgTVai.gif)

**Recent Payments** - History of micropayments to websites with web monetization

![Recent Payments](https://i.imgur.com/lxr2R6Q.gif)

**Enable/Disable Monetization**

![Enable/Disable Monetization](https://i.imgur.com/SrRnTqc.gif)

**Payment Counter** - Floating counter of how much went to each active tab in total

![Payment Counter](https://i.imgur.com/RbGT3yx.gif)

**Payment Alerts** - Get notifications when a certain amount is reached

![Payment Alerts](https://i.imgur.com/NcumuKe.gif)

**Export History** - Export micropayments history to csv/xlsx

![Export History](https://i.imgur.com/grIjHnt.gif)

**Charity** - Turn on to split payments between the Web-Monetized website and the PayTrackr developer. (Switched off by default)

![Charity](https://i.imgur.com/I6lDMXa.gif)

You can find updated demos in the comment section.

## Benefits

Installing PayTrackr side by side with Coil or any other provider soon gives us benefits like:

- We become financially aware and improve money management
- We can allocate money to each site equally if we want to
- We can monitor payment streams in real-time

## Link to Code

The code and installation instructions are hosted on GitHub.

{% github https://github.com/sorxrob/paytrackr %}

## How I built it

### Tech Stack

- Vue - JavaScript Framework
- Vuetify - Material Design component framework for Vue
- Chart.js - Flexible JS Charts
- SheetJS - Used to export data to csv/xlsx
- [CoinGecko API](https://www.coingecko.com/en/api) - XRP/USD conversion

Browser APIs used:

- [Storage](https://developer.chrome.com/extensions/storage) - used to store payment streams locally
- [Notifications](https://developer.chrome.com/apps/notifications) - used to notify user when a certain amount declared in the `Alerts` tab is reached.
- [Tabs](https://developer.chrome.com/extensions/tabs) - used to send updates when toggling monetization status, etc.

### My discovery along the way

If you're not familiar with browser extension development, I advise you to take a look at [Chrome's Getting Started Tutorial](https://developer.chrome.com/extensions/getstarted) or [Mozilla's](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension).

The first thing I did is to figure out how can I listen to monetization events in each page I visit. This is the most important part in building this extension because we can't really do much without having access to streamed payments.

Luckily, we can use a [content script](https://developer.chrome.com/content_scripts.html) since it has access to all pages we visit in the browser.

But no. We can't use a `content script`. 

`document.monetization` is an [expando](https://developer.mozilla.org/en-US/docs/Glossary/Expando) property on a standard DOM document interface, this property is not a part of DOM, it's essentially a JavaScript object so it's not accessible directly from a content script which runs in an isolated world - all JavaScript objects/variables/expandos are isolated so the page scripts can't see the JS objects of content scripts and vice versa.

In Chrome, to overcome this, we need to run a code in page context and then use standard DOM messaging via [CustomEvent](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) to coordinate the code in page context and the content script.

First we create a file called `inject.js`.

```javascript
if (document.monetization) {
  document.monetization.addEventListener("monetizationprogress", (e) => {
    document.dispatchEvent(
      new CustomEvent("paytrackr_monetizationprogress", {
        detail: e.detail,
      })
    );
  });
}
```

Then create our content script file called `content.js` and add this code.

```javascript
let s = document.createElement('script');
s.src = chrome.runtime.getURL('script.js');
s.onload = function() {
    this.remove();
};
(document.head || document.documentElement).appendChild(s);
```

Basically we injected a code and it is now running in page context. Then to communicate between the injected page script and the content script, we can add this to our `content.js` file.

```javascript
document.addEventListener('paytrackr_monetizationprogress', (e) => {
  console.log('received', e.detail);
});
``` 

With that, we can now listen to monetization progress events for all Web-Monetized content we visit which holds mostly the data we need to build our extension.

To do this in Firefox, we can use [wrappedJSObject](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Sharing_objects_with_page_scripts).

This is now what our `manifest.json` looks like.

```json
{
  "name": "PayTrackr",
  "description": "Track and manage your micropayments into one place 🎉",
  "version": "0.0.1",
  "manifest_version": 2,
  "icons": {
    "48": "icons/icon_48.png",
    "128": "icons/icon_128.png"
  },
  "browser_action": {
    "default_title": "paytrackr",
    "default_popup": "popup/popup.html"
  },
  "background": {
    "scripts": ["background.js"]
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ],
  "content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
  "web_accessible_resources": ["inject.js"],
  "permissions": ["storage", "unlimitedStorage", "notifications"]
}
```

To listen to events from iframes, we can set `all_frames` to `true` in our content script.

```json
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "all_frames": true,
      "js": ["content.js"]
    }
  ],
```

**Note**: We need to add `inject.js` in the [web_accessible_resources](https://developer.chrome.com/extensions/manifest/web_accessible_resources.html) for Chrome to not refuse to load our script and display the following error in the console:

```
Denying load of chrome-extension://[EXTENSIONID]/script.js. Resources must be listed in the web_accessible_resources manifest key in order to be loaded by pages outside the extension.
```

### Charity implementation

So you may be wondering how I did the split payments when the charity option is enabled.

I created a function that creates an iframe element and appends it to the body of the document.

```javascript
 const attachIframe = () => {
    const iframe = document.createElement("iframe");
    iframe.src = "https://paytrackr-developer.now.sh/";
    iframe.style = "width:0;height:0;border:0;border:none;";
    iframe.allow = "monetization";
    document.body.appendChild(iframe);
  };
```

The area of focus in the code above is the `iframe.src` and `iframe.allow`. The value of the `iframe.src` is basically an empty Web-Monetized page I deployed in [vercel](https://vercel.com/) and to monetize the iframe, we add `monetization` to the iframe's `allow` attribute.

### Walls I bumped into

Originally, I was going to use `chrome.storage.sync` instead of `chrome.storage.local` to store micropayments and have synchronization between devices but the `sync` property [have limits](https://developer.chrome.com/extensions/storage#property-sync).

Because of that, I refactored my code to use `chrome.storage.local` and good thing about this is that we can store unlimited amount of data ... but without sync.

## Future plans

- Sync data between devices
- ✅ Start/Stop/Pause payment streams?
- Send history to email

Thank you [Dev](https://dev.to/) and [Grant For The Web](https://www.grantfortheweb.org/) for conducting this awesome hackathon and giving all the participants an opportunity to contribute.

Please let me know if you have any questions.

*Cover photo by [Josh Appel](https://unsplash.com/@joshappel) on [Unsplash](https://unsplash.com/)*