- Allowed Tempo pull-mode validation to accept client attribution memos in both preflight calldata checks and split receipt matching while still rejecting truncated
transferWithMemocalldata. (by @BrendanRyan, #130)
- Defaulted
chain_idto 4217 (mainnet) in thetempo()function, removing the need to pass it explicitly. Removed the hardcoded testnet fee payer URL fallback, requiring explicit fee payer configuration on mainnet. Updated tests and docs accordingly. (by @BrendanRyan, #127) - Defaulted
chain_idto 4217 (mainnet) in thetempo()function, removing the need to pass it explicitly. Updated docs and example code accordingly. (by @BrendanRyan, #127) - Fixed
decode_fee_payer_envelopeto return aSignedKeyAuthorizationobject instead of raw RLP bytes when a key authorization is present in the fee payer envelope. (by @BrendanRyan, #127)
- Cached Tempo chain IDs per RPC URL to avoid redundant
eth_chainIdcalls. Also parallelizedeth_getTransactionCountandeth_gasPricefetches usingasyncio.gather. (by @BrendanRyan, #115) - Replaced
eth_sendRawTransaction+ polling loop witheth_sendRawTransactionSyncin Tempo's verify flow, eliminating the separate receipt polling step and extracting the transaction hash directly from the receipt response. (by @BrendanRyan, #115)
- Added split payments support for Tempo charges, allowing a single charge to be split across multiple recipients. Port of mpp-rs PR #180. (by @BrendanRyan, #104)
- Added Stripe payment method (
mpp.methods.stripe) supporting the Shared Payment Token (SPT) flow for HTTP 402 authentication. Includes client-sideStripeMethodandstripe()factory, server-sideChargeIntentfor PaymentIntent verification via Stripe SDK or raw HTTP, Pydantic schemas, and astripeoptional dependency group. (by @BrendanRyan, #104) - Added split payments support for Tempo charges, allowing a single charge to be split across multiple recipients. Port of mpp-rs PR #180. (by @BrendanRyan, #104)
- Fixed Tempo attribution memos to be deterministically bound to challenge IDs using a keccak256-derived nonce instead of random bytes. Added
verify_challenge_bindingto enforce that verified payments carry a memo matching the specific challenge being redeemed. (by @BrendanRyan, #111)
- Defaulted
chain_idto 4217 (mainnet) in thetempo()function, removing the need to pass it explicitly. Updated docs and example code accordingly. (by @BrendanRyan, #108) - Added Python 3.11 support by lowering the
requires-pythonconstraint from>=3.12to>=3.11, updating tooling targets accordingly, and replacing PEP 695 generic syntax withTypeVarfor compatibility. (by @BrendanRyan, #108)
- Fixed optional dependency handling by converting eager imports in
mpp.extensions.mcpandmpp.methods.tempoto lazy__getattr__-based loading, so importing these modules without their extras installed no longer raisesImportErrorat import time. Added clear install hint messages when optional attrs are accessed without the required extras, and addedeth-account,eth-hash,attrs, andrlpas declared dependencies for the[tempo]extra. (by @BrendanRyan, #105)
- Added access key signing support for Tempo transactions. When
root_accountis set, nonce and gas estimation now use the root account (smart wallet) address, transactions are signed viasign_tx_access_key, and the credential source reflects the root account rather than the access key address. (by @BrendanRyan, #103) - Added
RedisStoreandSQLiteStorebackends tompp.storesfor replay protection, with optional extras (pympp[redis],pympp[sqlite]). Addedstoreparameter toMpp.__init__andMpp.create()that automatically wires the store into intents supporting replay protection. (by @BrendanRyan, #103)
- Raised test coverage by adding tests for edge cases across charge, parsing, server, and store modules, and updated CI to generate and upload XML/HTML coverage reports for Python 3.12. (by @BrendanRyan, #103)
- Fixed fail-closed expiry enforcement in
ChargeIntent.verify: requests with a missingexpireschallenge parameter are now rejected instead of silently allowed through. (by @BrendanRyan, #103)
- Added atomic
put_if_absentmethod toStoreprotocol andMemoryStore, replacing the racyget()/put()pattern for charge replay protection. Normalized transaction hashes to lowercase before dedup to prevent mixed-case bypasses. (by @BrendanRyan, #91)
- Updated the testnet escrow contract address to
0xe1c4d3dce17bc111181ddf716f75bae49e61a336. (by @BrendanRyan, #90) - Updated
examples/api-server/README.mdto replace references to the externalpurltool with thepymppclient (python -m mpp.fetch) and corrected thesecret_keydocumentation to reflect that it is read from theMPP_SECRET_KEYenv var rather than auto-generated. (by @BrendanRyan, #90) - Added a pluggable
Storeinterface andMemoryStoreimplementation for transaction hash replay protection inChargeIntent. When a store is provided, verified tx hashes are recorded and subsequent attempts to reuse the same hash are rejected with aVerificationError. (by @BrendanRyan, #90) - Updated mainnet escrow contract address to
0x33b901018174DDabE4841042ab76ba85D4e24f25. (by @BrendanRyan, #90) - Raised
DEFAULT_GAS_LIMITfrom 100,000 to 1,000,000 for Tempo AA (type-0x76) transactions to account for their higher intrinsic gas cost (~270k for a single TIP-20 transfer). (by @BrendanRyan, #90)
- Require explicit server secret key — remove implicit
.envauto-generation/persistence.MPP_SECRET_KEYmust be set in the environment orsecret_keypassed explicitly; whitespace-only values are rejected. Preserves__wrapped__on payment decorators. Aligns pympp with mpp-rs and mppx behavior. (by @BrendanRyan, #81) - Consolidated
expiresfrom the request body into the challenge-levelexpiresauth-param exclusively. Removedexpiresas a field fromChargeRequestschema and updated all server, intent, and test code to read expiry fromcredential.challenge.expiresinstead. (by @BrendanRyan, #81) - Added
digestandopaquefields toMCPChallengefor extended challenge metadata propagation. Fixed deterministic body digest computation by addingsort_keys=Trueto JSON serialization, and corrected ISO 8601 timestamp formatting. Added cross-realm attack prevention by validating echoed challenge fields (realm,method,intent) against server-expected values in both HTTP and MCP transports. (by @BrendanRyan, #81)
- Applied
transform_requestmethod hook inMpp.chargeandMpp.payhelpers, ensuring the method's request transform is called before verification. Added tests covering both helpers. (by @BrendanRyan, #81) - Updated documentation URLs from
machinepayments.devtompp.dev, updated the IETF draft reference link, removed thecurrencyparameter from the README example, updated the example API endpoint, and updated the package description inpyproject.toml. (by @BrendanRyan, #81) - Added server-side expiry enforcement to reject replayed credentials after their challenge has expired. (by @BrendanRyan, #81)
- Added extensive test coverage for memo-based transfers, including unit tests for
_match_transfer_calldataand_verify_transfer_logswith memo fields, integration tests for end-to-end charge flows with server-specified memos, and new test modules for body digest computation, error types, expires helpers, and keychain signature handling. (by @BrendanRyan, #70) - Added 0x78 fee payer envelope encoding/decoding module with
encode_fee_payer_envelopeanddecode_fee_payer_envelopefunctions. UpdatedChargeIntent._cosign_as_fee_payerto use the new 0x78 wire format instead of 0x76, including sender address verification against the recovered signer. ExportedUSDC,PATH_USD, anddefault_currency_for_chainfrom the tempo package public API. (by @BrendanRyan, #70) - Added chain-specific default currency selection: mainnet now defaults to USDC and testnet defaults to pathUSD. Exported
USDC,PATH_USD, anddefault_currency_for_chainfrom the tempo package public API. Updated tests to reflect the new default currency behavior. (by @BrendanRyan, #70)
- Added pyright, build, and twine as dev dependencies. Improved CI pipeline with separate lint, test, and package jobs, concurrency cancellation, coverage enforcement, and package validation. Fixed pyright type errors with
type: ignoreannotations and refactoredChargeIntentto use a_get_rpc_url()helper to safely unwrap the optional RPC URL. (by @BrendanRyan, #70)
- Added
opaque/metafield support for server-defined correlation data in challenges. Themetaparameter onChallenge.create()andverify_or_challenge()stores arbitrary string key-value pairs as an HMAC-boundopaquefield, included in challenge IDs and serialized inWWW-AuthenticateandAuthorizationheaders. (by @BrendanRyan, #68) - Added full fee payer support to the Tempo method, allowing servers to co-sign sponsored transactions locally using a configured
fee_payeraccount instead of forwarding to an external service. Updated thetempo()factory andChargeIntentto propagate the fee payer account, and introduced expiring nonce logic for replay protection on sponsored transactions. (by @BrendanRyan, #68) - Added chain-specific default currency selection: mainnet now defaults to USDC and testnet defaults to pathUSD. Exported
USDC,PATH_USD, anddefault_currency_for_chainfrom the tempo package public API. Updated tests to reflect the new default currency behavior. (by @BrendanRyan, #68)
- Reordered
_REALM_ENV_VARSlist alphabetically and addedFLY_APP_NAME,HEROKU_APP_NAME, andWEBSITE_HOSTNAMEenvironment variables for realm detection. (by @BrendanRyan, #68)
- chore: remove release environment requirement from publish workflow (by @BrendanRyan, #58)
- Moved
VerificationErrorfrommpp.server.intenttompp.errorsso that client-only imports no longer transitively load server dependencies. Added a re-export shim inmpp.server.intentfor backwards compatibility and added import isolation tests to enforce the invariant. (by @BrendanRyan, #55) - Sorted imports to satisfy ruff I001 linting rules in
src/mpp/methods/tempo/intents.py. (by @BrendanRyan, #55)
- fix: accept
transferWithMemocalls in pre-broadcast validation when no explicit memo is required - Aligns with mppx behavior — clients may auto-generate attribution memos via
transferWithMemoeven when the server request has no explicit memo. Previously, the server would reject these withVerificationError: Invalid transaction: no matching payment call found. (by @BrendanRyan, #52)
- Fixed PyPI publish workflow by using Python 3.12 for building (package requires >=3.12). (by @BrendanRyan, #46)
- Test publish to verify end-to-end PyPI release pipeline. (by @BrendanRyan, #44)
- Added HMAC-bound challenge IDs for stateless MCP transport security and support for optional description/externalId fields in ChargeRequest. (by @BrendanRyan, #41)
- Renamed
@requires_paymentdecorator to@payacross the codebase, including all documentation, examples, and tests. Addedserver.pay()decorator method onMppclass. (by @BrendanRyan, #41) - Removed streaming payment support from the Tempo payment method. (by @BrendanRyan, #41)
- Switched pytempo dependency from git reference to PyPI package version. (by @BrendanRyan, #41)
- Fixed conformance issues by rejecting duplicate authentication parameters and requiring the
methodfield in payment receipts. (by @BrendanRyan, #41) - Added
sort_keys=Trueto alljson.dumps()calls to ensure JCS (RFC 8785) canonical JSON serialization when generating challenge IDs and credentials. (by @BrendanRyan, #41) - Switched payment token from AlphaUSD to PathUSD by updating currency address from
0x20c0000000000000000000000000000000000001to0x20c0000000000000000000000000000000000000and removed deprecatedALPHA_USDconstant. (by @BrendanRyan, #41)
Breaking: tempo() now requires an explicit intents parameter. The implicit ChargeIntent default has been removed. (by @BrendanRyan, #26)
- Initial release of pympp - HTTP 402 Payment Authentication for Python. (by @BrendanRyan, #26)