Behaviour for client-side payment providers.
A payment provider connects the MPP client to a real payment system (Stripe, Tempo, EVM, etc.). Each provider module implements two callbacks that the client transport uses when handling 402 challenges:
supports?/3— check if this provider handles the given method+intentpay/2— execute payment for a challenge, return a credential
This is the client-side counterpart to MPP.Method (server-side verification).
Where a method's verify/2 checks payment proof, PaymentProvider.pay/2
creates payment proof.
Usage
defmodule MyApp.Payments.StripeProvider do
use MPP.Client.PaymentProvider
@impl MPP.Client.PaymentProvider
def supports?(_method, _intent, _config), do: true
@impl MPP.Client.PaymentProvider
def pay(challenge, config) do
# 1. Parse challenge.request for payment details
# 2. Execute payment (call Stripe API, sign tx, etc.)
# 3. Return credential with proof payload
payload = %{"spt" => create_stripe_token(challenge, config)}
{:ok, %MPP.Credential{challenge: challenge, payload: payload}}
end
endDesign
Config is explicit. Providers receive a config map on every call —
no Application.get_env, no ENV fallback. The caller passes credentials,
RPC URLs, and other settings at the call site. This enables multi-tenant
usage and parallel testing with different configs.
For multi-provider dispatch, see MPP.Client.MultiProvider.
API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
pay | 3 | Execute payment using a provider module for the given challenge. | module: value, challenge: value, config: value |
supports? | 4 | Check if a provider module supports the given method and intent. | module: value, method: value, intent: value, config: value |
Summary
Callbacks
Execute payment for the given challenge and return a credential.
Check if this provider supports the given payment method and intent.
Functions
Execute payment using a provider module for the given challenge.
Check if a provider module supports the given method and intent.
Callbacks
@callback pay(challenge :: MPP.Challenge.t(), config :: map()) :: {:ok, MPP.Credential.t()} | {:error, term()}
Execute payment for the given challenge and return a credential.
This callback should:
- Decode the challenge request to extract payment details
- Execute the payment (sign transaction, call API, etc.)
- Build and return a
MPP.Credentialwith the echoed challenge and proof payload
Arguments
challenge— theMPP.Challengestruct from the 402 responseconfig— provider-specific configuration (API keys, RPC URLs, etc.)
Returns
{:ok, MPP.Credential.t()}— payment executed, credential ready to send{:error, term()}— payment failed
Check if this provider supports the given payment method and intent.
Called before pay/2 to determine which provider should handle a challenge.
Return true if this provider can execute payment for the combination.
Arguments
method— payment method name from the challenge (e.g.,"stripe","tempo")intent— intent type from the challenge (e.g.,"charge","session")config— provider-specific configuration (credentials, endpoints, etc.)
Functions
@spec pay(module(), MPP.Challenge.t(), map()) :: {:ok, MPP.Credential.t()} | {:error, term()}
Execute payment using a provider module for the given challenge.
Convenience function that delegates to module.pay/2.
Check if a provider module supports the given method and intent.
Convenience function that delegates to module.supports?/3.