Skip to main content

API Reference

Complete reference documentation for PayMCP classes, functions, and configuration options.

Core Classes

PayMCP

The main PayMCP class that integrates payment functionality into your MCP server.

class PayMCP:
def __init__(
self,
mcp_instance,
providers: list = None,
mode: Mode = Mode.AUTO,
state_store = None
)

Parameters

ParameterTypeDefaultDescription
mcp_instanceMCP server instanceRequiredYour MCP server instance
providersUnion[dict, Iterable]RequiredPayment provider configurations (see Provider Configuration section)
modeModeAUTOCoordination mode strategy
state_storeStateStoreInMemoryStateStoreState Store

Provider Configuration

Use the canonical provider list format:

providers = [StripeProvider(apiKey="sk_test_...")]

Alternative formats are also supported (config mapping, mixed styles) - see the Alternative Configuration section below.

Examples

from paymcp import PayMCP, Mode
from paymcp.providers import StripeProvider

PayMCP(
mcp,
providers=[StripeProvider(apiKey="sk_test_...")],
mode=Mode.AUTO
)

Mode

Enum defining available payment modes.

class Mode(str, Enum):
AUTO = "auto"
TWO_STEP = "two_step"
RESUBMIT = "resubmit"
X402 = "X402"
ELICITATION = "elicitation"
PROGRESS = "progress"
DYNAMIC_TOOLS = "dynamic_tools"

Coordination Modes

ModeDescriptionBest For
AUTODetects client capabilities; prefers X402 when configured and supported, otherwise uses ELICITATION and falls back to RESUBMITDefault, best balance of UX and compatibility
RESUBMITFirst call returns Payment Required Error with payment_url + payment_id; second call retries with the IDTool retries where the client handles user payment completion
ELICITATIONPayment link during executionReal-time interactions
TWO_STEPSplit into initiate/confirm stepsMaximum compatibility with older clients
X402Returns an x402 payment request; client replies with a payment signatureOn-chain x402 payments with compatible clients
PROGRESSExperimental auto-checking of payment statusReal-time interactions
DYNAMIC_TOOLSDynamically expose the next valid tool actionClients with listChanged support

For more details about coordination mode concepts, see Coordination Modes.

StateStore

By default, PayMCP stores pending tool arguments in memory using a process-local Map. This is not durable and will not work across server restarts or multiple server instances (no horizontal scaling).

To enable durable and scalable state storage, you can provide a custom StateStore implementation. PayMCP includes a built-in RedisStateStore, which works with any Redis-compatible client.

Example: Using Redis for State Storage

from redis.asyncio import from_url
from paymcp import PayMCP, RedisStateStore

redis = await from_url("redis://localhost:6379")
PayMCP(
mcp,
providers={"stripe": {"apiKey": "..."}},
state_store=RedisStateStore(redis)
)

Payment Providers

PayMCP provides an extensible provider system that abstracts payment providers behind a common interface. Providers can be supplied in multiple ways to give you maximum flexibility:

Configuration Methods

from paymcp.providers import StripeProvider

PayMCP(mcp, providers=[StripeProvider(apiKey="sk_test_...")])

Alternative Configuration

PayMCP(mcp, providers={
"stripe": {"apiKey": "..."}
})
from paymcp.providers import StripeProvider

PayMCP(mcp, providers={
"stripe": StripeProvider(apiKey="sk_test_...")
})

Custom Providers

BasePaymentProvider

All payment providers must inherit from BasePaymentProvider and implement the required methods.

from paymcp.providers import BasePaymentProvider

class MyProvider(BasePaymentProvider):
def __init__(self, api_key: str, **kwargs):
self.api_key = api_key
super().__init__(**kwargs)

def create_payment(self, amount: float, currency: str, description: str) -> tuple[str, str]:
"""Create a payment and return (payment_id, payment_url)"""
return "payment_id", "https://myprovider.com/pay/payment_id"

def get_payment_status(self, payment_id: str) -> str:
"""Return payment status: 'paid', 'pending', 'failed', or 'cancelled'"""
return "paid"

# Optional: subscriptions
async def get_subscriptions(self, user_id: str, email: str | None = None) -> dict:
return {
"current_subscriptions": [], # list of current user subscriptions
"available_subscriptions": [], # list of available plans
}

# Optional: subscriptions
async def start_subscription(self, plan_id: str, user_id: str, email: str | None = None) -> dict:
return {
"message": "Subscription created",
"sessionId": "SESSION_ID",
"checkoutUrl": "https://example.com/checkout",
}

# Optional: subscriptions
async def cancel_subscription(self, subscription_id: str, user_id: str, email: str | None = None) -> dict:
return {
"message": "Subscription cancellation scheduled",
"canceled": True,
"endDate": "2025-12-31T00:00:00Z",
}

Price & Subscription Configuration

PayMCP supports two common gating mechanisms: pay-per-request pricing via price, and access control via subscription (require an active plan to use the tool). Python users can use decorators; in other languages you can set the same values via tool metadata.

Price

Define pay-per-request pricing for a tool.

Parameters

TSPYTypeDefaultDescription
amountpricefloatRequiredPayment amount
currencycurrencystr"USD"ISO 4217 currency code

Example

@mcp.tool()
@price(price=0.50, currency="USD")
def generate_data_report(input: str, ctx: Context) -> str:
"""Generate a data report from input"""
return f"Report: {input}"

#Alternatively, you can pass pricing via tool metadata
@mcp.tool(meta={"price":{"price":0.50, "currency"="USD"}})
def generate_data_report(input: str, ctx: Context) -> str:
"""Generate a data report from input"""
return f"Report: {input}"

@subscription

Require an active subscription plan to use a tool (access control).

Parameters

ParameterTypeDefaultDescription
planstr | list[str]RequiredAccepted plan IDs from your provider

Example

from paymcp import subscription

@mcp.tool()
@subscription(plan="price_pro_monthly") # or a list of accepted plan IDs
async def generate_report(ctx: Context) -> str:
return "Your report"

#Alternatively, you can pass subscription info via tool metadata
@mcp.tool(meta={"subscription":{"plan":"price_pro_monthly"}})
def generate_data_report(input: str, ctx: Context) -> str:
"""Generate a data report from input"""
return f"Report: {input}"


Provider Configuration

Stripe

providers = [StripeProvider(api_key="sk_test_...")]

Parameters

TSPYTypeRequiredDescription
apiKeyapi_keystring / strYesStripe secret key (sk_test_... or sk_live_...).
successUrlsuccess_urlstring / strNoRedirect URL after a successful Stripe Checkout. Supports {CHECKOUT_SESSION_ID} placeholder.
cancelUrlcancel_urlstring / strNoRedirect URL if the user cancels payment in Stripe Checkout.
loggerloggerLoggerNoOptional logger instance to use for provider logs.

Walleot

providers = {
"walleot": {
"api_key": str, # Required: Walleot API key
}
}

Parameters

TSPYTypeRequiredDescription
apiKeyapi_keystring / strYesWalleot API key.
loggerloggerLoggerNoOptional logger instance to use for provider logs.

PayPal

providers = [PayPalProvider(client_id="...", client_secret="...", sandbox=True)]

Parameters

TSPYTypeRequiredDescription
clientIdclient_idstring / strYesPayPal application client ID.
clientSecretclient_secretstring / strYesPayPal application client secret.
sandboxsandboxboolean / boolNoIf true, uses PayPal sandbox API (api-m.sandbox.paypal.com); otherwise uses production (api-m.paypal.com). Default: true.
successUrlsuccess_urlstring / strNoURL the user is redirected to after successful approval/checkout.
cancelUrlcancel_urlstring / strNoURL the user is redirected to if they cancel payment.
loggerloggerLoggerNoOptional logger instance to use for provider logs.

Square

providers = {
"square": {
"access_token": str, # Required: Square access token
"location_id": str, # Required: Square location ID
}
}

Parameters

TSPYTypeRequiredDescription
accessTokenaccess_tokenstring / strYesSquare access token.
locationIdlocation_idstring / strYesSquare location identifier.
sandboxsandboxboolean / boolNoIf true, uses Square sandbox base URL; otherwise uses production base URL. Default: true.
redirectUrlredirect_urlstring / strNoURL Square redirects to after payment/checkout completion.
apiVersionapi_versionstring / strNoSquare API version. If not provided, Python uses SQUARE_API_VERSION env var, then defaults to 2025-03-19.
loggerloggerLoggerNoOptional logger instance to use for provider logs.

Adyen

providers = {
"adyen": {
"api_key": str, # Required: Adyen API key
"merchant_account": str, # Required: Merchant account name
}
}

Parameters

TSPYTypeRequiredDescription
apiKeyapi_keystring / strYesAdyen API key.
merchantAccountmerchant_accountstring / strYesAdyen merchant account identifier.
successUrlreturn_urlstring / strNoReturn URL / redirect URL used by Adyen to return the user to your app with payment result info.
sandboxsandboxboolean / boolNoIf true, uses Adyen test environment; otherwise uses live environment. Default: false.
loggerloggerLoggerNoOptional logger instance to use for provider logs.

Coinbase Commerce

providers = [CoinbaseProvider(api_key="...")]

Parameters

TSPYTypeRequiredDescription
apiKeyapi_keystring / strYesCoinbase Commerce API key.
successUrlsuccess_urlstring / strNoURL the user is redirected to after successful payment completion.
cancelUrlcancel_urlstring / strNoURL the user is redirected to if they cancel payment.
confirmOnPendingconfirm_on_pendingboolean / boolNoIf true, treat PENDING status as paid for faster confirmation. Risk: rare cases may still fail or be cancelled after PENDING. Default: false.
loggerloggerLoggerNoOptional logger instance to use for provider logs.

X402

from paymcp.providers import X402Provider

providers = [
X402Provider(
pay_to=[
{"address": "0xYourAddress", "network": "eip155:8453", "asset": "USDC"}
],
)
]

Parameters

TSPYTypeRequiredDescription
payTopay_toPayTo[] / list[dict]YesRecipient list (see PayTo item below).
facilitatorfacilitatorobject / dictNoFacilitator settings (see Facilitator below).
resourceInforesource_infoobject / dictNoOptional resource metadata shown to the client (see ResourceInfo below).
x402Versionx402_versionnumber / intNoX402 protocol version (default: 2).
gasLimitgas_limitstring / strNoDefault gas limit applied to all payTo items (can be overridden per-item).
loggerloggerLoggerNoOptional logger instance to use for provider logs.
PayTo item

Each item in payTo / pay_to supports:

TS keyPY keyTypeRequiredDefaultDescription
addressaddressstring / strYesRecipient wallet address.
networknetworkstring / strNoeip155:8453Network identifier (e.g. eip155:8453, eip155:84532, solana-devnet, solana-mainnet).
assetassetstring / strNoUSDCAsset symbol/address. For Base networks, USDC is auto-mapped to the canonical contract address.
multipliermultipliernumber / intNo1_000_000Token base-unit multiplier (USDC = 6 decimals).
domainNamedomainNamestring / strNoUSD CoinEIP-712 domain name. Note: Base Sepolia USDC uses USDC.
domainVersiondomainVersionstring / strNo2EIP-712 domain version (Circle USDC uses 2).
gasLimitgasLimitstring / strNoOptional per-recipient gas limit override.
Facilitator

facilitator controls how PayMCP talks to the x402 facilitator (default: Coinbase CDP). You can either provide apiKeyId + apiKeySecret or a custom createAuthHeaders callback.

TS keyPY keyTypeRequiredDescription
urlurlstring / strNoFacilitator base URL (default: https://api.cdp.coinbase.com/platform/v2/x402).
apiKeyIdapiKeyIdstring / strNoCoinbase CDP API key id (used to sign requests).
apiKeySecretapiKeySecretstring / strNoCoinbase CDP API key secret (used to sign requests).
createAuthHeaderscreateAuthHeaders(opts) => Record<string,string> / callableNoCustom auth header function. Receives { host, path, method } and should return headers (e.g. { Authorization: 'Bearer ...' }).
ResourceInfo
TS keyPY keyTypeRequiredDescription
urlurlstring / strNoResource URL that the payment is for (shown to the client).
descriptiondescriptionstring / strNoHuman-readable description of the resource/charge.
mimeTypemimeTypestring / strNoMIME type for the resource (e.g. application/json).

Context Requirements

All priced tools must include a ctx: Context / extra parameter:

from mcp.server.fastmcp import Context
@mcp.tool()
@price(amount=1.00, currency="USD")
def my_tool(input: str, ctx: Context) -> str: # ctx parameter is required for PayMCP integration
return process_input(input)

Why Context/Extra is Required

The Context/extra parameter provides:

  • User identification for payment tracking
  • Progress reporting capabilities (PROGRESS mode)
  • Elicitation support (ELICITATION mode)
  • Tool execution metadata

Error Handling

PayMCP handles various error scenarios automatically:

Payment Errors

Error TypeDescriptionUser Action
Payment declinedCard/payment method rejectedTry different payment method
Insufficient fundsNot enough balanceAdd funds or use different method
Payment timeoutUser didn't complete paymentRetry the tool call
Invalid currencyCurrency not supportedUse supported currency

Configuration Errors

Error TypeDescriptionSolution
Invalid API keyWrong or expired keyCheck provider dashboard
Missing providerProvider not configuredAdd provider to configuration
Invalid modeUnsupported payment modeUse supported mode

Tool Errors

Error TypeDescriptionSolution
Missing contextNo ctx parameterAdd ctx: Context parameter
Invalid amountAmount less than or equal to 0 or not numericUse valid positive amount

Advanced Configuration

Multiple Providers

In Mode.AUTO you can configure Multiple providers (One for clients who supports x402 protocol and one for all other clients)

PayMCP(
mcp,
providers=[
X402Provider(payTo=[{"address": "0xYourAddress"}]),
StripeProvider(apiKey="sk_test_...")
],
mode=Mode.AUTO
)

Next Steps