@@ -28,6 +28,7 @@ normative:
2828 RFC4648 :
2929 RFC8174 :
3030 RFC8259 :
31+ RFC8785 :
3132 I-D.httpauth-payment :
3233 title : " The 'Payment' HTTP Authentication Scheme"
3334 target : https://datatracker.ietf.org/doc/draft-ietf-httpauth-payment/
@@ -129,7 +130,9 @@ Fee Payer
129130# Request Schema
130131
131132The ` request ` parameter in the ` WWW-Authenticate ` challenge contains a
132- base64url-encoded JSON object.
133+ base64url-encoded JSON object. The JSON MUST be serialized using JSON
134+ Canonicalization Scheme (JCS) {{RFC8785}} before base64url encoding,
135+ per {{I-D.httpauth-payment}}.
133136
134137## Shared Fields
135138
@@ -138,6 +141,8 @@ base64url-encoded JSON object.
138141| ` amount ` | string | REQUIRED | Amount in base units (stringified number) |
139142| ` currency ` | string | REQUIRED | TIP-20 token address (e.g., ` "0x20c0..." ` ) |
140143| ` recipient ` | string | REQUIRED | Recipient address |
144+ | ` description ` | string | OPTIONAL | Human-readable payment description |
145+ | ` externalId ` | string | OPTIONAL | Merchant's reference (order ID, invoice number, etc.) |
141146
142147Challenge expiry is conveyed by the ` expires ` auth-param in
143148` WWW-Authenticate ` per {{I-D.httpauth-payment}}.
@@ -148,6 +153,7 @@ Challenge expiry is conveyed by the `expires` auth-param in
148153| -------| ------| ----------| -------------|
149154| ` methodDetails.chainId ` | number | OPTIONAL | Tempo chain ID (default: 42431) |
150155| ` methodDetails.feePayer ` | boolean | OPTIONAL | If ` true ` , server pays transaction fees (default: ` false ` ) |
156+ | ` methodDetails.memo ` | string | OPTIONAL | A ` bytes32 ` hex value. When present, the client MUST use ` transferWithMemo ` instead of ` transfer ` . |
151157
152158** Example:**
153159
@@ -376,6 +382,22 @@ the transaction. The server verifies the transaction onchain:
376382- Cannot be used with ` feePayer: true ` (client must pay their own fees)
377383- Server cannot modify or enhance the transaction
378384
385+ ## Transaction Verification {#transaction-verification}
386+
387+ Before broadcasting a transaction credential, servers MUST verify:
388+
389+ 1 . Deserialize the RLP-encoded transaction from ` payload.signature `
390+ 2 . Verify the transaction contains a ` transfer(recipient, amount) ` or
391+ ` transferWithMemo(recipient, amount, memo) ` call matching the challenge request
392+ 3 . Verify the call target matches the ` currency ` token address
393+ 4 . Verify the ` amount ` matches the challenge request amount
394+ 5 . Verify the ` recipient ` matches the challenge request recipient
395+ 6 . If ` methodDetails.memo ` is present, verify the transaction uses
396+ ` transferWithMemo ` with the matching memo value
397+ For hash credentials, servers MUST fetch the transaction receipt and
398+ verify the emitted ` Transfer ` or ` TransferWithMemo ` event logs match
399+ the challenge parameters.
400+
379401## Receipt Generation
380402
381403Upon successful settlement, servers MUST return a ` Payment-Receipt ` header
@@ -391,6 +413,7 @@ The receipt payload for Tempo charge:
391413| ` reference ` | string | Transaction hash of the settlement transaction |
392414| ` status ` | string | ` "success" ` |
393415| ` timestamp ` | string | {{RFC3339}} settlement time |
416+ | ` externalId ` | string | OPTIONAL. Echoed from the challenge request |
394417
395418# Security Considerations
396419
@@ -448,6 +471,22 @@ Intents" registry established by {{I-D.httpauth-payment}}:
448471
449472--- back
450473
474+ # ABNF Collected
475+
476+ ~~~ abnf
477+ tempo-charge-challenge = "Payment" 1*SP
478+ "id=" quoted-string ","
479+ "realm=" quoted-string ","
480+ "method=" DQUOTE "tempo" DQUOTE ","
481+ "intent=" DQUOTE "charge" DQUOTE ","
482+ "request=" base64url-nopad
483+
484+ tempo-charge-credential = "Payment" 1*SP base64url-nopad
485+
486+ ; Base64url encoding without padding per RFC 4648 Section 5
487+ base64url-nopad = 1*( ALPHA / DIGIT / "-" / "_" )
488+ ~~~
489+
451490# Example
452491
453492** Challenge:**
@@ -460,6 +499,7 @@ WWW-Authenticate: Payment id="kM9xPqWvT2nJrHsY4aDfEb",
460499 intent="charge",
461500 request="eyJhbW91bnQiOiIxMDAwMDAwIiwiY3VycmVuY3kiOiIweDIwYzAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAiLCJyZWNpcGllbnQiOiIweDc0MmQzNUNjNjYzNEMwNTMyOTI1YTNiODQ0QmM5ZTc1OTVmOGZFMDAiLCJtZXRob2REZXRhaWxzIjp7ImNoYWluSWQiOjQyNDMxfX0",
462501 expires="2025-01-06T12:00:00Z"
502+ Cache-Control: no-store
463503~~~
464504
465505The ` request ` decodes to:
0 commit comments