Securely manage your web monetized users without depending on the ILP stack in your application.
When you use Web Monetization, you often want to detect whether a visitor to your site is web monetized. You may want to make sure they pay a minimum amount per second, or a minimum amount in total to unlock your content.
You can detect web monetization by looking at the client-side javascript events, but this can be spoofedby a clever visitor. The only way to guarantee that the visitor is paying is by having a service that actually accepts Interledger packets and communicates to your backend.
Web Monetization Access serves as an Interledger receiver, and will sign a token that your backend can verify to check that a visitor is web monetized.
The interaction between the visitor and Web Monetization Access is provided by a client-side script that you can import.
The interaction between Web Monetization Access and your backend is just via a simple JWT that you can attach to requests.
- First, the visitor arrives on your page.
- If you detect the web monetization API on the client side, you can tell the visitor that the page is loading until monetization completes.
- If the visitor does not have the web monetization API, you can send them to the non-monetized version of your site.
-
Add a Web Monetization meta tag pointing to your Web Monetization Access server. You can tell the Web Monetization Access server where to pay out its funds via the query string, and arbitrary extra information can also be added to the query string.
-
The visitor opens a connection to the Web Monetization Access server. When the Web Monetization Access server receives payment from the client, it will send a signed JWT to the client.
-
When the visitor wants to request exclusive Web Monetized content from your site, they will attach the signed JWT to their request. Your backend validates this JWT and returns exclusive content if it is valid.
First, install and build the necessary components.
npm install -g ilp-spsp ilp-spsp-server moneyd
git clone git@github.com:sharafian/web-monetization-access.git
cd web-monetization-access
npm install
npm run build
Now in one shell, start moneyd
.
moneyd local
In another shell, start a local SPSP server.
ilp-spsp-server --localtunnel false --port 9000
And then start web monetization access
# go to the directory where we cloned WM access
cd web-monetization-access
PORT=8080 npm start
Now all the necessary components are running. To test a payment, you can use the ilp-spsp
tool.
ilp-spsp send -r 'http://localhost:8080/pay?pp=http://localhost:9000' -a 1000
You'll see that everything worked in the logs. You should see output similar to below:
# ILP SPSP client logs
paying 1000 to "http://localhost:8080/pay?pp=http://localhost:9000"...
sent 1000 units!
# Web Monetization Access logs
connection with metadata {"pp":"http://localhost:9000","requestId":"2b4e7010-124f-44ec-b2f6-0fb2cb5e2156"}
received money 1000
# SPSP Server logs
got packet for 1000 units
In the logs you can see that the Web Monetization Access server gave you an ID. This is based on the Web-Monetization-Id
header, or generated randomly if that's not present. You can use this ID to fetch your proof of payment.
curl http://localhost:8080/pay/proof/2b4e7010-124f-44ec-b2f6-0fb2cb5e2156
You'll get a response that looks similar to the one below:
{
"data": {
"metadata": {
"pp": "http://localhost:9000",
"requestId": "2b4e7010-124f-44ec-b2f6-0fb2cb5e2156"
},
"rate": 121.213,
"total": 1000
},
"token": "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0b3RhbCI6MTA2MTkxMTA2LCJyYXRlIjoyMTU1MzcuNiwibWV0YWRhdGEiOnsicHAiOiJodHRwOi8vbG9jYWxob3N0OjkwMDAiLCJyZXF1ZXN0SWQiOiI0YmQyMDM5Ny1jZjFhLTRkMjctODg3Mi0xZWFlNTk1MTBiOTUifSwiaWF0IjoxNTYxNTA4ODc3LCJleHAiOjE1NjE1MTE4Nzd9.nL1N1xz1BTqTy-XyDLVyb6wdzfNzOkFUt1aPUVeOIXP3opGQ6vNumbbKsTYAEJ7p86KRnvGVSuz5p64igDZozw"
}
This token is a signed JWT which can be verified by anyone. You can get the public key it's signed with by querying http://localhost:8080/pay/public_key
{
"public_key": "-----BEGIN PUBLIC KEY-----\nMFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAElRLGSaC2JyslmL+55TX0QOAQZcrpfnrK\niTzDVw0/Jml93IsoWdVtJrpDzpkvE76qKuxYQh8GS33kx+HCTBGjrA==\n-----END PUBLIC KEY-----\n"
}
This gives you the public key in the pem
format. It can be used to verify the JWT, which was signed with the ES256
algorithm.
Note: Your moneyd
needs to be connected to the livenet for this to work.
This repository includes an example project which uses Web Monetization Access to serve an image exclusively to web monetized users. To start the service, run the following steps:
Connect to the Interledger livenet with your method of choice.
moneyd xrp:start
Next we'll start up our local SPSP server.
ilp-spsp-server --localtunnel false --port 9000
Now the Web Monetization Access Server. We have to enable the ALLOW_CROSS_ORIGIN
flag for our example server to work. In production you'll likely be serving Web Monetization Access on the same domain as the rest of your site so you don't need to set that flag.
cd web-monetization-access
ALLOW_CROSS_ORIGIN=true npm start
This is the API server that will serve our exclusive content
cd web-monetization-acccess/examples/gallery-api
npm install
npm start
The client for our example gallery uses react-scripts, which comes with its own server.
cd web-monetization-access/examples/gallery
npm install
npm start
Make sure your browser is web-monetized, whether by the Coil extension or another method. Load up localhost:3000
in your browser (The URL output by the launch gallery client step.
You'll see the page go through the following phases:
Awaiting web monetization...
Loading image...
- Image is loaded and displayed
What's happening behind the scenese is:
- The webpage detects you're web monetized (client-side) and waits for web monetization to start
- Once web monetization has started the client asks the WM access server for proof
- The client gets the proof and presents it to the gallery API when it asks for the image
- The gallery api verifies the proof and ensures that the user has paid before serving the image
- The gallery api serves the image and the client renders it
- encrypt query string params/metadata
- allow support for BTP or HTTP plugin
- proxy ILP streams to the payment pointer in q string
- use websockets or long-polling or polling to fetch the tokens
- algorithm to detect the bandwidth of a connection
- use public key signature for the validation of JWT
- library that can be pulled into react for client
- example server that uses the JWT
- handle the timeout of the JWTs?
- handle errors and expiry for uncooperative payment pointer
- add persistence in redis
- add some kind of accounting persistence
- structured logging