Help us shape the future of Go! Take the 2026 Go Developer Survey

mpp

package
v0.5.0 Latest Latest
Warning

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

Go to latest
Published: Oct 25, 2026 License: MIT, Apache-2.0, MIT Imports: 19 Imported by: 1

Documentation

Overview

Package mpp implements the Machine Payments Protocol (MPP) types and header serialization for Go. It provides Challenge, Credential, and Receipt types along with authentication and receipt header parsing and formatting helpers.

Index

Constants

View Source
const (
	HeaderAcceptPayment          = "Accept-Payment"
	HeaderAuthorization          = "Authorization"
	HeaderPaymentAuthorization   = "Payment-Authorization"
	HeaderPaymentReceipt         = "Payment-Receipt"
	HeaderPaymentSession         = "Payment-Session"
	HeaderPaymentSessionSnapshot = "Payment-Session-Snapshot"
	HeaderWWWAuthenticate        = "WWW-Authenticate"
)

MPP HTTP header names.

View Source
const (
	HintPaymentRequired     = "" /* 167-byte string literal not displayed */
	HintMalformedCredential = "" /* 171-byte string literal not displayed */
	HintMethodUnsupported   = HintPaymentRequired
)

Default hints for error types that support them.

View Source
const (
	// MCPPaymentRequiredCode reports that a tool call requires payment.
	MCPPaymentRequiredCode = -32042
	// MCPPaymentVerificationFailedCode reports that a submitted Credential was rejected.
	MCPPaymentVerificationFailedCode = -32043
	// MCPInvalidParamsCode reports a malformed Credential or payload.
	MCPInvalidParamsCode = -32602
	// MCPInternalErrorCode reports a server-side payment processing failure.
	MCPInternalErrorCode = -32603
)

MCP JSON-RPC error codes for payment failures, matching mppx's Mcp module.

View Source
const (
	// MCPCredentialMetaKey carries the Credential in a tool call's params._meta.
	MCPCredentialMetaKey = "org.paymentauth/credential"
	// MCPPaymentRequiredMetaKey carries MCPPaymentRequiredData in a tool result's _meta.
	MCPPaymentRequiredMetaKey = "org.paymentauth/payment-required"
	// MCPReceiptMetaKey carries the MCPReceipt in a tool result's _meta.
	MCPReceiptMetaKey = "org.paymentauth/receipt"
)

MCP _meta keys for payment data.

View Source
const (
	SchemePayment = "Payment"
)

MPP HTTP authentication schemes.

Variables

View Source
var BodyDigest = struct {
	Compute func(body any) string
	Verify  func(digest string, body any) bool
}{
	Compute: computeDigest,
	Verify:  verifyDigest,
}

BodyDigest provides helpers for computing and verifying SHA-256 digests. Compute returns a digest string in the form "sha-256=<base64>". Verify recomputes the digest and compares using constant-time comparison.

A []byte, string or json.RawMessage body, or a value of a named string or byte-slice type, is digested as is. Any other value is digested as its json.Marshal output, not the raw request bytes, so pass the raw bytes to bind exactly what the client sent. Compute and Verify panic for a value json.Marshal cannot encode, such as a channel or function, and for an io.Reader such as http.Request.Body: read it and pass the bytes.

View Source
var ErrVerification = errors.New("payment verification failed")

ErrVerification is a sentinel error for payment verification failures.

View Source
var Expires = struct {
	Seconds func(n int) string
	Minutes func(n int) string
	Hours   func(n int) string
	Days    func(n int) string
	Weeks   func(n int) string
}{
	Seconds: func(n int) string { return formatISO(time.Now().UTC().Add(time.Duration(n) * time.Second)) },
	Minutes: func(n int) string { return formatISO(time.Now().UTC().Add(time.Duration(n) * time.Minute)) },
	Hours:   func(n int) string { return formatISO(time.Now().UTC().Add(time.Duration(n) * time.Hour)) },
	Days:    func(n int) string { return formatISO(time.Now().UTC().Add(time.Duration(n) * 24 * time.Hour)) },
	Weeks:   func(n int) string { return formatISO(time.Now().UTC().Add(time.Duration(n) * 7 * 24 * time.Hour)) },
}

Expires provides helpers for generating ISO 8601 expiration timestamps with millisecond precision and Z suffix.

Functions

func AdvertisedCredentialHeader added in v0.4.0

func AdvertisedCredentialHeader(header string) string

AdvertisedCredentialHeader returns a challenge header parameter value, or empty for the implicit Authorization default.

func B64Decode

func B64Decode(s string) (map[string]any, error)

B64Decode decodes a base64url (no padding) string into a map.

Numbers decode as json.Number rather than float64. The challenge ID is an HMAC over the re-encoded request, so a decode that cannot reproduce the bytes it was given cannot reproduce the ID either: float64 carries 53 bits of mantissa, and any JSON integer above 2^53 comes back rounded, making a challenge the server itself issued fail to verify.

func ChallengeBoundJSONEqual added in v0.4.0

func ChallengeBoundJSONEqual(left, right any) bool

ChallengeBoundJSONEqual compares values using the RFC 8785 representation used to bind challenge requests and opaque values into their HMAC IDs.

func ConstantTimeEqual

func ConstantTimeEqual(a, b string) bool

ConstantTimeEqual returns true if a and b are equal, using constant-time comparison to avoid timing side-channels.

func ExtractAuthorizationScheme

func ExtractAuthorizationScheme(header, scheme string) string

ExtractAuthorizationScheme returns the first authorization value that matches the requested scheme from a possibly merged Authorization header.

func ExtractAuthorizationSchemeStrict added in v0.2.0

func ExtractAuthorizationSchemeStrict(header, scheme string) (string, error)

ExtractAuthorizationSchemeStrict returns a single authorization value that matches scheme, or an error if the header includes multiple matching values.

func FindPaymentAuthorization

func FindPaymentAuthorization(header string) string

FindPaymentAuthorization returns the Payment credential from an Authorization header. It tolerates comma-separated schemes and ignores non-Payment values.

func FindPaymentAuthorizationStrict added in v0.2.0

func FindPaymentAuthorizationStrict(header string) (string, error)

FindPaymentAuthorizationStrict returns the Payment credential from an Authorization header, or an error if more than one Payment credential exists.

func FormatAuthenticate

func FormatAuthenticate(c *Challenge, realm string) string

FormatAuthenticate formats a Challenge as an authentication header value.

Output format: Payment id="...", realm="...", method="...", intent="...", request="..."

func FormatAuthenticateStrict added in v0.1.2

func FormatAuthenticateStrict(c *Challenge, realm string) (string, error)

FormatAuthenticateStrict formats a Challenge as an authentication header value. It rejects values that cannot be safely represented in quoted auth-params, and empty required auth-params (id, realm, method, intent, request), which would otherwise be omitted and yield a challenge ParseChallenge rejects.

func FormatAuthorization

func FormatAuthorization(c *Credential) string

FormatAuthorization formats a Credential as an Authorization header value.

Output format: Payment <base64url-json>

func FormatAuthorizationStrict added in v0.4.0

func FormatAuthorizationStrict(c *Credential) (string, error)

FormatAuthorizationStrict formats a credential and enforces the wire size limit.

func FormatPaymentReceipt

func FormatPaymentReceipt(r *Receipt) string

FormatPaymentReceipt formats a Receipt as a Payment-Receipt header value.

Output format: <base64url-json>

func FormatReceipt

func FormatReceipt(r *Receipt) string

FormatReceipt formats a Receipt as a receipt header value.

func GenerateChallengeID

func GenerateChallengeID(opts GenerateChallengeIDInput) string

GenerateChallengeID produces an HMAC-SHA256 challenge ID from the given inputs. The HMAC is computed over pipe-delimited fields:

realm|method|intent|request_b64|expires|digest|opaque_b64

Challenges that advertise a credential header insert it immediately before the final opaque slot. Authorization is the implicit default and is never included, preserving the legacy binding.

The result is base64url-encoded without padding.

func GenerateChallengeIDWithError added in v0.4.0

func GenerateChallengeIDWithError(opts GenerateChallengeIDInput) (string, error)

GenerateChallengeIDWithError produces an HMAC-SHA256 challenge ID and rejects ambiguous fields, invalid advertised credential headers and request values that JCS cannot represent exactly.

func JSONEqual

func JSONEqual(left, right any) bool

JSONEqual compares two JSON-like values using Go's stable JSON encoding.

func MCPErrorCode added in v0.5.0

func MCPErrorCode(err error) int

MCPErrorCode maps a payment error to its MCP JSON-RPC error code. A nil error or a payment-required error maps to MCPPaymentRequiredCode, malformed credentials and invalid payloads to MCPInvalidParamsCode, internal payment errors and non-payment errors to MCPInternalErrorCode, and every other payment error to MCPPaymentVerificationFailedCode.

func ParseUnits

func ParseUnits(value string, decimals int) (int64, error)

ParseUnits converts a human-readable decimal string to base units. For example, ParseUnits("1.5", 6) returns 1500000. Returns an error if value is not a valid decimal, is negative, would produce fractional base units, or decimals is negative.

func SplitAuthenticate

func SplitAuthenticate(header string) []string

SplitAuthenticate splits a potentially merged authentication header value into individual scheme values while preserving commas inside quoted auth-param values.

func TransformUnits

func TransformUnits(request map[string]any) (map[string]any, error)

TransformUnits transforms request amounts from human-readable to base units. If a "decimals" key is present in the request map, it converts the "amount" field (and optionally "suggestedDeposit") from human-readable decimal strings to base unit strings, then removes the "decimals" key. If no "decimals" key is present, the request is returned unchanged.

Types

type Challenge

type Challenge struct {
	ID          string            `json:"id"`
	Method      string            `json:"method"`
	Intent      string            `json:"intent"`
	Request     map[string]any    `json:"request,omitempty"`
	Realm       string            `json:"realm,omitempty"`
	RequestB64  string            `json:"requestB64,omitempty"`
	Digest      string            `json:"digest,omitempty"`
	Expires     string            `json:"expires,omitempty"`
	Description string            `json:"description,omitempty"`
	Opaque      map[string]string `json:"opaque,omitempty"`

	// Header is the HTTP field for the Payment credential. Empty means the
	// implicit default, Authorization. When set, it is advertised as the
	// challenge header parameter and bound into the challenge ID.
	Header string `json:"header,omitempty"`
	// contains filtered or unexported fields
}

Challenge represents a server-issued payment challenge sent via the WWW-Authenticate header.

func FromAuthenticate

func FromAuthenticate(header string) (*Challenge, error)

FromAuthenticate parses an authentication header value into a Challenge.

func NewChallenge

func NewChallenge(secretKey, realm, method, intent string, request map[string]any, opts ...ChallengeOption) *Challenge

NewChallenge creates a new Challenge with an HMAC-bound ID. The secretKey and realm are used to compute the ID via GenerateChallengeID.

func NewChallengeWithError added in v0.4.0

func NewChallengeWithError(secretKey, realm, method, intent string, request map[string]any, opts ...ChallengeOption) (*Challenge, error)

NewChallengeWithError creates a new Challenge with an HMAC-bound ID. It rejects invalid advertised credential headers and request values that JCS cannot represent without changing their numeric value.

func ParseChallenge

func ParseChallenge(header string) (*Challenge, error)

ParseChallenge parses a Payment challenge from a WWW-Authenticate header value.

func (*Challenge) CredentialHeader added in v0.4.0

func (c *Challenge) CredentialHeader() string

CredentialHeader returns the HTTP field a client must use for the payment credential. Challenges that omit header default to Authorization.

func (Challenge) MarshalJSON

func (c Challenge) MarshalJSON() ([]byte, error)

MarshalJSON emits the standard JSON challenge shape with a decoded request object, while keeping RequestB64 as an internal cache for header formatting.

func (*Challenge) NewCredential

func (c *Challenge) NewCredential(payload map[string]any, opts ...CredentialOption) *Credential

NewCredential builds a Credential from a Challenge and payment payload.

func (*Challenge) ToAuthenticate

func (c *Challenge) ToAuthenticate(realm string) string

ToAuthenticate formats this Challenge as an authentication header value.

func (*Challenge) ToAuthenticateStrict added in v0.1.2

func (c *Challenge) ToAuthenticateStrict(realm string) (string, error)

ToAuthenticateStrict formats this Challenge as an authentication header value and rejects values that cannot be safely represented in quoted auth-params.

func (*Challenge) ToEcho

func (c *Challenge) ToEcho() ChallengeEcho

ToEcho returns the ChallengeEcho representation suitable for inclusion in a Credential.

func (*Challenge) UnmarshalJSON

func (c *Challenge) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts the standard JSON challenge shape and computes the cached RequestB64 field used by header serialization.

func (*Challenge) Verify

func (c *Challenge) Verify(secretKey, realm string) bool

Verify checks whether the challenge ID matches the expected HMAC for the given secretKey and realm. Uses constant-time comparison. A parsed challenge whose issuer encoded opaque non-canonically is verified against that encoding, as it is echoed.

type ChallengeEcho

type ChallengeEcho struct {
	ID      string            `json:"id"`
	Realm   string            `json:"realm,omitempty"`
	Method  string            `json:"method"`
	Intent  string            `json:"intent"`
	Request string            `json:"request,omitempty"`
	Expires string            `json:"expires,omitempty"`
	Digest  string            `json:"digest,omitempty"`
	Opaque  map[string]string `json:"opaque,omitempty"`
	Header  string            `json:"header,omitempty"`
	// contains filtered or unexported fields
}

ChallengeEcho is the subset of a Challenge echoed back in a Credential.

type ChallengeOption

type ChallengeOption func(*challengeConfig)

ChallengeOption configures optional fields when creating a new Challenge.

func WithDescription

func WithDescription(desc string) ChallengeOption

WithDescription sets the human-readable description on a Challenge.

func WithDigest

func WithDigest(digest string) ChallengeOption

WithDigest sets the body digest on a Challenge.

func WithExpires

func WithExpires(expires string) ChallengeOption

WithExpires sets the expiration timestamp on a Challenge.

func WithHeader added in v0.4.0

func WithHeader(header string) ChallengeOption

WithHeader sets the HTTP field that must carry the Payment credential. Authorization is the implicit default and is not advertised on the wire.

func WithMeta

func WithMeta(meta map[string]string) ChallengeOption

WithMeta sets opaque metadata key-value pairs on a Challenge.

type Credential

type Credential struct {
	Challenge ChallengeEcho  `json:"challenge"`
	Payload   map[string]any `json:"payload,omitempty"`
	Source    string         `json:"source,omitempty"`
}

Credential represents a client-submitted payment proof. It is sent in Authorization by default, or in Payment-Authorization when the challenge advertised that header.

func FromAuthorization

func FromAuthorization(header string) (*Credential, error)

FromAuthorization parses an Authorization header value into a Credential.

func ParseCredential

func ParseCredential(header string) (*Credential, error)

ParseCredential parses a Payment credential from an Authorization header value.

Expected format: Payment <base64url-json> The JSON payload contains: challenge (echo), payload, and optional source.

func (Credential) MarshalJSON

func (c Credential) MarshalJSON() ([]byte, error)

MarshalJSON emits the ergonomic JSON credential shape with a decoded challenge request object instead of the internal base64url string.

func (*Credential) ToAuthorization

func (c *Credential) ToAuthorization() string

ToAuthorization formats this Credential as an Authorization header value.

func (*Credential) UnmarshalJSON

func (c *Credential) UnmarshalJSON(data []byte) error

UnmarshalJSON accepts a credential with either an object request or the raw base64url echo string and normalizes it into the wire-centric ChallengeEcho.

type CredentialOption

type CredentialOption func(*Credential)

CredentialOption configures optional fields when creating a Credential.

func WithCredentialSource

func WithCredentialSource(source string) CredentialOption

WithCredentialSource sets the optional payer identity on a Credential.

type ErrorType

type ErrorType string

ErrorType identifies a machine-readable MPP problem type.

const (
	ErrorTypePaymentRequired     ErrorType = "https://paymentauth.org/problems/payment-required"
	ErrorTypeMalformedCredential ErrorType = "https://paymentauth.org/problems/malformed-credential"
	ErrorTypeInvalidChallenge    ErrorType = "https://paymentauth.org/problems/invalid-challenge"
	ErrorTypeVerificationFailed  ErrorType = "https://paymentauth.org/problems/verification-failed"
	ErrorTypePaymentExpired      ErrorType = "https://paymentauth.org/problems/payment-expired"
	ErrorTypeInvalidPayload      ErrorType = "https://paymentauth.org/problems/invalid-payload"
	ErrorTypeBadRequest          ErrorType = "https://paymentauth.org/problems/bad-request"
	ErrorTypePaymentInsufficient ErrorType = "https://paymentauth.org/problems/payment-insufficient"
	ErrorTypeMethodUnsupported   ErrorType = "https://paymentauth.org/problems/method-unsupported"
	ErrorTypeInternalPayment     ErrorType = "https://paymentauth.org/problems/internal-payment-error"
)

func (ErrorType) Status

func (t ErrorType) Status() int

Status returns the default HTTP status for the error type.

func (ErrorType) Title

func (t ErrorType) Title() string

Title returns the default RFC 9457 title for the error type.

type GenerateChallengeIDInput

type GenerateChallengeIDInput struct {
	SecretKey string
	Realm     string
	Method    string
	Intent    string
	Request   map[string]any
	Expires   string
	Digest    string
	Opaque    map[string]string
	Header    string
}

GenerateChallengeIDInput holds the inputs for HMAC-based challenge ID generation.

type MCPPaymentRequiredData added in v0.5.0

type MCPPaymentRequiredData struct {
	// HTTPStatus is the HTTP status the same failure would have, such as 402.
	HTTPStatus int `json:"httpStatus"`
	// Challenges lists the offered Challenges in presentation order.
	Challenges []*Challenge `json:"challenges"`
	// Problem holds RFC 9457 problem details when a Credential was rejected.
	Problem map[string]any `json:"problem,omitempty"`
}

MCPPaymentRequiredData is the data of an MCP payment error, or the value of MCPPaymentRequiredMetaKey in a tool result.

type MCPReceipt added in v0.5.0

type MCPReceipt struct {
	Receipt
	// ChallengeID is the ID of the Challenge the Credential answered.
	ChallengeID string
}

MCPReceipt is a Receipt carried in MCP tool result metadata, bound to the Challenge it settles.

func (MCPReceipt) MarshalJSON added in v0.5.0

func (r MCPReceipt) MarshalJSON() ([]byte, error)

MarshalJSON emits the canonical receipt fields plus challengeId.

func (*MCPReceipt) UnmarshalJSON added in v0.5.0

func (r *MCPReceipt) UnmarshalJSON(data []byte) error

UnmarshalJSON validates the receipt fields and reads challengeId.

type PaymentError

type PaymentError struct {
	Type   ErrorType `json:"type"`
	Title  string    `json:"title"`
	Status int       `json:"status"`
	Detail string    `json:"detail"`
	Hint   string    `json:"hint,omitempty"`
	// Details contains safe machine-readable error metadata.
	Details map[string]any `json:"details,omitempty"`
	// contains filtered or unexported fields
}

PaymentError represents an MPP error using RFC 9457 Problem Details.

func ErrBadRequest

func ErrBadRequest(reason string) *PaymentError

ErrBadRequest returns a generic 400 error.

func ErrCredentialReplayed added in v0.4.0

func ErrCredentialReplayed(reason string) *PaymentError

ErrCredentialReplayed returns a 403 verification-failed error for a credential whose payment was already used: consumed by an earlier request or claimed by a concurrent one. The payment itself was valid, so the response carries no retry challenge and auto-paying clients do not pay again. The problem type stays verification-failed; only the status differs.

func ErrInternalPayment added in v0.4.0

func ErrInternalPayment() *PaymentError

ErrInternalPayment returns a 500 error for unexpected payment processing failures. Its detail is fixed so internal error messages are never exposed.

func ErrInvalidChallenge

func ErrInvalidChallenge(challengeID, reason string) *PaymentError

ErrInvalidChallenge returns a 402 error for invalid or tampered challenges.

func ErrInvalidPayload

func ErrInvalidPayload(reason string) *PaymentError

ErrInvalidPayload returns a 402 error for invalid payment payloads.

func ErrMalformedCredential

func ErrMalformedCredential(reason string) *PaymentError

ErrMalformedCredential returns a 402 error for unparseable credentials.

func ErrMethodUnsupported

func ErrMethodUnsupported(method string) *PaymentError

ErrMethodUnsupported returns a 400 error for unsupported payment methods.

func ErrPaymentExpired

func ErrPaymentExpired(expires string) *PaymentError

ErrPaymentExpired returns a 402 error for expired payment challenges.

func ErrPaymentInsufficient

func ErrPaymentInsufficient(reason string) *PaymentError

ErrPaymentInsufficient returns a 402 error for insufficient payment amounts.

func ErrPaymentRequired

func ErrPaymentRequired(realm, description string) *PaymentError

ErrPaymentRequired returns a 402 Payment Required error.

func ErrVerificationFailed

func ErrVerificationFailed(reason string) *PaymentError

ErrVerificationFailed returns a 402 error for failed payment verification.

func (*PaymentError) Error

func (e *PaymentError) Error() string

func (*PaymentError) ProblemDetails

func (e *PaymentError) ProblemDetails(challengeID string) map[string]any

ProblemDetails returns an RFC 9457 problem details map, optionally including a challengeID if non-empty.

func (*PaymentError) Unwrap

func (e *PaymentError) Unwrap() error

Unwrap returns the cause attached with WithCause, if any, so errors.Is and errors.As reach it. Otherwise verification errors unwrap to ErrVerification.

func (*PaymentError) WithCause added in v0.4.0

func (e *PaymentError) WithCause(cause error) *PaymentError

WithCause returns a copy of e that unwraps to cause. The cause is not part of the problem details sent to clients.

type Receipt

type Receipt struct {
	Status         string    `json:"status"`
	Timestamp      time.Time `json:"timestamp"`
	Reference      string    `json:"reference"`
	Method         string    `json:"method,omitempty"`
	ExternalID     string    `json:"externalId,omitempty"`
	SubscriptionID string    `json:"subscriptionId,omitempty"`
	// FundingCurrency is the currency actually debited to fund the payment,
	// when the payment method knows it.
	FundingCurrency string         `json:"fundingCurrency,omitempty"`
	Extra           map[string]any `json:"extra,omitempty"`

	// Extensions holds method-defined top-level receipt fields beyond the
	// base set. draft-ietf-httpauth-payment §5.3 states "Payment method
	// specifications MAY define additional fields for receipts", and the
	// canonical mppx schema preserves such unknown fields across
	// parse/serialize round trips rather than discarding them. These are
	// flattened to the top level of the Payment-Receipt JSON (not nested
	// under "extra"). The tag is "-" because serialization is handled
	// explicitly by FormatPaymentReceipt, not via struct marshaling.
	Extensions map[string]any `json:"-"`
}

Receipt represents a server-issued payment receipt sent via the Payment-Receipt header.

func FromPaymentReceipt

func FromPaymentReceipt(header string) (*Receipt, error)

FromPaymentReceipt parses a Payment-Receipt header value into a Receipt.

func ParsePaymentReceipt

func ParsePaymentReceipt(header string) (*Receipt, error)

ParsePaymentReceipt parses a Payment-Receipt header value into a Receipt.

Expected format: <base64url-json>

func ParseReceipt

func ParseReceipt(header string) (*Receipt, error)

ParseReceipt parses a receipt header value into a Receipt.

func Success

func Success(method, reference string, opts ...ReceiptOption) *Receipt

Success creates a new successful Receipt with the given method, reference, and options.

func (*Receipt) ToPaymentReceipt

func (r *Receipt) ToPaymentReceipt() string

ToPaymentReceipt formats this Receipt as a Payment-Receipt header value.

type ReceiptOption

type ReceiptOption func(*Receipt)

ReceiptOption configures optional fields when creating a Receipt.

func WithExternalID

func WithExternalID(id string) ReceiptOption

WithExternalID sets the external transaction ID on a Receipt.

func WithExtra

func WithExtra(extra map[string]any) ReceiptOption

WithExtra sets extra metadata on a Receipt.

func WithFundingCurrency added in v0.4.0

func WithFundingCurrency(currency string) ReceiptOption

WithFundingCurrency sets the currency actually debited to fund the payment.

func WithSubscriptionID added in v0.4.0

func WithSubscriptionID(id string) ReceiptOption

WithSubscriptionID sets the server-issued subscription ID on a Receipt.

Jump to

Keyboard shortcuts

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