| Internet-Draft | Lightning Charge Intent | March 2026 |
| Zhang, et al. | Expires 19 September 2026 | [Page] |
This document defines the "charge" intent for the "lightning" payment method within the Payment HTTP Authentication Scheme [I-D.httpauth-payment]. The server issues a BOLT11 invoice as a challenge; the client pays it and proves payment by presenting the preimage as a credential.¶
This Internet-Draft is submitted in full conformance with the provisions of BCP 78 and BCP 79.¶
Internet-Drafts are working documents of the Internet Engineering Task Force (IETF). Note that other groups may also distribute working documents as Internet-Drafts. The list of current Internet-Drafts is at https://datatracker.ietf.org/drafts/current/.¶
Internet-Drafts are draft documents valid for a maximum of six months and may be updated, replaced, or obsoleted by other documents at any time. It is inappropriate to use Internet-Drafts as reference material or to cite them other than as "work in progress."¶
This Internet-Draft will expire on 19 September 2026.¶
Copyright (c) 2026 IETF Trust and the persons identified as the document authors. All rights reserved.¶
This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) in effect on the date of publication of this document. Please review these documents carefully, as they describe your rights and restrictions with respect to this document.¶
HTTP Payment Authentication [I-D.httpauth-payment] defines a challenge-response mechanism that gates access to resources behind micropayments. This document registers the "charge" intent for the "lightning" payment method.¶
The flow proceeds as follows:¶
Client Server Lightning Network
| | |
| (1) GET /resource | |
|--------------------------> | |
| | |
| | (2) Create invoice |
| |--------------------------> |
| | (3) invoice, hash |
| |<-------------------------- |
| | |
| (4) 402 Payment Required | |
| (invoice, hash) | |
|<-------------------------- | |
| | |
| (5) Pay invoice | |
|------------------------------------------------------> |
| (6) Preimage (HTLC) | |
|<------------------------------------------------------ |
| | |
| (7) GET /resource | |
| credential: preimage | |
|--------------------------> | |
| | |
| (8) 200 OK (resource) | |
|<-------------------------- | |
| | |
¶
This document inherits the shared request semantics of the "charge"
intent from [I-D.payment-intent-charge]. It defines only the
Lightning-specific methodDetails, payload, and settlement
procedures for the "lightning" payment method.¶
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.¶
A Lightning Network payment request encoded per the BOLT11 specification, containing a payment hash, amount, expiry, and optional routing hints.¶
A 32-byte SHA-256 hash of the payment preimage, encoded as a lowercase hex string. Embedded in the BOLT11 invoice and used by the server to verify payment.¶
A 32-byte random secret whose SHA-256 hash equals the payment hash. Revealed to the payer upon successful payment settlement; serves as proof of payment.¶
The intent identifier for this specification is "charge". It MUST be lowercase.¶
The "charge" intent represents a one-time payment gating access to a resource. The server generates a fresh BOLT11 invoice ([BOLT11]) per request. The client pays the invoice on the Lightning Network and presents the payment preimage as the credential. The server verifies the preimage cryptographically without contacting any external service.¶
All JSON [RFC8259] objects carried in auth-params or HTTP headers in this specification MUST be serialized using the JSON Canonicalization Scheme (JCS) [RFC8785] before encoding. JCS produces a deterministic byte sequence, which is required for any digest or signature operations defined by the base spec [I-D.httpauth-payment].¶
The resulting bytes MUST then be encoded using base64url
[RFC4648] Section 5 without padding characters
(=). Implementations MUST NOT append = padding
when encoding, and MUST accept input with or without padding when
decoding.¶
This encoding convention applies to: the request
auth-param in WWW-Authenticate, the credential token in
Authorization, and the receipt token in
Payment-Receipt.¶
The following fields are nested under methodDetails in
the request JSON. The BOLT11 invoice (methodDetails.invoice)
is the authoritative source for payment parameters. The
paymentHash and network fields are provided as
convenience to spare clients from decoding the invoice; if present,
they MUST exactly match the values encoded in the invoice. Servers
MUST verify this consistency before issuing the challenge. Clients
MUST decode and verify the invoice independently before paying,
and MUST reject challenges where the convenience fields do not
match the invoice.¶
REQUIRED. The full BOLT11-encoded payment request string (e.g., "lnbc100n1..."). This field is authoritative; all other payment parameters are derived from it.¶
OPTIONAL convenience field. The payment hash embedded in the
invoice, as a lowercase hex-encoded string. If present, MUST
equal the payment hash decoded from invoice.¶
OPTIONAL convenience field. Identifies the Lightning Network the invoice is issued on. MUST be one of "mainnet", "regtest", or "signet". If present, MUST match the network implied by the invoice's human-readable prefix. Defaults to "mainnet" if omitted. Clients SHOULD reject invoices whose network does not match their configured network.¶
The Authorization header carries a single base64url-encoded
JSON token (no auth-params). The decoded object contains two top-level
fields:¶
REQUIRED. An echo of the challenge auth-params from the
WWW-Authenticate header: id, realm,
method, intent, request, and
(if present) expires. This binds the credential to the
exact challenge that was issued.¶
OPTIONAL. A payer identifier string, as defined by [I-D.httpauth-payment]. The RECOMMENDED format is a Decentralized Identifier (DID) per [W3C-DID]. Lightning-specific implementations MAY omit this field; servers MUST NOT require it.¶
REQUIRED. A JSON object containing the Lightning-specific credential
fields. The single required field is preimage: the 32-byte
payment preimage revealed upon successful HTLC settlement, encoded
as a lowercase hex string.¶
Example (decoded):¶
{
"challenge": {
"id": "kM9xPqWvT2nJrHsY4aDfEb",
"realm": "api.example.com",
"method": "lightning",
"intent": "charge",
"request": "eyJ...",
"expires": "2026-03-15T12:05:00Z"
},
"source": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"payload": {
"preimage": "a3f1...e209"
}
}
¶
Upon receiving a request with a credential, the server MUST:¶
Decode the base64url credential and parse the JSON.¶
Verify that "preimage" is present and is a 64-character lowercase hex string.¶
Look up the stored challenge using credential.challenge.id.
Retrieve the paymentHash recorded when the challenge was
issued. If no matching challenge is found, reject the request.¶
Verify that all fields in credential.challenge exactly match
the stored challenge auth-params (e.g., id, realm, method,
intent, request, expires).¶
Decode the echoed credential.challenge.request and verify that
the amount and currency in it match the invoice parameters
stored with the challenge.¶
Compute sha256(hex_to_bytes(preimage)) and verify the result equals the stored paymentHash.¶
To prevent preimage replay across different resources or sessions,
the server MUST store the issued paymentHash keyed by the
challenge id assigned at challenge time. When the client
presents a credential, the server MUST verify
credential.challenge is an exact echo of the issued
challenge params and look up the stored paymentHash by
credential.challenge.id. A preimage that is valid for one
challenge MUST NOT be accepted for a different challenge.¶
Lightning Network settlement is synchronous from the payer's perspective: the preimage is only revealed after the HTLC resolves (see [BOLT4]). Settlement is therefore considered complete at the moment the server successfully verifies the preimage (step 6 of Section 9). No out-of-band confirmation is required.¶
The server MUST atomically mark the challenge as consumed and deliver the resource. Specifically, the challenge invalidation and the decision to return HTTP 200 MUST be treated as a single operation: a challenge that has been marked consumed MUST NOT be accepted again, even if the resource delivery subsequently fails. If resource delivery fails after the challenge is consumed, the server MUST return an appropriate HTTP error (e.g., 500) and MUST NOT reissue the same challenge. The client MUST treat such a response as a payment loss and MAY retry with a new payment. Unlike reversible payment methods, Lightning settlement is final once the preimage is revealed; the payment cannot be refunded through the payment channel.¶
Servers MUST include Cache-Control: no-store on all HTTP
402 responses. The challenge contains a single-use invoice; caching
it could cause clients to attempt to pay a stale or already-settled
invoice.¶
A challenge has two independent expiry signals: the expires
auth-param on the WWW-Authenticate header (defined by
[I-D.httpauth-payment]) and the expiry field
embedded in the BOLT11 invoice. The effective expiry of a
challenge is the earlier of the two.¶
Servers MUST NOT set the expires auth-param to a time
later than the invoice's BOLT11 expiry time. Clients SHOULD use
the earlier of the two values when deciding whether to attempt
payment. Servers MUST reject credentials for challenges whose
effective expiry has passed, regardless of which signal
triggered it.¶
The server MUST include a Payment-Receipt header in the 200 response with the following fields:¶
REQUIRED. The string "lightning".¶
REQUIRED. The challenge identifier (the id from the
WWW-Authenticate challenge) for audit and traceability
correlation.¶
REQUIRED. The payment hash (SHA-256 of the preimage) as a lowercase hex string. Serves as a globally unique, publicly shareable payment receipt identifier. The preimage MUST NOT be used here, as it is a bearer secret and its exposure in logs, analytics, or shared receipts would allow replay.¶
REQUIRED. The string "success".¶
Example (decoded):¶
{
"method": "lightning",
"challengeId": "kM9xPqWvT2nJrHsY4aDfEb",
"reference": "bc230847...",
"status": "success",
"timestamp": "2026-03-10T21:00:00Z"
}
¶
When rejecting a credential, the server MUST return HTTP 402 (Payment
Required) with a fresh WWW-Authenticate: Payment challenge per
[I-D.httpauth-payment]. The server SHOULD include a response body
conforming to RFC 9457 [RFC9457] Problem Details, with
Content-Type: application/problem+json. The following
problem types are defined for this intent:¶
HTTP 402. The credential token could not be decoded, the JSON
could not be parsed, or required fields (challenge,
payload, payload.preimage) are absent or have
the wrong type. A fresh challenge MUST be included in
WWW-Authenticate.¶
HTTP 402. The value of credential.challenge.id does not
match any challenge issued by this server, or the challenge has
already been consumed. A fresh challenge MUST be included in
WWW-Authenticate.¶
HTTP 402. SHA-256(payload.preimage) does not equal the
paymentHash stored for the identified challenge. A fresh
challenge MUST be included in WWW-Authenticate.¶
HTTP 402. The BOLT11 invoice associated with the challenge has
passed its expiry time, or the challenge expires
auth-param indicates the challenge has expired. A fresh
challenge MUST be included in WWW-Authenticate.¶
Example error response body:¶
{
"type": "https://paymentauth.org/problems/lightning/invalid-preimage",
"title": "Invalid Preimage",
"status": 402,
"detail": "SHA-256 of the provided preimage does not match the stored payment hash"
}
¶
Each BOLT11 invoice MUST use a freshly generated random payment hash. Reusing a payment hash allows a client who has previously paid to replay the credential indefinitely.¶
The server MUST verify that the amount encoded in the BOLT11 invoice matches the intended charge amount before issuing the challenge. Clients SHOULD independently decode and verify the invoice amount before paying.¶
BOLT11 invoices carry an expiry field (default 3600 seconds). Servers MUST NOT accept credentials for expired invoices. Servers MAY enforce a shorter expiry window to reduce the window in which a compromised preimage could be replayed.¶
The payment preimage MUST only be transmitted over HTTPS. Servers, clients, and intermediaries MUST NOT log, persist, or include preimages in error responses, analytics, or diagnostic output. Exposure of the preimage allows any party to present it as a valid credential until the challenge has been consumed or the invoice has expired. Servers MUST invalidate a challenge on first successful use to enforce consume-once semantics. The acceptance check and invalidation MUST be atomic: concurrent requests presenting the same valid preimage MUST result in exactly one success and one rejection, with no window in which both are accepted.¶
This document requests registration of the following entry in the "HTTP Payment Methods" registry established by [I-D.httpauth-payment]:¶
| Method Identifier | Description | Reference |
|---|---|---|
lightning
|
Lightning Network BOLT11 invoice payment | This document |
Contact: Lightspark (contact@lightspark.com)¶
This document requests registration of the following entry in the "HTTP Payment Intents" registry established by [I-D.httpauth-payment]:¶
| Intent | Applicable Methods | Description | Reference |
|---|---|---|---|
charge
|
lightning
|
One-time BOLT11 invoice payment gating access to a resource | This document |
GET /weather HTTP/1.1 Host: api.example.com HTTP/1.1 402 Payment Required WWW-Authenticate: Payment id="kM9xPqWvT2nJrHsY4aDfEb", realm="api.example.com", method="lightning", intent="charge", request="eyJhbW91bnQiOiIxMDAiLCJjdXJyZW5jeSI6IkJUQyIsImRlc2NyaXB0aW9uIjoiV2VhdGhlciByZXBvcnQgZm9yIDk0MTA3IiwibWV0aG9kRGV0YWlscyI6eyJpbnZvaWNlIjoibG5iYzF1MXAuLi4iLCJwYXltZW50SGFzaCI6ImJjMjMwODQ3Li4uIiwibmV0d29yayI6Im1haW5uZXQifX0", expires="2026-03-15T12:05:00Z" Cache-Control: no-store¶
Decoded request:¶
{
"amount": "100",
"currency": "sat",
"description": "Weather report for 94107",
"methodDetails": {
"invoice": "lnbc1u1p...",
"paymentHash": "bc230847...",
"network": "mainnet"
}
}
¶
GET /weather HTTP/1.1
Host: api.example.com
Authorization: Payment eyJjaGFsbGVuZ2UiOnsiaWQiOiJrTTl4UHFXdlQybkpySHNZNGFEZkViIiwicmVhbG0iOiJhcGkuZXhhbXBsZS5jb20iLCJtZXRob2QiOiJsaWdodG5pbmciLCJpbnRlbnQiOiJjaGFyZ2UiLCJyZXF1ZXN0IjoiZXlKLi4uIiwiZXhwaXJlcyI6IjIwMjYtMDMtMTVUMTI6MDU6MDBaIn0sInBheWxvYWQiOnsicHJlaW1hZ2UiOiJhM2YxLi4uZTIwOSJ9fQ
HTTP/1.1 200 OK
Payment-Receipt: eyJtZXRob2QiOiJsaWdodG5pbmciLCJyZWZlcmVuY2UiOiJhM2YxLi4uZTIwOSIsInN0YXR1cyI6InN1Y2Nlc3MiLCJ0aW1lc3RhbXAiOiIyMDI2LTAzLTEwVDIxOjAwOjAwWiJ9
Content-Type: application/json
{"temperature": 72, "condition": "sunny"}
¶
Decoded receipt:¶
{
"method": "lightning",
"challengeId": "kM9xPqWvT2nJrHsY4aDfEb",
"reference": "bc230847...",
"status": "success",
"timestamp": "2026-03-10T21:00:00Z"
}
¶
Decoded credential:¶
{
"challenge": {
"id": "kM9xPqWvT2nJrHsY4aDfEb",
"realm": "api.example.com",
"method": "lightning",
"intent": "charge",
"request": "eyJ...",
"expires": "2026-03-15T12:05:00Z"
},
"source": "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK",
"payload": {
"preimage": "a3f1...e209"
}
}
¶
The authors thank the Spark SDK team and the broader Lightning Network developer community. The authors also thank Brendan Ryan for his review of this document.¶