Tempo payment method — verifies payment via on-chain TIP-20 token transfer.
Tempo supports two credential types:
type="hash"— Client already broadcast the transaction; server verifies the receipt via RPC (eth_getTransactionReceipt).type="transaction"— Client sends a signed Tempo Transaction (0x76); server decodes, optionally adds fee payer signature, broadcasts, and verifies.
Configuration
Pass Tempo-specific config via :method_config in MPP.Plug opts:
plug MPP.Plug,
secret_key: "hmac-secret",
realm: "api.example.com",
method: MPP.Methods.Tempo,
amount: "1000000",
currency: "0x20c0000000000000000000000000000000000000",
method_config: %{
"rpc_url" => "https://rpc.moderato.tempo.xyz",
"chain_id" => 42431,
"fee_payer" => false
}Config Keys
"rpc_url"— (required) Tempo RPC endpoint URL"chain_id"— (optional) network chain ID, defaults to42431(Moderato testnet)"fee_payer"— (optional) enable server-side fee sponsorship, defaults tofalse. Whentrue, the server co-signs client transactions with domain0x78to pay transaction fees. Requires"fee_payer_private_key"and"fee_token", unless"fee_payer_url"is set for hosted sponsorship."fee_payer_url"— (optional) URL of a hosted fee-payer service that implementseth_fillTransaction. When set, the server delegates co-signing to the remote endpoint instead of using"fee_payer_private_key". Mutually exclusive with the local key path."fee_payer_private_key"— (required whenfee_payer: trueand nofee_payer_url) hex-encoded 32-byte secp256k1 private key for the fee payer account"fee_token"— (required for local co-sign whenfee_payer: trueand nofee_payer_url) hex address of a USD-denominated TIP-20 token to use for fee payment (e.g., pathUSD). Must be on the sponsor allowlist (fee_payer_allowed_fee_tokensor per-chain defaults)."fee_payer_allowed_fee_tokens"— (optional,fee_payer: trueonly) list of hex addresses overriding the default sponsor fee-token allowlist."fee_payer_policy"— (optional,fee_payer: trueonly) map of sponsor ceilings overriding the per-chain defaults:"max_gas","max_fee_per_gas","max_priority_fee_per_gas","max_total_fee"(wei), and"max_validity_window_seconds"(seconds). Bounds the client-supplied gas fields and validity window before the server co-signs so a malicious client cannot drain the fee-payer wallet via inflated gas price, total fee budget, or a padded access list, nor hold a co-signed sponsorship broadcastable far into the future. SeeMPP.Methods.Tempo.FeePayerPolicy."memo"— (optional) bytes32 hex memo fortransferWithMemo"wait_for_confirmation"— (optional) whenfalse, broadcasts without waiting for on-chain confirmation. Pre-simulates the full co-signed transaction viaeth_simulateV1first (same guard as the default path) to reject a tx that would revert before broadcast, then broadcasts async and returns an optimistic receipt. Defaulttrue."store"— (optional) module implementingMPP.Tempo.Storebehaviour for transaction dedup, or{MPP.Tempo.ConCacheStore, opts}to configure the built-in ConCache store (for example a custom cache:name). Prevents replay by tracking used tx hashes. Whennil(default), no dedup is performed — library stays stateless.
Credential Payload
The credential payload map must contain one of:
"type" => "hash","hash" => "0x..."— transaction hash for receipt verification"type" => "transaction","signature" => "..."— RLP-serialized signed Tempo Transaction
Dependencies
Requires the onchain and onchain_tempo packages (optional dependencies)
for RPC calls, transfer log parsing, and 0x76 Tempo transaction handling.
The method checks availability at init time via validate_config!/1.
API Functions
| Function | Arity | Description | Param Kinds |
|---|---|---|---|
challenge_method_details | 1 | Return Tempo-specific fields (chainId, feePayer, memo) for the 402 challenge. | charge: value |
verify | 2 | Verify a Tempo credential by checking on-chain settlement. | payload: value, charge: value |
validate_config! | 1 | Validate Tempo method_config at init time. Raises on missing rpc_url or unavailable onchain / onchain_tempo dependencies. | config: value |
method_name | 0 | Return the payment method identifier for Tempo. | - |
Summary
Functions
Return Tempo-specific fields (chainId, feePayer, memo) for the 402 challenge.
Return the payment method identifier for Tempo.
Validate Tempo method_config at init time. Raises on missing rpc_url or unavailable onchain / onchain_tempo dependencies.
Verify a Tempo credential by checking on-chain settlement.
Functions
@spec challenge_method_details(MPP.Intents.Charge.t()) :: map() | nil
@spec challenge_method_details(MPP.Intents.Charge.t()) :: map()
Return Tempo-specific fields (chainId, feePayer, memo) for the 402 challenge.
Parameters
charge- Charge struct with method_details containingchain_id,fee_payer, and optionallymemo(value)
Returns
Map with chainId (default 42431), feePayer (default false), and optional memo (map)
# descripex:contract
%{
params: %{
charge: %{
description: "Charge struct with method_details containing `chain_id`, `fee_payer`, and optionally `memo`",
kind: :value
}
},
returns: %{
type: :map,
description: "Map with `chainId` (default 42431), `feePayer` (default false), and optional `memo`"
}
}
@spec method_name() :: String.t()
Return the payment method identifier for Tempo.
# descripex:contract
%{}
Validate Tempo method_config at init time. Raises on missing rpc_url or unavailable onchain / onchain_tempo dependencies.
Parameters
config- method_config map to validate (value)
Returns
:ok on success, raises ArgumentError on missing keys (atom)
# descripex:contract
%{
params: %{
config: %{description: "method_config map to validate", kind: :value}
},
returns: %{
type: :atom,
description: "`:ok` on success, raises `ArgumentError` on missing keys"
}
}
@spec verify(map(), MPP.Intents.Charge.t()) :: {:ok, MPP.Receipt.t()} | {:error, MPP.Errors.t()}
@spec verify(map(), MPP.Intents.Charge.t()) :: {:ok, MPP.Receipt.t()} | {:error, MPP.Errors.t()}
@spec verify(map(), MPP.Intents.Charge.t()) :: {:ok, MPP.Receipt.t()} | {:error, MPP.Errors.t()}
@spec verify(map(), MPP.Intents.Charge.t()) :: {:error, MPP.Errors.t()}
Verify a Tempo credential by checking on-chain settlement.
Parameters
payload- Credential payload map with"type"("hash"or"transaction") and corresponding proof field (value)charge- Charge intent struct with amount, currency, and method_details (includingrpc_url) (value)
Returns
{:ok, receipt} on success, {:error, error} on failure (tagged_tuple)
Errors
:invalid_payload:verification_failed
# descripex:contract
%{
params: %{
payload: %{
description: "Credential payload map with `\"type\"` (`\"hash\"` or `\"transaction\"`) and corresponding proof field",
kind: :value
},
charge: %{
description: "Charge intent struct with amount, currency, and method_details (including `rpc_url`)",
kind: :value
}
},
errors: [:invalid_payload, :verification_failed],
returns: %{
type: :tagged_tuple,
description: "`{:ok, receipt}` on success, `{:error, error}` on failure"
}
}