The core protocol makes no assumptions about how payments work. It defines the HTTP mechanics; payment methods define the semantics.
Any payment method specific details should only live in the Methods layer, outside of examples provided in the context of new Intents.
✓ "The credential field contains method-specific authorization data"
✗ "The credential field contains a signed transaction"Protocol mechanics are separate from the evolving payment ecosystems:
Core: HTTP 402 semantics, headers, registries (rarely changes)Intents: Abstract payment patterns like charge, authorize, subscription (occasionally extended)Methods: Concrete implementations for specific networks (frequently added)Extensions: Optional protocol additions (as needed)
The core spec should contain only what's necessary for interoperability. Push complexity to method specs where it belongs.
Require explicit declaration of payment requirements. Servers must advertise; clients must consent.
When in doubt, deny access. Invalid credentials, expired challenges, and verification failures all result in 402.
All specificication should adhere to the standard IETF format and style guide ref.
Use RFC 2119 keywords precisely:
| Keyword | Meaning |
|---|---|
| MUST | Absolute requirement |
| MUST NOT | Absolute prohibition |
| SHOULD | Recommended, but valid reasons to ignore may exist |
| SHOULD NOT | Discouraged, but valid reasons to do it may exist |
| MAY | Truly optional |
Following IETF guidelines, ever spec should follow the below structure:
1. Abstract - What this document does (2-3 sentences)
2. Introduction - Context and motivation
3. Requirements - RFC 2119 boilerplate
4. Terminology - Define terms used
5. [Technical body] - The actual specification
6. Security - Security considerations (never empty)
7. IANA - Registry updates
8. References - Normative and informativeDefine terms on first use. Use consistent terminology:
| Term | Definition |
|---|---|
| Challenge | A WWW-Authenticate header with scheme "Payment" |
| Credential | An Authorization header with scheme "Payment" |
| Intent | What kind of payment (charge, authorize, subscription) |
| Method | How payment works (tempo, stripe, lightning) |
| Receipt | Server acknowledgment of successful payment |
Include examples for every non-trivial concept. Use realistic but obviously fake values:
HTTP/1.1 402 Payment Required
WWW-Authenticate: Payment id="pay_abc123",
method="tempo",
intent="charge",
request="eyJ..."Prefer stable references over hardcoded section numbers.
- For internal references, use labels/anchors (e.g.,
{{payment-receipt-header}}). - For external drafts, prefer section-agnostic wording like "defined in {{I-D.httpauth-payment}}".
- Avoid
Section X.Y of {{I-D...}}unless there is no practical alternative.
Never leave this section empty. Address at minimum:
- Authentication/authorization boundaries
- Replay protection / idempotency
- Information disclosure
- Denial of service vectors
Use 2-space indentation, no trailing commas:
{
"amount": "1.00",
"currency": "USD"
}Keep lines under 72 characters in the markdown source for proper RFC rendering.
specs/
├── core/ # The Payment scheme itself
├── intents/ # Payment patterns (charge, authorize, etc.)
├── methods/ # Network implementations (tempo, stripe, etc.)
└── extensions/ # Optional features (discovery, etc.)Each directory contains specs at the same abstraction level. Cross-references should flow downward: core → intents → methods.
The Payment scheme uses a two-layer versioning strategy aligned with the layered architecture above.
The Payment scheme name is the stable anchor. The core
does NOT carry a version identifier on the wire.
No deployed HTTP authentication scheme uses a version
parameter (Basic, Bearer, Digest are all
unversioned). Evolution happens through:
- Adding optional challenge parameters (peers MUST ignore unknown parameters)
- Adding optional credential fields (peers MUST ignore unknown fields)
- Publishing new RFCs that Update or Obsolete the original
If a future change is truly incompatible with the core
wire format, register a new scheme name (e.g., Payment2).
Prior art: HTTP auth schemes (RFC 7617, RFC 6750, RFC 7616), OAuth 2.0 (RFC 6749), JOSE/JWT (RFC 7515-7519).
Methods are identified by strings in the IANA Payment
Methods Registry (e.g., tempo, x402, stripe).
Method specs MAY include a version field in their
methodDetails. The absence of a version field is
implicitly version 1:
{
"chainId": 42431,
"feePayer": true
}When a breaking change is needed, the method spec adds
a version field starting at 2:
{
"version": 2,
"chainId": 42431,
"feePayer": true
}- Compatible changes (adding optional fields, defining defaults): made in-place, same version.
- Breaking changes (removing required fields, changing
semantics): add or increment
version.
Methods MAY also register a new identifier (e.g.,
tempo-v2) for changes fundamental enough to warrant a
distinct name, but this is not required.
Intents (charge, authorize, session, etc.) do not
carry their own version. They evolve through the same
compatibility rules as the core protocol:
- Adding optional fields with defined defaults is always compatible
- New intent types are registered as new identifiers
- Breaking changes to an existing intent's semantics
require a new intent identifier (e.g.,
charge-v2)
This keeps intent schemas simple and avoids version negotiation complexity in the request blob.
All layers follow the same rule:
Implementations MUST ignore unknown fields in challenges, credentials, request objects, and receipts.
This is the primary mechanism for forward compatibility and enables most evolution without version changes.
| Layer | Versioning | Breaking Change |
|---|---|---|
| Core | None (stable scheme name) | New scheme (Payment2) |
| Methods | Optional methodDetails.version (absent = v1) |
Add/increment version |
| Intents | None (stable intent identifier) | New identifier (charge-v2) |