Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Web Monetization to <meta> tag flow. #500

Merged
merged 15 commits into from
Jan 17, 2019
Merged

Conversation

sharafian
Copy link

Originally suggested by @michielbdejong in #488

Instead of doing web monetization like this:

<script src="https://polyfill.webmonetization.org/polyfill.js"></script>
<script src="https://cdn.coil.com/donate.js"></script>
<script>
  window.WebMonetizationScripts.donate({
    paymentPointer: '$twitter.xrptipbot.com/sharafian_'
  })
</script>

Do it like this:

<meta
  name="webmonetization"
  content="paymentPointer=$twitter.xrptipbot.com/sharafian_" />

We've noticed that in most of the use-cases of Web Monetization, the webmaster wants the browser to pay to a single payment pointer as long as they're on their site. Initially we assumed this was because sites didn't have any features for premium content, but when implementing web-monetization-receiver I also found this to be the most useful pattern.

Making the Web Monetization standard serve a narrower use-case opens of opportunities for performance optimization. It makes implementation for the site simpler too.

Another factor that makes this desirable is the removal of 3rd party scripts. Instead of loading 2 large external scripts, this example is now only static HTML. Webmasters, especially those of large (and therefore risk averse) sites, would be opening themselves up to far fewer security risks.

Using HTML instead of Javascript also means you can scrape the page with a simple HTTP client in order to get its payment information. This could be very useful if you wanted to index a page or embed some of its content, and automatically pay for it.

Finally, this approach gives more flexibility to the user's agent. If the browser wanted to authorize a higher bandwidth for some payment pointers or sites, it would be difficult. STREAM has no concept of a payment pointer, so the browser would have to just look at which tab the money is coming from. The browser could even send the payment details to a remote server and have all the heavy lifting occur elsewhere. This would be a huge boon for mobile browsers, where maintaining a constant STREAM connection can drain battery.

Let me know your feedback!

@michielbdejong
Copy link
Contributor

Great!

@justmoon
Copy link
Member

justmoon commented Jan 3, 2019

The query string inside of a meta tag strikes me as odd. When I look at other meta-tags, it seems that most schemes that require multiple fields keep them as separate meta tags, e.g. for Open Graph:

<meta property="og:title" content="The Rock" />
<meta property="og:type" content="video.movie" />
<meta property="og:url" content="http://www.imdb.com/title/tt0117500/" />
<meta property="og:image" content="http://ia.media-imdb.com/images/rock.jpg" />

Not sure about all the pros/cons but just wanted to point out that this seems like the more common pattern.

There is also viewport which is very widely used, which looks like this:

<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=contain">

Do we actually need the ability to provide a correlation ID? I'm not a big fan of having these two different ways that things happen, i.e. either I pass the correlation ID or it gets auto-generated. What if the correlation ID always gets auto-generated and if I want to use my own unique identifier, I just make a unique payment pointer?

- The browser creates an iframe to the handler URL, reading from the state that was stored during [registration](#registration).
- An ILP/STREAM connection object is returned from the function for the site to use.
- When the page wants to use the ILP/STREAM connection, they use the javascript STREAM API to send money and/or data. The browser sends outgoing ILP packets to the handler iframe using `postMessage`. The handler iframe forwards incoming packets by calling `window.parent.postMessage`.
- The user's browser looks for the Web Monetization `<meta>` tag ([specified below](#meta-tag)). The `<meta>` tag MUST be present once `document.readyState` is `interactive`. Implementations MUST NOT look process the tag earlier than this, but MAY wait longer before processing.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: "look process".. should be "look for" or "process"?

```

### Web Monetization Handler API
The `paymentPointer` matches the one in the `<meta>` tag. The correlationId matches the one in the `<meta>` tag if specified, and is otherwise generated as a random UUID (see [Flow](#flow)).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: code-quote correlationId

@sharafian
Copy link
Author

@justmoon

The query string inside of a meta tag strikes me as odd. When I look at other meta-tags, it seems that most schemes that require multiple fields keep them as separate meta tags, e.g. for Open Graph:

Sounds like a reasonable change.

Do we actually need the ability to provide a correlation ID?

Yeah, you're right; the server could just supply a payment pointer with a different path as a way to encode the user ID.

@sharafian
Copy link
Author

sharafian commented Jan 4, 2019

Updated. Tag now looks like this:

<meta
  name="webmonetization:paymentpointer"
  content="$twitter.xrptipbot.com/sharafian_" />

Also added a specification for iframes which include pages with web monetization meta tags, mandating that they set data-allowwebmonetization as an attribute.

Copy link
Collaborator

@adrianhopebailie adrianhopebailie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I understand the correlation id idea.

I assume the only way the server associates the correlation id from the SPSP request to the original HTTP request for the page is by catching the webmonetization events?

i.e. The flow is:

  • UA fetches page and server creates session with UA
  • UA processes meta tag
  • UA emits event with correlation id
  • Client side code notifies server of correlation id and server associates with to session?
  • UA makes SPSP request using correlation id in header and server can now correlate session with payment

This seems clunky in comparison to simply generating a unique payment pointer per session? Is there another use for the correlation id I'm missing?

I am also concerned that this correlation may be considered a tracking cookie. I'd suggest being explicit that the UA generates a unique correlation id per request/session/origin?

Finally, the wording of the spec seems odd to me. Why would the browser be making payments and not some wallet or other user agent?

It seems like the ideal architecture is for the browser to process meta tags and invoke a registered wallet (Payment Handler) passing it the payment pointer.

- The user's browser looks for the Web Monetization `<meta>` tags ([specified below](#meta-tags)). The `<meta>` tags MUST be present once `document.readyState` is `interactive`. Implementations MUST NOT process the tags earlier than this, but MAY wait longer before processing.
- The `<meta>` tags MUST be in the `<head>` of the document.
- If the Web Monetization `<meta>` tags are malformed, the browser will stop here. The browser SHOULD report a warning via the console.
- If the Web Monetization `<meta>` tags are well-formed, the browser should extract the Payment Pointer and Correlation ID.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the browser extract or generate the correlation ID?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I should have fixed that wording; the browser always generates the correlation ID.

- If the Web Monetization `<meta>` tags are malformed, the browser will stop here. The browser SHOULD report a warning via the console.
- If the Web Monetization `<meta>` tags are well-formed, the browser should extract the Payment Pointer and Correlation ID.
- The browser will generate a fresh UUID (version 4) and use this as the Correlation ID from this point forward.
- The user's browser dispatches a [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) on `window`, indicating that the Web Monetization tags has been recognized and payment will be made.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

`s/tags has/tags have/

- The browser will generate a fresh UUID (version 4) and use this as the Correlation ID from this point forward.
- The user's browser dispatches a [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) on `window`, indicating that the Web Monetization tags has been recognized and payment will be made.
- The `CustomEvent`'s type is `webmonetizationload`. The `CustomEvent`'s `detail` is an object containing the Payment Pointer and the Correlation ID ([specified below](#webmonetizationload))
- The user's browser resolves the payment pointer and begins to pay. The payment process MAY be carried out from a different machine acting as the user's agent. Cookies and browser headers MAY not be carried with any requests made to resolve the Payment Pointer.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cookies and browser headers MAY not be carried with any requests made to resolve the Payment Pointer.

I suggest the spec is explicit about this as it has privacy implications.

I would recommend that the SPSP requests NOT send any headers that correlate it to the main session as this allows a website to delegate receiving of payments to a third-party without compromising any of their user's privacy.

Using a unique payment pointer should be enough for the website to recognise which user is making the payment or is there a need for stronger correlation?

Also see my other comments about the correlation id

- The user's browser dispatches a [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent) on `window`, indicating that the Web Monetization tags has been recognized and payment will be made.
- The `CustomEvent`'s type is `webmonetizationload`. The `CustomEvent`'s `detail` is an object containing the Payment Pointer and the Correlation ID ([specified below](#webmonetizationload))
- The user's browser resolves the payment pointer and begins to pay. The payment process MAY be carried out from a different machine acting as the user's agent. Cookies and browser headers MAY not be carried with any requests made to resolve the Payment Pointer.
- On the SPSP query to resolve the Payment Pointer, a `Web-Monetization-Id` header is sent, containing the Correlation ID. The server may use this to associate future requests by the user with their payments.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The server may use this to associate future requests by the user with their payments.

More detail required here.

Why will future requests use the same correlation id, isn't it up to the UA to generate a new correlation id whenever they want?

Is this a privacy violation if the same id is used across origins?

Is the same id used for a parent context and children (service workers or iframes)?

I guess I;'m still not sure what value the correlation id is adding...

Copy link
Author

@sharafian sharafian Jan 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correlation ID will always be completely unique per page load, even on the same origin

- The `CustomEvent`'s type is `webmonetizationload`. The `CustomEvent`'s `detail` is an object containing the Payment Pointer and the Correlation ID ([specified below](#webmonetizationload))
- The user's browser resolves the payment pointer and begins to pay. The payment process MAY be carried out from a different machine acting as the user's agent. Cookies and browser headers MAY not be carried with any requests made to resolve the Payment Pointer.
- On the SPSP query to resolve the Payment Pointer, a `Web-Monetization-Id` header is sent, containing the Correlation ID. The server may use this to associate future requests by the user with their payments.
- With the details fetched from the SPSP query, a STREAM connection is established. A single STREAM is opened on this connection, and a positive SendMax is set.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

details fetched from the SPSP query

SPSP responses come in a few forms these days. Which fields are required here?

Will there be meta-data about the expected payment throughput in the SPSP response?

Copy link
Author

@sharafian sharafian Jan 4, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can clarify that only the required SPSP response fields (destinationAccount and sharedSecret) are required by Web Monetization


##### Parameters
If the `<meta>` tag exists inside of an iframe, the iframe MUST have `data-allowwebmonetization` as an attribute.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suggest looking at feature policy which is the mechanism the Web Payments APIs are moving to for giving explicit permission to sub-contexts (iframes) to use the APIs.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 will check it out


#### End Connection
These events are dispatched on `window`. All Web Monetization events are [`CustomEvent`](https://developer.mozilla.org/en-US/docs/Web/API/CustomEvent)s.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the motivation for emitting from window?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could also be dispatched on document; not sure which is more typical.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know either (or the practical differences).

@sharafian
Copy link
Author

sharafian commented Jan 4, 2019

@adrianhopebailie

i.e. The flow is:

UA fetches page and server creates session with UA
UA processes meta tag
UA emits event with correlation id
Client side code notifies server of correlation id and server associates with to session?
UA makes SPSP request using correlation id in header and server can now correlate session with payment

That's all correct, yeah.

This seems clunky in comparison to simply generating a unique payment pointer per session? Is there another use for the correlation id I'm missing?

Generation of a unique payment pointer is supported by this scheme, but the correlationId allows Web Monetization to work on pages where the user identity is not known when the page is loaded. Take a react application, for example, where the page itself is loaded statically out of a CDN and then data is loaded from subsequent AJAX calls. In this situation the Javascript will just catch the correlationId and send it along to the server another API call.

I am also concerned that this correlation may be considered a tracking cookie. I'd suggest being explicit that the UA generates a unique correlation id per request/session/origin?

Good idea; I'll make that explicit.

Finally, the wording of the spec seems odd to me. Why would the browser be making payments and not some wallet or other user agent?

The most common scenario is probably that the browser will be making calls to some external wallet to trigger ILP payments, but it might be most general to just say it's the user's agent.

It seems like the ideal architecture is for the browser to process meta tags and invoke a registered wallet (Payment Handler) passing it the payment pointer.

Right now it's going to be done through an extension. That might make more sense as an addition if browsers were implementing it.

Copy link
Member

@emschwartz emschwartz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the near-term, this seems like a sensible way to remove the need for the browser to load some big JS files if 90+% of the sites using the spec will use it in the same way.

In the longer term, I wonder about:

  • Versioning SPSP + STREAM - Do we have a good enough approach for versioning those protocols to support new versions coming out and potential mismatches between browser and website?
  • Website-Defined Rate - Right now the focus for this spec is on a donation-based model rather than a website-defined payment rate. Would this spec be expanded in the future to include a way to express that? If so, would it go as a separate meta tag?
  • Payment for Non-Time-Based Behavior - Are there any plans to support monetizing features of the websites other than how much time you spend on them? What about paying while a video is playing but not paying if it's stopped?

@sharafian
Copy link
Author

sharafian commented Jan 7, 2019

@emschwartz

Versioning SPSP + STREAM - Do we have a good enough approach for versioning those protocols to support new versions coming out and potential mismatches between browser and website?

The SPSP query contains an Accept header which we already use for version negotiation, so I think this is fine. Right now it's application/spsp4+json but if we have more versions the browser can list all the ones it accepts.

Website-Defined Rate - Right now the focus for this spec is on a donation-based model rather than a website-defined payment rate. Would this spec be expanded in the future to include a way to express that? If so, would it go as a separate meta tag?

Yeah, I think more meta tags in the future are the way to go about this. We could have something like webmonetization:paymentrate to give a hint about what payment rate is required to see the content. In the end it's up to the site's server to verify if the money coming in is enough.

Payment for Non-Time-Based Behavior - Are there any plans to support monetizing features of the websites other than how much time you spend on them? What about paying while a video is playing but not paying if it's stopped?

This sorta falls outside of the 90+% use case. So long as the user is willing to pay, we can probably assume the site is going to want payment. However, in the future, we could add more meta tags to give hints like "this is a video-based site so pay when the video is activated" or "this is an audio based page and should be paid for even in the background." Then it's up to the user's agent to decide how much to pay and when, and whether to ask for user permission if ever.

@justmoon
Copy link
Member

justmoon commented Jan 8, 2019

@sharafian

While thinking through implementing this on one of my sites, I noticed that there wasn't a good way to distinguish the case where the user's browser does not support monetization from the case where it does but it just hasn't emitted the event yet.

I think the former should immediately show a "this content requires Web Monetization ..." message and the latter should show a spinner.

Updated. Tag now looks like this:

<meta
 name="webmonetization:paymentpointer"
 content="$twitter.xrptipbot.com/sharafian_" />`

The name is kinda long imo but I also don't really have a better suggestion. Maybe name="monetize" if we're only going to one field or name="wm:destination" if we end up having multiple fields.


Found some interesting prior art:

https://autotip.io/docs/microtip-meta-tag

That's actually close enough that we could almost adopt it. They only thing I don't like is that they conflate currency with payment method. Also we'd be doing more than just tipping, so "pay" or "monetize" would be more appropriate than "tip". I had the same thought regarding @michielbdejong's suggestion, "donate".

@sentientwaffle
Copy link
Contributor

@sharafian re:

Yeah, I think more meta tags in the future are the way to go about this. We could have something like webmonetization:paymentrate to give a hint about what payment rate is required to see the content. In the end it's up to the site's server to verify if the money coming in is enough.

If you later add a new tag that the WM implementation doesn't support, it will be ignored. Should there be a spec version included in the name field (or elsewhere?) for WM implementations to test, to ensure they test for the right features?

@stpeter
Copy link

stpeter commented Jan 8, 2019

+1 to removing the need for third-party scripts, at least. I'll look at this more closely and provide more detailed feedback.

@sharafian
Copy link
Author

@sentientwaffle

If you later add a new tag that the WM implementation doesn't support, it will be ignored. Should there be a spec version included in the name field (or elsewhere?) for WM implementations to test, to ensure they test for the right features?

We could add a version number if we breakingly change in the future. If the webmonetization:paymentpointer label is not provided then browsers/extensions that only support this version of web monetization won't try to monetize the page. I think that should probably be sufficient.

@justmoon

While thinking through implementing this on one of my sites, I noticed that there wasn't a good way to distinguish the case where the user's browser does not support monetization from the case where it does but it just hasn't emitted the event yet.

We could try injecting a window.webMonetizationSupported field into every page. The only problem I see there is that it has to run right at document start or else it might not be there when the page checks.

Another option would be some kind of header sent to the server, which would support this functionality even in the server-side rendering case. It would require the extension to modify all outgoing requests, though, which is less than ideal from a security perspective and might not be possible on all platforms.

@sharafian
Copy link
Author

Following a discussion with @justmoon

  • Put all events on document instead of window
  • Remove webmonetizationload event
  • Add document.webMonetizationState, which can be in pending or started. If web monetization is not supported, it will be undefined.
  • Add webmonetizationprogress event, which indicates when an ILP packet to the site has been fulfilled

@justmoon
Copy link
Member

Quick note on webMonetizationState, webmonetizationstart, ... vs monetizationState, monetizationstart, etc.:

When we had a single JavaScript class as the insertion point, it made sense to model the spec after WebSocket, WebAssembly, etc. and use window.WebMonetization. Now that we have an API that is more integrated, the web part feels a bit redundant. We say document.readyState, not webDocument.webReadyState and we say <meta name="author" ...> not <web-meta name="web-author" ...>

Put all events on document instead of window

To give some context: This made more sense to me because Web Monetization is now triggered by a meta-tag and meta-tags describe the document. So whereas previously it was a feature of the JavaScript environment (hence existing on window, it's now a feature of the HTML document.

@adrianhopebailie
Copy link
Collaborator

@justmoon @sharafian

After discussing this with an engineer on the Chrome team I have a suggestion that brings this closer to the PaymentRequest and PaymentHandler APIs so that there is a clearer path to integration with browsers in the long term.

The current proposal doesn't clearly separate the difference between the browser and the entity that is making the payments (wallet?) which I think is a problem.

First, some component needs to parse the meta tags, generate the correlation id, and provide an environment for the client-side code on the page to execute.

Second, some component needs to take the data parsed from the meta tags (the payment pointer) and the data generated by the first component (the correlation id) and start making payments.

While these two components COULD be the same thing, this seems unlikely since they are performing very different roles. Our goal should be for the parsing of the tags and integration into the DOM to be standardised and native to all browsers so that there is no need for extensions and polyfills.

On the other hand, sending payments is unlikely to ever be a function of the browser directly. Rather, users are likely to have a wallet or similar application that sends payments using the payment pointer parsed by the browser.

Fortunately, the PaymentRequest and PaymentHandler APIs already fit this model, and the PaymentRequest object even has a unique id that was added for the same reason as the correlation id.

My suggestion is that the flow is as follows:

=================================

  • The user visits a webpage.
  • The user's browser sets document.monetization to an empty Object.

This satisfies the feature detection requirement. i.e. If document.monetization is truthy then the browser supports monetization.

  • The user's browser looks for the Monetization tags. The <meta> tags MUST be...etc
  • The user's browser creates a new PaymentRequest object with the following PaymentMethodData argument:
{
	"supportedMethods": "monetization",
	"data": {
		"paymentPointer": "{{payment pointer parsed from meta tags}}"
	}
}
  • The user's browser sets document.monetization.request to the new PR object and initiates the processing steps that would normally be started when the client code calls document.monetization.request.show()

This helps detect if parsing succeeded. I.e. If document.monetization.request is truthy then the browser successfully parsed the tags.

  • The browser executes the PR API algorithms as described in the PR API spec with the exception that the browser does not show a payment sheet to the user and also does not require user interaction to succeed. Instead, the request is passed directly to the Payment Handler registered to handle monetization payments.

The payment process MAY be carried out by the browser but more likely it will be passed off to a Payment Handler. If it is handled by the browser Cookies and session information from the user's browser session MUST not be sent in any requests related to the payment.

The default behaviour of the browser SHOULD be to initiate the registered Payment Handler capable of performing monetization payments. The Payment Handler will execute the logic defined in the PH spec for emitting a new PaymentRequestEvent.

The event has a property paymentRequestId which contains the unique correlation id generated by the browser for this request and a property methodData which will contain an object of the form:

{
	"supportedMethods": "monetization",
	"data": {
		"paymentPointer": "{{payment pointer parsed from meta tags}}"
	}
}
  • The Payment Handler resolves the Payment Pointer and begins to pay.
    • On the SPSP query to resolve the Payment Pointer, the Payment Handler should send a Web-Monetization-Id header containing the value of the paymentRequestId. The server may use this to associate future requests by the user with their payments.
    • With the destinationAccount and sharedSecret fetched from the SPSP query, a STREAM connection is established. A single STREAM is opened on this connection, and a positive SendMax is set.
    • The user's agent SHOULD set their SendMax high enough that it is never reached, and make payment adjustments by limiting throughput.
  • Once the STREAM connection has fulfilled an ILP packet with a non-zero amount, the Payment Handler calls respondWith(). Payment SHOULD continue.

The Payment Handler API needs to be updated to accommodate a different response that is more akin to a signal that "monetization has started" as opposed to "payment has completed". This should result in events being emitted by the document.monetization.request object such as the start and progress events.

For now, the website can call document.monetization.request.show() at any time which will return a Promise that resolves once monetization has started. A better future API would be for the PaymentRequest object to emit two new events that are similar to monetizationstart and monetizationprogress. For now I have excluded the monetizationstart event from the flow in favour of calling document.monetization.request.show().

  • The document.monetization.request object emits a monetization event, corresponding to this first packet (and subsequent packets). If there are no listeners the event MAY NOT be emitted.

  • Payment continues until the user closes/leaves the page.

    • The Payment Handler MAY decide to stop/start payment, e.g. if the user is idle, backgrounds the page, or instructs the browser to do so.
    • The website MAY also terminate the STREAM (until another page load) by calling document.monetization.request.abort().
    • If the STREAM connection is severed, it will redo the SPSP query to the same Payment Pointer as before with the same Correlation ID. The user's agent MUST NOT re-process the tags.
    • Each time a packet with a nonzero amount is fulfilled, the document.monetization.request object emits a monetization event. The event's detail is an object containing the details of the packet.

=========================

As far as I can tell this flow provides all the same developer hooks but aligns with the existing APIs.

Some existing methods/properties are used as a means to an end but in future more appropriate solutions could be added to PR and PH APIs.

Importantly this could be polyfilled pretty easily by exposing an object at document.monetization.request that conforms to the PaymentRequest interface even if it is not a "real" PR object generated by the browser.

As an interim measure this may handle the payments directly (rather than invoking a Payment Handler) but this can be changed in future without breaking any website integrations.

@sharafian
Copy link
Author

After a conversation with @adrianhopebailie and @justmoon about this Payment Request based approach, we seem to have settled on a proposal that allows us to use Payment Handler while still keeping the declarative style currently laid out in this pull request.

The API will work with a meta tag, but we'll also specify in this document how it can be translated into a Payment Request with a method of webmonetization. In the future, browsers implementing the Web Monetization standard will detect the <meta> tags, create a PaymentRequest based on their contents, and pass that PaymentRequest to the webmonetization PaymentHandler to process the payment. For current implementations the plan is still to use an extension, but we will pursue Web Monetization with this PaymentHandler based flow as a browser standard.

Additions that would have to be made to the PaymentHandler spec are the ability to get progress events on an ongoing payment, and the ability for the browser to invoke a PaymentHandler with no user interaction.

@adrianhopebailie Does this sound good, based on what we discussed?

- An ILP/STREAM connection object is returned from the function for the site to use.
- When the page wants to use the ILP/STREAM connection, they use the javascript STREAM API to send money and/or data. The browser sends outgoing ILP packets to the handler iframe using `postMessage`. The handler iframe forwards incoming packets by calling `window.parent.postMessage`.
- The user's agent sets `document.monetizationState` to `pending`.
- The user's agent looks for the Web Monetization `<meta>` tags ([specified below](#meta-tags)). The `<meta>` tags MUST be present once `document.readyState` is `interactive`. Implementations MUST NOT process the tags earlier than this, but MAY wait longer before processing.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it would simpler to just say that you can't dynamically insert the meta tag.

---

# Web Monetization

Web Monetization is a proposed browser API that uses ILP micropayments to monetize a site. It can be provided through a polyfill or an extension, but the goal is to eventually implement it directly into the browser.
Web Monetization is a proposed user's agent API that uses ILP micropayments to monetize a site. It can be provided through a polyfill or an extension, but the goal is to eventually implement it directly into the user's agent.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should remove this design goal or explain why you are deliberately not meeting it:

  • Should work on mobile and desktop without requiring an extension or special browser.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

addressed

- The user's agent will generate a fresh UUID (version 4) and use this as the Correlation ID from this point forward. **This Correlation ID MUST be unique per page load**, not per browser, session nor site.
- The user's agent resolves the payment pointer and begins to pay. The payment process MAY be carried out from a different machine acting as the user's agent. Cookies and session information MUST not be carried with any requests made to resolve the Payment Pointer, with the exception of the Correlation ID.
- On the SPSP query to resolve the Payment Pointer, a `Web-Monetization-Id` header is sent, containing the Correlation ID. The server may use this to associate future requests by the user with their payments.
- With the `destinationAccount` and `sharedSecret` fetched from the SPSP query, a STREAM connection is established. A single STREAM is opened on this connection, and a positive SendMax is set.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be snake case?


### ILP Connection Class
If the `<meta>` tag exists inside of an iframe, the iframe MUST have `data-allowmonetization` as an attribute.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some HTML sanitizers may be configured to allow data-*, so it's dangerous to make a security-sensitive property starting with data-*. I think the best way would be to use the proper way allow="monetization". In the short term, our extension is going to be able to parse the allow attribute even though it can't perfectly simulate other aspects of the feature policy API, e.g. headers. But if we are going to have websites add these codes let's give them something that can be stable long-term.

| `error` | `Event.error: Error` | When an error occurs on this ILP connection. |
| Name | Required? | Format | Description |
|:--|:--|:--|:--|
| `monetization:paymentpointer` | Yes | [Payment Pointer](../0026-payment-pointers/0026-payment-pointers.md) | The Payment Pointer that the user's agent will pay. |
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that we only have one tag, maybe we can just drop the :paymentpointer.

@@ -64,21 +63,21 @@ This flow refers to the user's agent: in implementation this may be done by an e

This `<meta>` tags MUST be in the document's `<head>`. The `<meta>` tags allows the user's agent to pay a site via Web Monetization by specifying a [Payment Pointer](../0026-payment-pointers/0026-payment-pointers.md).

If the `<meta>` tag exists inside of an iframe, the iframe MUST have `data-allowmonetization` as an attribute.
If the `<meta>` tag exists inside of an iframe, the iframe MUST contain `monetization` as one of the items in its `allow` attribute, e.g. `allow="monetization"`.

The `name` of the `<meta>` tags all start with `monetization:`. The table below lists the different `name`s and the formats of their `content`. Currently there is only one tag, but this may be expanded in the future.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove the colon, i.e. should be:

The name of the <meta> tags all start with monetization.

@@ -40,14 +39,14 @@ This flow refers to the user's agent: in implementation this may be done by an e

- The user visits a webpage.
- The user's agent sets `document.monetizationState` to `pending`.
- The user's agent looks for the Web Monetization `<meta>` tags ([specified below](#meta-tags)). The `<meta>` tags MUST be present once `document.readyState` is `interactive`. Implementations MUST NOT process the tags earlier than this, but MAY wait longer before processing.
- The user's agent looks for the Web Monetization `<meta>` tags ([specified below](#meta-tags)). The `<meta>` tags MUST NOT be inserted dynamically on the client-side.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slight nit-pick: Is there any way to insert something dynamically other than by using JavaScript? If not, maybe this should read "dynamically using client-side JavaScript" to be super clear.

@sharafian
Copy link
Author

@adrianhopebailie @justmoon

Added a "Payment Handler Flow" section, as per our earlier conversation

Copy link
Collaborator

@adrianhopebailie adrianhopebailie left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good!

I can start putting together a payment method specification for 'webmonetization' as soon as you think the flow is stable.

That spec is what browsers will work from and we'll take it through the W3C standards track.

@@ -30,271 +29,142 @@ Web Monetization is a proposed browser API that uses ILP micropayments to moneti

The reason this is not using the W3C Web Payments API is that Web Monetization is intended for continuous payments rather than discrete payments. It is also not designed to have any user interaction. The idea is to provide a direct alternative to advertisements, rather than an alternative to existing checkout methods.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested replacement for this paragraph:

The W3C have published two payments related APIs for browsers, the Payment Request API and the Payment Handler API.

The reason this API is not using the Payment Request API directly is that Web Monetization is intended for continuous payments rather than discrete payments. It is also not designed to have any user interaction. It is intended to provide a direct alternative to advertisements, rather than an alternative to existing checkout methods.

Some changes will be required to Payment Request and Payment Handler to fully support Web Monetization in future, however this API brings the necessary features to the browser in a way that allows for tighter integration in the future.

This flow refers to the user's **browser** and the user's **provider**, [defined above](#terminology).

- The user navigates their browser to a webpage.
- The browser sets `document.monetizationState` to `pending`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe use document.monetization.state?

Motivation being that adding a single monetization property to document is a less additional API surface.


#### Events
- The browser calls `.show()` on this PaymentRequest, triggering the [PaymentHandler](https://www.w3.org/TR/payment-handler/) for `webmonetization`. This PaymentHandler is how the browser communicates to the provider.
- The return value of `.show()` MUST return a Promise, and must also implement the [EventTarget](https://developer.mozilla.org/en-US/docs/Web/API/EventTarget) interface. The provider will emit [MonetizationStart](#monetizationstart) and [MonetizationProgress](#monetizationprogress) events from this Promise to communicate to the browser when payment occurs. The Promise MUST NOT resolve, because Web Monetization continues for the entire lifetime of the page. The Promise MAY reject if there is an error preventing the provider from paying and no retries will occur.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the return value of show() a Promise or something that implements the Promise interface?
Does it also implement the EventTarget interface?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, it says it must implement the EventTarget interface. I don't see anywhere that documents a promise interface in the browser.


### Payment Handler Flow

When the browser uses an extension to implement Web Monetization, Payment Handlers MAY NOT be used. Otherwise communication to the user's Web Monetization provider SHOULD be implemented with this flow.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When the browser uses an extension to implement Web Monetization, Payment Handlers MAY NOT be used

What's the motivation here? Maybe this could be:

"A provider can be implemented as a Payment Handler supporting the 'webmonetization' payment method. (The payment method specification for this payment method is still under development.)"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants