mpp

package
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 25, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Index

Constants

View Source
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"
)
View Source
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.

View Source
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.

View Source
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"
)
View Source
const Subsystem = "MPAY"

Subsystem defines the sub system name of this package.

Variables

This section is empty.

Functions

func Base64URLDecode

func Base64URLDecode(s string) ([]byte, error)

Base64URLDecode decodes a base64url-encoded string. It accepts input with or without padding per the spec requirement.

func Base64URLEncode

func Base64URLEncode(data []byte) string

Base64URLEncode encodes data using base64url encoding without padding per RFC 4648 Section 5, as required by the Payment HTTP Authentication Scheme.

func Canonicalize

func Canonicalize(v any) ([]byte, error)

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

func CanonicalizeJSON(raw []byte) ([]byte, error)

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

func DecodeRequest(encoded string, target any) error

DecodeRequest base64url-decodes and unmarshals a request parameter into the given target.

func EncodeRequest

func EncodeRequest(v any) (string, error)

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

func SetReceiptHeader(h http.Header, r *Receipt) error

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

func UseLogger(logger btclog.Logger)

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.

func ParseReceiptHeader

func ParseReceiptHeader(h http.Header) (*Receipt, error)

ParseReceiptHeader extracts and decodes a Payment-Receipt from the given http.Header.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL