Documentation
¶
Index ¶
- Constants
- func Base64URLDecode(s string) ([]byte, error)
- func Base64URLEncode(data []byte) string
- func Canonicalize(v any) ([]byte, error)
- func CanonicalizeJSON(raw []byte) ([]byte, error)
- func ComputeChallengeID(secret []byte, params *ChallengeParams) string
- func DecodeRequest(encoded string, target any) error
- func EncodeRequest(v any) (string, error)
- func PaymentRequiredProblem() []byte
- func SetChallengeHeader(h http.Header, p *ChallengeParams)
- func SetReceiptHeader(h http.Header, r *Receipt) error
- func UseLogger(logger btclog.Logger)
- func VerifyChallengeID(secret []byte, params *ChallengeParams, id string) bool
- type ChallengeEcho
- type ChallengeParams
- type ChargeMethodDetails
- type ChargePayload
- type ChargeRequest
- type Credential
- type NeedTopUpEvent
- type ProblemDetails
- type Receipt
- type SessionAction
- type SessionPayload
- type SessionReceipt
- type SessionRequest
Constants ¶
const ( // ProblemBaseURI is the canonical base URI for Payment HTTP Auth // problem types per draft-httpauth-payment-00 Section 8.1. ProblemBaseURI = "https://paymentauth.org/problems/" // ProblemLightningBaseURI is the base URI for Lightning-specific // problem types. ProblemLightningBaseURI = ProblemBaseURI + "lightning/" // ProblemContentType is the MIME type for RFC 9457 Problem Details. ProblemContentType = "application/problem+json" )
const ( ProblemPaymentRequired = ProblemBaseURI + "payment-required" ProblemPaymentInsufficient = ProblemBaseURI + "payment-insufficient" ProblemPaymentExpired = ProblemBaseURI + "payment-expired" ProblemVerificationFailed = ProblemBaseURI + "verification-failed" ProblemMethodUnsupported = ProblemBaseURI + "method-unsupported" ProblemMalformedCredential = ProblemBaseURI + "malformed-credential" ProblemInvalidChallenge = ProblemBaseURI + "invalid-challenge" )
Well-known problem type URIs per draft-httpauth-payment-00 Section 8.2.
const ( ProblemLightningMalformed = ProblemLightningBaseURI + "malformed-credential" ProblemLightningUnknown = ProblemLightningBaseURI + "unknown-challenge" ProblemLightningPreimage = ProblemLightningBaseURI + "invalid-preimage" ProblemLightningExpired = ProblemLightningBaseURI + "expired-invoice" ProblemLightningSessionNotFound = ProblemLightningBaseURI + "session-not-found" ProblemLightningSessionClosed = ProblemLightningBaseURI + "session-closed" ProblemLightningInsufficient = ProblemLightningBaseURI + "insufficient-balance" ProblemLightningChallengeExpired = ProblemLightningBaseURI + "challenge-expired" ProblemLightningReturnInvoice = ProblemLightningBaseURI + "invalid-return-invoice" )
Lightning-specific problem type URIs per draft-lightning-charge-00 Section 11.
const ( // AuthScheme is the HTTP authentication scheme name for the Payment // protocol as defined in draft-httpauth-payment-00. AuthScheme = "Payment" // MethodLightning is the payment method identifier for Lightning // Network payments. MethodLightning = "lightning" // IntentCharge is the intent identifier for one-time charge payments // as defined in draft-lightning-charge-00. IntentCharge = "charge" // IntentSession is the intent identifier for prepaid session payments // as defined in draft-lightning-session-00. IntentSession = "session" // CurrencySat is the currency identifier for satoshis, the base unit // used for Lightning/Bitcoin amounts. CurrencySat = "sat" // HeaderPaymentReceipt is the HTTP header field name for payment // receipts returned on successful payment verification. HeaderPaymentReceipt = "Payment-Receipt" // ReceiptStatusSuccess is the only valid receipt status value. Receipts // are only issued on successful payment responses. ReceiptStatusSuccess = "success" )
const Subsystem = "MPAY"
Subsystem defines the sub system name of this package.
Variables ¶
This section is empty.
Functions ¶
func Base64URLDecode ¶
Base64URLDecode decodes a base64url-encoded string. It accepts input with or without padding per the spec requirement.
func Base64URLEncode ¶
Base64URLEncode encodes data using base64url encoding without padding per RFC 4648 Section 5, as required by the Payment HTTP Authentication Scheme.
func Canonicalize ¶
Canonicalize produces a JSON Canonicalization Scheme (JCS) output per RFC 8785 for the given value. JCS defines a deterministic JSON serialization that ensures identical logical values produce identical byte sequences.
This is a minimal implementation sufficient for the flat and simply-nested JSON objects used in the Payment HTTP Authentication Scheme. It handles:
- Sorting object keys lexicographically (by Unicode code point).
- No whitespace between tokens.
- Strings serialized with standard JSON escaping.
- Numbers serialized per the ES6 specification (JSON default for integers).
- Null, boolean values serialized as-is.
- Nested objects and arrays are recursed into.
func CanonicalizeJSON ¶
CanonicalizeJSON takes a raw JSON byte slice, unmarshals it into a generic representation, and re-serializes it using JCS canonicalization.
func ComputeChallengeID ¶
func ComputeChallengeID(secret []byte, params *ChallengeParams) string
ComputeChallengeID computes the HMAC-SHA256 challenge ID from challenge parameters using the 7-slot positional scheme defined in draft-httpauth-payment-00 Section 5.1.2.1.1.
The HMAC input is constructed from exactly seven fixed positional slots:
Slot 0: realm (required, string value)
Slot 1: method (required, string value)
Slot 2: intent (required, string value)
Slot 3: request (required, JCS-serialized then base64url-encoded)
Slot 4: expires (optional, string value or empty string if absent)
Slot 5: digest (optional, string value or empty string if absent)
Slot 6: opaque (optional, JCS-serialized then base64url-encoded, or
empty string if absent)
All seven slots are joined with the pipe character ("|") as delimiter. The result is the base64url-encoded (without padding) HMAC-SHA256 of the joined string.
func DecodeRequest ¶
DecodeRequest base64url-decodes and unmarshals a request parameter into the given target.
func EncodeRequest ¶
EncodeRequest JCS-serializes and base64url-encodes a request object for use in the challenge's request parameter.
func PaymentRequiredProblem ¶
func PaymentRequiredProblem() []byte
PaymentRequiredProblem returns the default Problem Details body for a 402 Payment Required response.
func SetChallengeHeader ¶
func SetChallengeHeader(h http.Header, p *ChallengeParams)
SetChallengeHeader writes a WWW-Authenticate: Payment challenge header to the given http.Header using the auth-param syntax defined in draft-httpauth-payment-00 Section 5.1.
func SetReceiptHeader ¶
SetReceiptHeader writes a Payment-Receipt header to the given http.Header. The receipt is a base64url-encoded JSON object per draft-httpauth-payment-00 Section 5.3.
func UseLogger ¶
UseLogger uses a specified Logger to output package logging info. This should be used in preference to SetLogWriter if the caller is also using btclog.
func VerifyChallengeID ¶
func VerifyChallengeID(secret []byte, params *ChallengeParams, id string) bool
VerifyChallengeID verifies that a challenge ID matches the expected HMAC-SHA256 binding for the given parameters. Uses constant-time comparison to prevent timing attacks.
Types ¶
type ChallengeEcho ¶
type ChallengeEcho struct {
// ID is the challenge identifier from the WWW-Authenticate header.
ID string `json:"id"`
// Realm is the protection space from the challenge.
Realm string `json:"realm"`
// Method is the payment method identifier from the challenge.
Method string `json:"method"`
// Intent is the payment intent type from the challenge.
Intent string `json:"intent"`
// Request is the base64url-encoded payment request from the challenge.
Request string `json:"request"`
// Expires is the challenge expiration timestamp, if present in the
// original challenge.
Expires string `json:"expires,omitempty"`
// Description is the human-readable description, if present in the
// original challenge.
Description string `json:"description,omitempty"`
// Opaque is the server correlation data, if present in the original
// challenge.
Opaque string `json:"opaque,omitempty"`
// Digest is the content digest, if present in the original challenge.
Digest string `json:"digest,omitempty"`
}
ChallengeEcho is the challenge object echoed back in the credential. The client returns all challenge parameters unchanged so the server can verify the binding.
func (*ChallengeEcho) ToChallengeParams ¶
func (e *ChallengeEcho) ToChallengeParams() *ChallengeParams
ToChallengeParams converts a ChallengeEcho back to ChallengeParams for HMAC verification. This is used when the server receives a credential and needs to verify the challenge binding.
type ChallengeParams ¶
type ChallengeParams struct {
// ID is the unique challenge identifier. Servers bind this value to
// the challenge parameters via HMAC-SHA256 to enable stateless
// verification.
ID string
// Realm is the protection space identifier per RFC 9110.
Realm string
// Method is the payment method identifier (e.g., "lightning").
Method string
// Intent is the payment intent type (e.g., "charge", "session").
Intent string
// Request is the base64url-encoded JCS-serialized JSON containing
// payment-method-specific data needed to complete payment.
Request string
// Expires is an optional RFC 3339 timestamp indicating when this
// challenge expires.
Expires string
// Digest is an optional content digest of the request body, formatted
// per RFC 9530.
Digest string
// Description is an optional human-readable description of the
// resource or payment purpose.
Description string
// Opaque is optional base64url-encoded JCS-serialized JSON containing
// server-defined correlation data.
Opaque string
}
ChallengeParams represents the auth-params sent in the WWW-Authenticate: Payment header per draft-httpauth-payment-00 Section 5.1.
func ParseChallengeHeader ¶
func ParseChallengeHeader(headerValue string) (*ChallengeParams, error)
ParseChallengeHeader parses a WWW-Authenticate: Payment header value into ChallengeParams. This is primarily used by clients to extract the challenge parameters from a 402 response.
type ChargeMethodDetails ¶
type ChargeMethodDetails struct {
// Invoice is the full BOLT11-encoded payment request string. This
// field is authoritative; all other payment parameters are derived
// from it.
Invoice string `json:"invoice"`
// PaymentHash is an optional convenience field containing the payment
// hash embedded in the invoice, as a lowercase hex-encoded string.
PaymentHash string `json:"paymentHash,omitempty"`
// Network identifies the Lightning Network the invoice is issued on.
// Must be one of "mainnet", "regtest", or "signet". Defaults to
// "mainnet" if omitted.
Network string `json:"network,omitempty"`
}
ChargeMethodDetails contains the Lightning-specific fields for a charge request per draft-lightning-charge-00 Section 7.2.
type ChargePayload ¶
type ChargePayload struct {
// Preimage is the 32-byte payment preimage revealed upon successful
// HTLC settlement, encoded as a lowercase hex string (64 characters).
Preimage string `json:"preimage"`
}
ChargePayload is the credential payload for the Lightning charge intent per draft-lightning-charge-00 Section 8.
type ChargeRequest ¶
type ChargeRequest struct {
// Amount is the invoice amount in base units (satoshis), encoded as a
// decimal string.
Amount string `json:"amount"`
// Currency identifies the unit for Amount. Must be "sat" for
// Lightning.
Currency string `json:"currency"`
// Description is an optional human-readable memo describing the
// resource or service being paid for.
Description string `json:"description,omitempty"`
// Recipient is an optional payment recipient in method-native format.
Recipient string `json:"recipient,omitempty"`
// ExternalID is an optional merchant reference (e.g., order ID).
ExternalID string `json:"externalId,omitempty"`
// MethodDetails contains Lightning-specific fields nested under
// methodDetails in the request JSON.
MethodDetails ChargeMethodDetails `json:"methodDetails"`
}
ChargeRequest is the decoded request field for method="lightning", intent="charge" as defined in draft-lightning-charge-00 Section 7.
type Credential ¶
type Credential struct {
// Challenge contains the echoed challenge parameters from the original
// WWW-Authenticate header.
Challenge ChallengeEcho `json:"challenge"`
// Source is an optional payer identifier. The recommended format is a
// DID per W3C-DID.
Source string `json:"source,omitempty"`
// Payload contains the payment-method-specific proof of payment. The
// structure depends on the method and intent. We use json.RawMessage
// to defer parsing until the method/intent is known.
Payload json.RawMessage `json:"payload"`
}
Credential is the decoded Authorization: Payment token sent by the client. It contains the echoed challenge parameters and the payment-method-specific payload proving payment.
func ParseCredential ¶
func ParseCredential(h *http.Header) (*Credential, error)
ParseCredential extracts and decodes a Payment credential from the Authorization header. The credential is a base64url-encoded JSON object per draft-httpauth-payment-00 Section 5.2.
Returns nil and an error if the header does not contain a valid Payment credential.
type NeedTopUpEvent ¶
type NeedTopUpEvent struct {
// SessionID is the session identifier (paymentHash of the deposit
// invoice).
SessionID string `json:"sessionId"`
// BalanceSpent is the total satoshis spent from the current deposit at
// the point of exhaustion.
BalanceSpent int64 `json:"balanceSpent"`
// BalanceRequired is the satoshis needed for the next unit of service.
BalanceRequired int64 `json:"balanceRequired"`
}
NeedTopUpEvent represents the SSE event data emitted when a streaming response exhausts the session balance per draft-lightning-session-00 Section 13.1.
type ProblemDetails ¶
type ProblemDetails struct {
// Type is a URI reference that identifies the problem type.
Type string `json:"type"`
// Title is a short, human-readable summary of the problem.
Title string `json:"title"`
// Status is the HTTP status code.
Status int `json:"status"`
// Detail is a human-readable explanation specific to this occurrence.
Detail string `json:"detail,omitempty"`
// ChallengeID is the associated challenge identifier, if applicable.
ChallengeID string `json:"challengeId,omitempty"`
}
ProblemDetails represents an RFC 9457 Problem Details object for use in error responses.
type Receipt ¶
type Receipt struct {
// Status is always "success". Receipts are only issued on successful
// payment responses.
Status string `json:"status"`
// Method is the payment method used (e.g., "lightning").
Method string `json:"method"`
// Timestamp is the RFC 3339 settlement timestamp.
Timestamp string `json:"timestamp"`
// Reference is a method-specific reference (e.g., payment hash hex
// for Lightning).
Reference string `json:"reference"`
// ChallengeID is the challenge identifier for audit and traceability.
ChallengeID string `json:"challengeId,omitempty"`
}
Receipt is the decoded Payment-Receipt header returned by the server on successful payment verification per draft-httpauth-payment-00 Section 5.3.
type SessionAction ¶
type SessionAction string
SessionAction enumerates the credential action types for the session intent.
const ( // SessionActionOpen opens a new session with a deposit payment. SessionActionOpen SessionAction = "open" // SessionActionBearer authenticates a request against an existing // session without additional payment. SessionActionBearer SessionAction = "bearer" // SessionActionTopUp adds funds to an existing session via a new // deposit payment. SessionActionTopUp SessionAction = "topUp" // SessionActionClose terminates a session and triggers a refund of // unspent balance. SessionActionClose SessionAction = "close" )
type SessionPayload ¶
type SessionPayload struct {
// Action is the session operation type: "open", "bearer", "topUp", or
// "close".
Action SessionAction `json:"action"`
// Preimage is the hex-encoded payment preimage. Used for open, bearer,
// and close actions. SHA-256(preimage) must equal the session's
// paymentHash.
Preimage string `json:"preimage,omitempty"`
// SessionID is the paymentHash of the original deposit invoice,
// identifying the session. Used for bearer, topUp, and close actions.
SessionID string `json:"sessionId,omitempty"`
// ReturnInvoice is a BOLT11 invoice with no encoded amount, created
// by the client at session open. The server pays this invoice with the
// unspent session balance on close. Used for open action only.
ReturnInvoice string `json:"returnInvoice,omitempty"`
// TopUpPreimage is the preimage of the top-up invoice. Used for topUp
// action only. SHA-256(topUpPreimage) must equal the paymentHash of
// the fresh invoice issued for this top-up.
TopUpPreimage string `json:"topUpPreimage,omitempty"`
}
SessionPayload is the credential payload for the Lightning session intent. The Action field discriminates the type of operation. Per draft-lightning-session-00 Section 8.
type SessionReceipt ¶
type SessionReceipt struct {
// Method is always "lightning".
Method string `json:"method"`
// Reference is the session ID (paymentHash).
Reference string `json:"reference"`
// Status is always "success".
Status string `json:"status"`
// Timestamp is the settlement time in RFC 3339 format.
Timestamp string `json:"timestamp"`
// RefundSats is the unspent balance that was refunded on close. Only
// present for close actions.
RefundSats int64 `json:"refundSats,omitempty"`
// RefundStatus indicates the outcome of the refund attempt on close.
// One of "succeeded", "failed", or "skipped". Only present for close
// actions.
RefundStatus string `json:"refundStatus,omitempty"`
}
SessionReceipt extends the base Receipt with session-specific fields per draft-lightning-session-00 Section 15.
type SessionRequest ¶
type SessionRequest struct {
// Amount is the cost per unit of service in base units (satoshis),
// encoded as a decimal string.
Amount string `json:"amount"`
// Currency identifies the unit for Amount. Must be "sat".
Currency string `json:"currency"`
// Description is an optional human-readable description of the
// service.
Description string `json:"description,omitempty"`
// UnitType is an optional human-readable label for the unit being
// priced (e.g., "token", "chunk", "request").
UnitType string `json:"unitType,omitempty"`
// DepositInvoice is a BOLT11 invoice the client must pay to open or
// top up the session. Required for open and topUp challenges.
DepositInvoice string `json:"depositInvoice,omitempty"`
// PaymentHash is the SHA-256 hash of the deposit invoice preimage, as
// a lowercase hex string.
PaymentHash string `json:"paymentHash"`
// DepositAmount is the exact deposit amount in satoshis, as a decimal
// string. When present, must equal the amount encoded in
// DepositInvoice.
DepositAmount string `json:"depositAmount,omitempty"`
// IdleTimeout is the server's idle timeout policy for open sessions,
// in seconds, as a decimal string.
IdleTimeout string `json:"idleTimeout,omitempty"`
}
SessionRequest is the decoded request field for method="lightning", intent="session" as defined in draft-lightning-session-00 Section 7.