Plug middleware implementing the MPP 402 payment handshake.
Mount this plug in any Phoenix or Plug router to gate endpoints behind payment. Each route gets its own pricing via plug opts — no global config.
Single-Method Usage
plug MPP.Plug,
secret_key: "your-hmac-secret",
realm: "api.example.com",
method: MyApp.Payments.Stripe,
amount: "1000",
currency: "usd"Multi-Method Usage
Accept multiple payment methods per endpoint. Each method can have its own
pricing and config. The 402 response includes one WWW-Authenticate header
per method; the agent picks whichever it can pay with.
plug MPP.Plug,
secret_key: "your-hmac-secret",
realm: "api.example.com",
methods: [
[method: MyApp.Payments.Stripe, amount: "1000", currency: "usd",
method_config: %{"stripe_secret_key" => "sk_..."}],
[method: MyApp.Payments.Tempo, amount: "950", currency: "usd"]
]Flow
- Request without
Authorization: Payment→ 402 withWWW-Authenticatechallenge(s) - Client pays off-band, retries with
Authorization: Payment <credential> - Valid credential → request passes through with
Payment-Receiptheader + receipt in assigns - Invalid credential → 402 with fresh challenge(s) + RFC 9457 error body
Shared Options
:secret_key— (required) HMAC-SHA256 key for challenge binding:realm— (required) server protection space:expires_in— (optional) challenge TTL in seconds (integer, defaults to 300):digest— (optional) expected content digest for body-bound challenges:opaque— (optional) base64url-encoded server correlation data:store— (optional) sharedMPP.Tempo.Storereplay-protection store
Single-Method Options
:method— (required) module implementingMPP.Method:amount— (required) price in base units (string):currency— (required) currency code (string, normalized to lowercase):recipient— (optional) payment recipient identifier:description— (optional) human-readable description:external_id— (optional) merchant reference ID included in the challenge request:method_config— (optional) server-only config map forverify/2
Multi-Method Options
:methods— (required) list of keyword lists, each with per-method opts::method,:amount,:currency, and optionally:recipient,:description,:external_id,:method_config
Summary
Functions
Runs the MPP 402 payment handshake for the current request.
Builds validated plug configuration from options at init time.
Functions
@spec call(Plug.Conn.t(), MPP.Plug.Config.t()) :: Plug.Conn.t()
Runs the MPP 402 payment handshake for the current request.
Returns 402 with fresh challenges when no valid credential is present;
halts with an RFC 9457 problem body on verification failure; otherwise
passes the connection through with :mpp_receipt assigned and a
Payment-Receipt response header.
@spec init(keyword()) :: MPP.Plug.Config.t()
Builds validated plug configuration from options at init time.
Normalizes single- or multi-method opts into a %Config{} with one
MethodEntry per accepted payment method. Raises on missing required
options or duplicate method names.