Skip to main content
The browser must never hold a secret key, and the browser must never be able to change where money goes. A checkout session is how both stay true.

What a session is

A session is issued server side, against a funding intent that already exists. It grants the ability to complete that one intent and nothing else.
The browser then constructs a client with the session token instead of a key:
One credential, one client. Passing both a key and a session token is an error, because which one authorised a call matters when one of them can move money.

What a session cannot do

The scoping is per intent, not per application. Scoping by application alone would let one customer’s session read another customer’s intent. Sessions expire, and expiry is part of the lookup rather than a check afterwards, so there is no path that finds a session and forgets to ask whether it is still good.

Handoff tokens

The hardest problem in mobile checkout is the jump from browser to wallet app and back. The session token must not make that trip: a deep link, a URL bar, and an app-switch log are all places where things get written down and kept. So there are two tokens.
The handoff token is short lived, single use, and the only thing that travels. A handoff seen twice is a handoff somebody else also saw, so the second attempt to spend one fails. Because it travels through a URL, assume it leaks. So it is bound to a secret your browser keeps and never sends: a code_verifier. You send its hash when minting, and the verifier itself when redeeming. This is required, not optional, because a binding an attacker can strip is not a binding.
On the way back, exchange it for a fresh session:
resume takes no API key. The handoff token and the verifier together are the credential, which is why the token is single use, expires in about a minute, and allows only three attempts before it is spent. A token lifted from a URL, a browser history, or an app-switch log is useless without the verifier, which never left the browser that asked for it.

Storage

Only hashes are stored, for both token kinds. A leaked table yields nothing usable, which is the same posture as API keys.
A session token is not a bearer token you should persist. Hold it in memory for the life of the checkout, and use a handoff when you need to survive a navigation.