Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@
title: "Open Payments Auth Server",
href: "https://openpayments.dev/apis/auth-server/",
},
oprs: {
title: "Open Payments Resource Server",
href: "https://openpayments.dev/apis/resource-server/",
},
GNAP: {
title: "GNAP",
href: "https://datatracker.ietf.org/doc/html/draft-ietf-gnap-core-protocol",
Expand Down Expand Up @@ -99,6 +103,7 @@
<div data-include="section/storage.html" data-include-replace="true"></div>
<div data-include="section/authentication.html" data-include-replace="true"></div>
<div data-include="section/wallet-setup.html" data-include-replace="true"></div>
<div data-include="section/status.html" data-include-replace="true"></div>
<div data-include="section/session-flow.html" data-include-replace="true"></div>
<div data-include="section/algorithms.html" data-include-replace="true"></div>
<div data-include="section/dictionaries.html" data-include-replace="true"></div>
Expand Down
30 changes: 30 additions & 0 deletions section/dictionaries.html
Original file line number Diff line number Diff line change
Expand Up @@ -292,4 +292,34 @@ <h4>Enums</h4>
};
</pre>
</section>

<section>
<h3>Session Dictionaries</h3>
<p>These dictionaries define internal session records used by the user agent to track active monetization sessions.</p>

<section data-dfn-for="Session">
<h4>The `Session` dictionary</h4>
<p>
The {{Session}} dictionary represents an active monetization session. This is an internal user agent
structure maintained in the [=active sessions list=] and is not exposed to JavaScript.
</p>
<pre class="idl">
dictionary Session {
required HTMLLinkElement linkElement;
required WalletAddressDetails walletDetails;
required DOMString incomingPaymentId;
};
</pre>
<dl>
<dt><dfn>`linkElement`</dfn></dt>
<dd>The {{HTMLLinkElement}} with <code>rel="monetization"</code> that initiated this session.</dd>

<dt><dfn>`walletDetails`</dfn></dt>
<dd>The {{WalletAddressDetails}} obtained from the wallet address discovery request.</dd>

<dt><dfn>`incomingPaymentId`</dfn></dt>
<dd>The incoming payment identifier (payment bucket URI) created for receiving funds during this session.</dd>
</dl>
</section>
</section>
</section>
228 changes: 194 additions & 34 deletions section/session-flow.html
Original file line number Diff line number Diff line change
@@ -1,54 +1,214 @@
<section id="session-flow">
<h2>Session Flow</h2>
<h2>Session flow</h2>

<p>
This section describes the payment session flow that occurs when a user visits a [=web monetized website=].
The session flow involves establishing a payment session, managing ongoing payments, and handling session lifecycle events.
</p>

<section>
<h3>Session lifecycle</h3>
<p>
This section defines when sessions are created and ended. It specifies the triggers that invoke the
[[[#session-initiation]]] and [[[#session-termination]]] algorithms defined later in this section.
</p>

<p>
When a {{Document}} becomes [=Document/fully active=], the user agent MUST
[=initiate document-wide sessions=].
If a {{Document}} becomes not [=Document/fully active=], the user agent MUST
[=end sessions for document=] for it and all of its [=child navigables=].
</p>

<p>
This section describes the payment session flow that occurs when a user visits a [=web monetized website=].
The session flow involves establishing a payment session, managing ongoing payments, and handling session lifecycle events.
When the user starts interacting with a [=media element=], the user agent SHOULD
Copy link
Member

Choose a reason for hiding this comment

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

We would eventually need to define what counts as "interacting".
For video and audio elements, mostly simple when on tab. Otherwise, with video, we've picture-in-picture; and with audio, we've background audio.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

agreed, shall I file an issue for this?

[=initiate media-scoped sessions=] given that element; when the user stops interacting, the user agent
SHOULD [=end sessions scoped to=] that element.
</p>

<h3>Session Flow Overview</h3>
<p>
When a user navigates to a web monetized website, the browser initiates a payment session flow that includes:
If any of the conditions defined in the
<a href="https://webmonetization.org/specification/#link-type-monetization">link fetch and processing timing</a>
cause a monetization link element to be reprocessed, the user agent MUST [=end session=] for the session
associated with that element (if any) and then [=initiate a session=] for that element.
</p>
<ul>
<li>Discovery of payment endpoints.</li>
<li>Session establishment.</li>
<li>Payment streaming.</li>
<li>Session management and cleanup.</li>
</ul>

<h3>Session Initiation</h3>
<p>
The payment session begins when the browser detects a monetization link element on a webpage.
The browser must validate the [=wallet address=] and establish a connection with the configured wallet.
The user agent maintains an [=active sessions list=] to track all currently running monetization sessions.
Each session in this list corresponds to a monetization link element and contains the information needed
to distribute payments to the website's wallet.
</p>

<p>To initiate a payment session, given a <var>walletAddress</var>:</p>
<ol>
<li>Validate the [=wallet address=] format.</li>
<li>Check if a wallet is configured and available.</li>
<li>Establish a payment session with the wallet.</li>
<li>Begin payment streaming to the [=wallet address=].</li>
</ol>
<dl>
<dt><dfn>active sessions list</dfn></dt>
<dd>
A user agent maintained ordered list of {{Session}} records created by [=initiate a session=].
</dd>
</dl>
</section>

<h3>Payment Streaming</h3>
<section>
<h3>Session initiation</h3>
<p>
Once a session is established, the browser manages the ongoing payment stream according to the configured budget and rate limits.
This section defines the algorithms for initiating payment sessions at various scopes—document-wide,
media-scoped, or for individual monetization link elements. Session initiation discovers website wallet details,
negotiates authorization, creates an incoming payment, and registers a session record in the [=active sessions list=].
</p>

<h3>Session Lifecycle Management</h3>
<div class="algorithm">
<p>To <dfn>initiate document-wide sessions</dfn> given a |document:Document|, run these steps:</p>
<ol>
<li>If the result of [=get user wallet=] is null, then return.</li>
<li>[=initiate sessions scoped to=] the [^head^] element of |document|.</li>
<li>For each [=child navigable=] of |document|, recursively [=initiate document-wide sessions=].</li>
</ol>
</div>

<div class="algorithm">
<p>To <dfn>initiate media-scoped sessions</dfn> given a |media:HTMLElement|, run these steps:</p>
<ol>
<li>If the result of [=get user wallet=] is null, then return.</li>
<li>[=initiate sessions scoped to=] |media|.</li>
</ol>
</div>

<div class="algorithm">
<p>To <dfn>initiate sessions scoped to</dfn> the |root:HTMLElement|, run these steps:</p>
<ol>
<li>Let |document:Document| be the {{Node/ownerDocument}} of |root|.</li>
<li>If [=document monetization disabled=] given |document|, then return.</li>
<li>Let |linkElements:NodeList| be all [^link^] elements with `rel="monetization"` that are descendants of |root|.</li>
Copy link
Member

Choose a reason for hiding this comment

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

Do we include disabled elements here? Or handle them in "initiate a session"?
Maybe we should have an algorithm "get link elements".

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

it looks like initiate a session alg is the right place
not sure about specific "get link elements" alg because technically "initiate sessions scoped to" is that one

<li>For each |linkElement:HTMLLinkElement| in |linkElements|, [=initiate a session=] given |linkElement|.</li>
</ol>
</div>

<div class="algorithm">
<p>To <dfn>initiate a session</dfn>, given |linkElement:HTMLLinkElement|, perform the following steps:</p>
<ol>
<li>If |linkElement| has [^link/disabled^] attribute, then return.</li>
<li>
Let |websiteWalletAddress:DOMString| be the value of the {{HTMLLinkElement/href}} of |linkElement|.
</li>
<li>
If [=wallet monetization restricted=] given |document| and |websiteWalletAddress|, then return.
</li>
<li>
Let |websiteWalletDetails:WalletAddressDetails?| be the result of [=send a wallet address request=] with |websiteWalletAddress|.
</li>
<li>
If |websiteWalletDetails| is null, fire an `error` event on |linkElement| and return.
Copy link
Member

Choose a reason for hiding this comment

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

At what stage CSP is handled? Will a CSP error also result in error event on linkElement?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

It's part of [=wallet monetization restricted=] of the "initiate a session" alg. Not quite sure if we should keep silent or send an error. Do you want me to send an issue on this one?

Copy link
Member

Choose a reason for hiding this comment

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

Let's keep the same behavior as with other link tags.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

apparently it varies from browser to browser, it seems all are agree that error is reported in console such as "Content-Security-Policy: The page’s settings blocked a style (style-src-elem) at https://example.com/blocked.css from being applied because it violates the following directive: “style-src 'self'”, but nothing else in Firefox. Chromium fires error event. It's weird but apparently it's implementation dependent.

Do you think we should reflect that somehow in the spec?

Copy link
Member

Choose a reason for hiding this comment

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

I'd rather file an interop bug and see what browsers agree on. But adding a note here would be good as well.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'd rather file an interop bug and see what browsers agree on. But adding a note here would be good as well.

right, do you know what's a typical procedure for this (cc'ing @lukewarlow )

</li>
<li>
Fire a `load` event on |linkElement|.
</li>
<li>
Let |incomingPaymentGrant:Grant?| be the result of [=send an incoming payment grant request=] with |websiteWalletDetails|.
</li>
<li>
If |incomingPaymentGrant| is null, return.
</li>
<li>
Let |incomingPaymentId:DOMString?| be the result of [=send a create incoming payment request=] with |websiteWalletDetails|, and |incomingPaymentGrant|.
</li>
<li>
If |incomingPaymentId| is null, return.
</li>
<li>
Let |continueUri:DOMString| be |incomingPaymentGrant|.{{Grant/continue}}.{{Continue/uri}}.
</li>
<li>
Let |accessToken:DOMString| be |incomingPaymentGrant|.{{Grant/continue}}.{{Continue/access_token}}.{{ContinueAccessToken/value}}.
</li>
<li>
[=Send a cancel grant request=] with |continueUri| and |accessToken|.
<p class="note">The incoming payment grant is no longer needed after the payment bucket has been created. Canceling it ensures proper cleanup and revokes the authorization. Implementations should handle cancellation failures gracefully but can proceed with the session even if cancellation fails, as the grant will expire naturally.</p>
</li>
<li>
Let |session:Session| be a new {{Session}} with:
<ul>
<li>{{Session/linkElement}} set to |linkElement|</li>
<li>{{Session/walletDetails}} set to |websiteWalletDetails|</li>
<li>{{Session/incomingPaymentId}} set to |incomingPaymentId|</li>
</ul>
</li>
<li>
Append |session| to the [=active sessions list=].
</li>
<li>
If this is the first active session, [=start payment streaming loop=].
</li>
</ol>
</div>

<aside class="note" title="Session initiation summary">
<p>The main [=initiate a session=] algorithm performs these key steps:</p>
<ol>
<li><strong>Preconditions:</strong> Verify user wallet exists and monetization is permitted</li>
<li><strong>Discovery:</strong> Request website wallet address details,
firing `load` event on success or `error` event on failure</li>
<li><strong>Authorization:</strong> Obtain an incoming payment grant to create payments on the website's wallet</li>
<li><strong>Payment setup:</strong> Create an incoming payment (payment bucket) on the website's wallet resource server</li>
<li><strong>Cleanup:</strong> Cancel the temporary incoming payment grant (no longer needed)</li>
<li><strong>Registration:</strong> Add the session to the active sessions list and
start the payment streaming loop if this is not yet started</li>
</ol>
</aside>
</section>

<section>
<h3>Session termination</h3>
<p>
The browser must handle various session lifecycle events including:
This section defines the algorithms for ending payment sessions at various scopes—document-wide,
media-scoped, or for individual monetization link elements. Session termination removes session records
from the [=active sessions list=] and cancels scheduled payment work.
</p>
<ul>
<li>Session pause and resume.</li>
<li>Tab focus changes.</li>
<li>Page navigation.</li>
<li>Wallet disconnection.</li>
<li>Budget exhaustion.</li>
</ul>

<h3>Error Handling</h3>
<div class="algorithm">
<p>To <dfn>end sessions for document</dfn>, given a |document:Document|, run these steps:</p>
<ol>
<li>[=End sessions scoped to=] the [^head^] element of |document|.</li>
<li>For each [=child navigable=] of |document|, recursively [=end sessions for document=].</li>
</ol>
</div>

<div class="algorithm">
<p>To <dfn>end sessions scoped to</dfn> the |root:HTMLElement|, run these steps:</p>
<ol>
<li>Let |linkElements:NodeList| be all [^link^] elements with `rel="monetization"` that are descendants of |root|.</li>
<li>For each |linkElement:HTMLLinkElement| in |linkElements|:
<ol>
<li>Let |session:Session| be the session record in the [=active sessions list=] whose {{HTMLLinkElement}} is |linkElement|, or null if none exists.</li>
<li>If |session| is not null, [=end session=] given |session|.</li>
</ol>
</li>
</ol>
</div>

<div class="algorithm">
<p>To <dfn>end session</dfn>, given a session record |session:Session|, run these steps:</p>
<ol>
<li>Remove |session| from the [=active sessions list=].</li>
<li>The user agent MUST cancel any scheduled payment work for |session|.</li>
</ol>
</div>
</section>

<section>
<h3>Payment streaming</h3>
<p>
Once a session is established, the browser manages the ongoing payment stream according to the configured budget and rate limits.
</p>

<div class="algorithm">
<p>To <dfn>start payment streaming loop</dfn>, run these steps:</p>
<ol>
<li>
</li>
</ol>
</div>

<p>
The session flow must gracefully handle various error conditions and provide appropriate feedback to both the website and the user.
The session flow must gracefully handle various error conditions and provide appropriate feedback to both the website and the user.
</p>
</section>
</section>
60 changes: 60 additions & 0 deletions section/status.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
<section id="monetization-status">
<h2>Monetization status</h2>

<p>
User agents SHOULD provide controls to disable Web Monetization completely and on a per-[=origin=]
basis via a wallet management interface. User agents SHOULD provide a user interface that allows
users to view and adjust these preferences.
</p>

<p>
A website can disable Web Monetization for itself and/or its subdocuments by setting
the <a href="https://webmonetization.org/developers/permissions-policy-monetization/">`monetization`</a>
[=policy-controlled feature=].
</p>

<p>
A website can also restrict Web Monetization to <a>wallet addresses</a> of certain [=origin=]s
by using the <a href="https://webmonetization.org/developers/csp-monetization-src/">`monetization-src`</a>
directive in the [=content security policy=].
</p>

<section id="monetization-is-disabled">
<h3>Document monetization restrictions</h3>

<p>
<dfn data-lt="document monetization disabled">Document monetization disabled</dfn>
given a |document:Document| if either of the following conditions apply:
</p>

<ul>
<li>
The user preferences specify that Web Monetization is either completely disabled or disabled specifically
for the [=Document/origin=] of the |document|.
</li>
<li>
The |document| is not [=allowed to use=]
the <a href="https://webmonetization.org/developers/permissions-policy-monetization/">`monetization`</a>
[=policy-controlled feature=].
</li>
</ul>
</section>

<section id="monetization-is-disabled-for-wallet">
<h3>Wallet address restrictions</h3>

<p>
<dfn data-lt="wallet monetization restricted">Wallet monetization restricted</dfn>
given a |document:Document| and a [=wallet address=] |walletAddress:DOMString|
if the <a href="https://webmonetization.org/developers/csp-monetization-src/">`monetization-src`</a> directive
in the [=content security policy=] of the |document| restricts the |walletAddress|'s [=origin=].
</p>
</section>

<p class="note">
These mechanisms provide multiple layers of control over Web Monetization: the user controls whether
monetization is enabled in their browser, the website can control whether monetization is allowed
via permissions policy, and the website can restrict which wallet addresses are allowed
via content security policy. All three mechanisms work together to ensure monetization only occurs when all parties consent.
</p>
</section>
2 changes: 1 addition & 1 deletion section/storage.html
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ <h3>Storage</h3>
</section>

<section>
<h3>Storage Operations</h3>
<h3>Storage operations</h3>

<div class="algorithm">
<p>To <dfn>get storage</dfn>:</p>
Expand Down