Epoch milliseconds. Tokens without it are rejected.
OptionalidHost-app internal id (PaymentSession.id), also stamped into Adyen metadata.
OptionalmetadataHost metadata, forwarded to Adyen's metadata map and echoed on PaymentInfo.
OptionalreceiptShopper email (shopperEmail on the payment).
The reference sent on POST /payments — Adyen's merchant reference for the payment.
OptionalreturnWhere Adyen sends the shopper back from a redirect action.
Adyen is tokenize-first AND creates nothing at session time: the browser encrypts the card inside Adyen's hosted fields, and the payment object only comes into existence when the server's
completePaymentposts/payments. That call needs the amount, currency, reference and capture method — none of which exist anywhere at session time, because PayFanout persists nothing. SocreatePaymentSessionencodes them into thepspSessionIditself:base64url(json) + "." + base64url(hmac).The HMAC (sessionSigningKey) makes the context tamper-proof: the token round-trips through the browser, and without the signature a client could inflate or deflate the amount before server completion. The client adapter reads the payload half (amount/currency drive Adyen Web's own UI copy) without ever holding the key.
Every context carries an expiry (
expiresAt, epoch ms): a signed token must not stay completable forever. Enforced at decode time —completePaymentrejects expired tokens with code "session_expired" (hosts recover by creating a fresh session).Crypto is WebCrypto (async) so this module runs on edge runtimes too.