diff --git a/docs/development-guide/nevermined-x402.mdx b/docs/development-guide/nevermined-x402.mdx index 9eaa797..ab9fcab 100644 --- a/docs/development-guide/nevermined-x402.mdx +++ b/docs/development-guide/nevermined-x402.mdx @@ -19,7 +19,7 @@ This section explains: - The x402 HTTP 402 handshake and `PAYMENT-SIGNATURE` retry pattern - How Nevermined extends x402 with Smart Account-based settlement - How subscribers generate and sign x402 payment proofs -- How permissions, session keys, and delegated execution work +- How delegations, session keys, and delegated execution work - How the facilitator verifies and settles requests - How to use the Python and TypeScript x402 client libraries - Advanced integration with Google A2A/AP2 @@ -92,7 +92,7 @@ Nevermined introduces two x402 schemes for different payment rails: | `nvm:erc4337` | `eip155:84532` | Crypto payments | ERC-4337 UserOps + session keys | | `nvm:card-delegation` | `stripe` | Fiat/credit card | Stripe PaymentIntent + credit burn | -For the complete card-delegation specification, see the [Card Delegation Spec](/docs/specs/x402-card-delegation). +For the complete delegation specification, see the [Delegation Spec](/docs/specs/x402-card-delegation). ### `nvm:erc4337` — Smart Account Extension @@ -281,7 +281,7 @@ When the server returns 402 Payment Required, it includes the `payment-required` ### Step 2 — Build a payment payload -Using Nevermined Payments libraries (Python or TS), you generate an x402 access token: +Using Nevermined Payments libraries (Python or TS), you generate an x402 access token. Token generation requires a `delegationConfig` that controls spending limits and duration. You can either auto-create a delegation inline (Pattern A) or create one explicitly and reuse its ID (Pattern B). @@ -306,8 +306,20 @@ Using Nevermined Payments libraries (Python or TS), you generate an x402 access // Extract planId and agentId from accepts array const { planId, agentId } = paymentRequired.accepts[0] - // Generate the x402 access token - const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId) + // Pattern A: Auto-create a delegation inline + const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId, { + delegationConfig: { spendingLimitCents: 10000, durationSecs: 604800 } + }) + + // Pattern B: Create a delegation explicitly, then reuse its ID + const delegation = await payments.delegation.createDelegation({ + provider: 'erc4337', + spendingLimitCents: 10000, + durationSecs: 604800 + }) + const { accessToken: token } = await payments.x402.getX402AccessToken(planId, agentId, { + delegationConfig: { delegationId: delegation.delegationId } + }) } ``` @@ -335,15 +347,28 @@ Using Nevermined Payments libraries (Python or TS), you generate an x402 access plan_id = payment_required["accepts"][0]["planId"] agent_id = payment_required["accepts"][0].get("extra", {}).get("agentId") - # Generate the x402 access token - token_res = payments.x402.get_x402_access_token(plan_id, agent_id) + # Pattern A: Auto-create a delegation inline + token_res = payments.x402.get_x402_access_token(plan_id, agent_id, { + 'delegationConfig': { 'spendingLimitCents': 10000, 'durationSecs': 604800 } + }) + access_token = token_res["accessToken"] + + # Pattern B: Create a delegation explicitly, then reuse its ID + delegation = payments.delegation.create_delegation({ + 'provider': 'erc4337', + 'spendingLimitCents': 10000, + 'durationSecs': 604800 + }) + token_res = payments.x402.get_x402_access_token(plan_id, agent_id, { + 'delegationConfig': { 'delegationId': delegation['delegationId'] } + }) access_token = token_res["accessToken"] ``` -The SDK auto-detects the payment scheme from plan metadata. For fiat plans (`isCrypto: false`), use `resolveScheme()` (TypeScript) or `resolve_scheme()` (Python) to determine the correct scheme. Middleware handles this automatically — see the [Express.js](/docs/integrate/add-to-your-agent/express) and [FastAPI](/docs/integrate/add-to-your-agent/fastapi) integration guides. +The SDK auto-detects the payment scheme from plan metadata. For fiat plans (`isCrypto: false`), use `resolveScheme()` (TypeScript) or `resolve_scheme()` (Python) to determine the correct scheme. For fiat plans, use `provider: 'stripe'` in the delegation; for crypto plans, use `provider: 'erc4337'`. Middleware handles this automatically — see the [Express.js](/docs/integrate/add-to-your-agent/express) and [FastAPI](/docs/integrate/add-to-your-agent/fastapi) integration guides. ### Step 3 — Submit with HTTP header @@ -556,8 +581,8 @@ This section provides a comprehensive guide for developers integrating Nevermine - **x402** gives a universal payment-required protocol - **Nevermined** enriches it with Smart Accounts, UserOps, and advanced billing models using the `nvm:erc4337` and `nvm:card-delegation` schemes -- **Card delegation** enables fiat/credit card payments via `nvm:card-delegation`, with auto-detection from plan metadata -- **Subscribers** delegate controlled permissions using session keys +- **Delegations** provide a unified permission model for both crypto (`nvm:erc4337`) and fiat (`nvm:card-delegation`) schemes, with auto-detection from plan metadata +- **Subscribers** delegate controlled permissions using `DelegationConfig` (spending limits and duration) - **Servers** use `payment-signature` headers and verify/settle via the facilitator - **Facilitators** verify and settle on-chain - **Python & TypeScript libraries** provide turnkey developer tooling diff --git a/docs/integrate/patterns/fiat-payments.mdx b/docs/integrate/patterns/fiat-payments.mdx index 635079a..ca070cb 100644 --- a/docs/integrate/patterns/fiat-payments.mdx +++ b/docs/integrate/patterns/fiat-payments.mdx @@ -107,13 +107,13 @@ Card delegation is what enables fiat-based agent-to-agent payments. An agent own ## Permission Model -Card delegations enforce strict spending limits to protect the cardholder. Each delegation has three controls: +Delegations enforce strict spending limits to protect the cardholder. The delegation model (`DelegationConfig`) is shared between crypto and fiat payment providers. Each delegation has the following controls: | Limit | Description | |-------|-------------| | `spendingLimitCents` | Maximum total amount (in cents) that can be charged across all transactions | -| `maxTransactions` | Maximum number of individual charges allowed | -| `expiresAt` | Expiration date after which the delegation becomes invalid | +| `durationSecs` | Duration in seconds after which the delegation expires | +| `maxTransactions` | (Optional) Maximum number of individual charges allowed | A delegation moves through a simple lifecycle: @@ -181,8 +181,8 @@ Fiat payments carry higher fees than stablecoin payments (2% + Stripe fees vs. 1 Accept crypto payments with the lowest fees and on-chain transparency - - Full technical specification for the card delegation extension + + Full technical specification for the delegation extension (crypto and fiat) diff --git a/docs/integrate/patterns/stablecoin-payments.mdx b/docs/integrate/patterns/stablecoin-payments.mdx index db571db..341f689 100644 --- a/docs/integrate/patterns/stablecoin-payments.mdx +++ b/docs/integrate/patterns/stablecoin-payments.mdx @@ -37,8 +37,8 @@ Accept USDC, EURC, or any ERC-20 token directly on-chain. Stablecoin payments ar The subscriber funds their Nevermined smart account with ERC-20 tokens (USDC, EURC, or another supported token). This account is an ERC-4337 smart account that supports programmable permissions. - - The subscriber generates scoped session keys that grant the facilitator permission to execute specific actions (order plans, burn credits, redeem access) on their behalf. Each key follows the principle of least privilege. + + The subscriber creates a delegation that grants the facilitator permission to execute specific actions (order plans, burn credits, redeem access) on their behalf. Each delegation is scoped by spending limits and duration, following the principle of least privilege. @@ -46,7 +46,7 @@ Accept USDC, EURC, or any ERC-20 token directly on-chain. Stablecoin payments ar - Each API call or agent query burns credits according to the plan configuration. If the subscriber's balance runs low, the system can auto-order more credits (if the wallet has sufficient balance and the session key permits it). + Each API call or agent query burns credits according to the plan configuration. If the subscriber's balance runs low, the system can auto-order more credits (if the wallet has sufficient balance and the delegation permits it). @@ -65,20 +65,30 @@ Nevermined supports any ERC-20 token on Base. If you need to accept a custom tok ## Permission Model -Session keys are the core of the stablecoin permission model. Instead of giving the facilitator full control over a wallet, subscribers create scoped keys that limit exactly what can be done. +Delegations are the core of the stablecoin permission model. Instead of giving the facilitator full control over a wallet, subscribers create scoped delegations that limit exactly what can be done. The delegation model is shared between crypto (`erc4337`) and fiat (`stripe`) payment providers. -Each session key enforces the **principle of least privilege** through three limits: +Each delegation enforces the **principle of least privilege** through two limits: | Limit | Description | |-------|-------------| -| `orderLimit` | Maximum token amount the key can spend on plan purchases | -| `redemptionLimit` | Maximum number of credit burn or redeem operations the key can perform | -| `expiresAt` | Expiration date after which the key becomes invalid | +| `spendingLimitCents` | Maximum total amount (in cents) that can be spent across all transactions | +| `durationSecs` | Duration in seconds after which the delegation expires | -When any limit is reached, the session key stops working. The subscriber must generate a new one with fresh limits to continue. +When the spending limit is exhausted or the delegation expires, it stops working. The subscriber must create a new delegation with fresh limits to continue. + +Delegations are created via the SDK before generating an x402 access token: + +```typescript +// Create a delegation for crypto payments +const delegation = await payments.delegation.createDelegation({ + provider: 'erc4337', + spendingLimitCents: 10000, + durationSecs: 604800 // 7 days +}) +``` -Session keys are revocable at any time by the smart account owner. You always retain full control over your wallet. +Delegations are revocable at any time by the smart account owner. You always retain full control over your wallet. Under the hood, delegations create the appropriate session keys for the chosen provider. ## Fees and Settlement @@ -124,13 +134,13 @@ Because there's no payment processor in the middle, stablecoin payments carry no When a subscriber's credit balance runs low, the system can automatically purchase more credits on their behalf. This happens seamlessly if: - The subscriber's wallet has sufficient token balance -- The active session key has remaining `orderLimit` capacity +- The active delegation has remaining spending capacity (`spendingLimitCents` not exhausted) - The plan supports re-ordering This keeps agent-to-agent workflows running without interruption. -If the session key's `orderLimit` or `redemptionLimit` is exhausted, auto-ordering stops. The subscriber must create a new session key with fresh limits to resume. +If the delegation's `spendingLimitCents` is exhausted or the delegation has expired, auto-ordering stops. The subscriber must create a new delegation with fresh limits to resume. ## Next Steps diff --git a/docs/products/x402-facilitator/how-it-works.mdx b/docs/products/x402-facilitator/how-it-works.mdx index f25d569..13e4406 100644 --- a/docs/products/x402-facilitator/how-it-works.mdx +++ b/docs/products/x402-facilitator/how-it-works.mdx @@ -124,7 +124,7 @@ When calling a protected endpoint, the server returns a `402 Payment Required` r ### Step 2: Generate x402 access token -Use the Nevermined SDK to generate an x402 access token with the required session keys: +Use the Nevermined SDK to generate an x402 access token. Token generation requires a `delegationConfig` that controls spending limits and duration. You can either auto-create a delegation inline or reuse an existing one. @@ -136,8 +136,20 @@ Use the Nevermined SDK to generate an x402 access token with the required sessio environment: 'sandbox' }) - // Generate the x402 access token - const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId) + // Pattern A: Auto-create a delegation inline + const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId, { + delegationConfig: { spendingLimitCents: 10000, durationSecs: 604800 } + }) + + // Pattern B: Create a delegation explicitly, then reuse its ID + const delegation = await payments.delegation.createDelegation({ + provider: 'erc4337', + spendingLimitCents: 10000, + durationSecs: 604800 + }) + const { accessToken: token } = await payments.x402.getX402AccessToken(planId, agentId, { + delegationConfig: { delegationId: delegation.delegationId } + }) ``` @@ -149,8 +161,21 @@ Use the Nevermined SDK to generate an x402 access token with the required sessio PaymentOptions(nvm_api_key=os.environ['NVM_API_KEY'], environment='sandbox') ) - # Generate the x402 access token - token_res = payments.x402.get_x402_access_token(plan_id, agent_id) + # Pattern A: Auto-create a delegation inline + token_res = payments.x402.get_x402_access_token(plan_id, agent_id, { + 'delegationConfig': { 'spendingLimitCents': 10000, 'durationSecs': 604800 } + }) + access_token = token_res['accessToken'] + + # Pattern B: Create a delegation explicitly, then reuse its ID + delegation = payments.delegation.create_delegation({ + 'provider': 'erc4337', + 'spendingLimitCents': 10000, + 'durationSecs': 604800 + }) + token_res = payments.x402.get_x402_access_token(plan_id, agent_id, { + 'delegationConfig': { 'delegationId': delegation['delegationId'] } + }) access_token = token_res['accessToken'] ``` @@ -389,7 +414,7 @@ sequenceDiagram Client->>Agent: GET /resource (no payment) Agent->>Client: 402 + payment-required header - Client->>Client: Generate x402 token via SDK + Client->>Client: Create delegation + generate x402 token via SDK Client->>Agent: POST /resource + payment-signature header Agent->>Facilitator: POST /verify diff --git a/docs/specs/x402-card-delegation.mdx b/docs/specs/x402-card-delegation.mdx index f58c150..1d14fda 100644 --- a/docs/specs/x402-card-delegation.mdx +++ b/docs/specs/x402-card-delegation.mdx @@ -1,16 +1,21 @@ --- -title: "x402 Card Delegation Extension" -description: "Specification for extending x402 with credit card delegation via Stripe for fiat-based payment settlement" -icon: "credit-card" +title: "x402 Delegation Extension" +description: "Specification for extending x402 with delegated payment permissions for both crypto (ERC-4337) and fiat (Stripe) settlement" +icon: "key" --- ## Abstract -This specification defines a **card-delegation extension** to the [x402 protocol](https://x402.org/) that enables payment settlement through on-chain credit burns, with automatic card-funded credit top-ups when the subscriber's balance is insufficient. While standard x402 settles payments via EIP-3009 ERC-20 token transfers and the smart accounts extension uses ERC-4337 UserOperations with crypto-funded ordering, this extension allows the ordering step to be funded via Stripe PaymentIntents backed by pre-authorized card delegations. +This specification defines a **delegation extension** to the [x402 protocol](https://x402.org/) that enables payment settlement through on-chain credit burns with delegated spending permissions. The delegation model is shared between crypto and fiat payment providers: -The scheme identifier is: `nvm:card-delegation` +- **Crypto delegations** (`provider: 'erc4337'`): Use ERC-4337 smart accounts and session keys for on-chain settlement, with crypto-funded ordering when the subscriber's balance is insufficient. +- **Fiat delegations** (`provider: 'stripe'`): Use Stripe PaymentIntents backed by pre-authorized card delegations for automatic card-funded credit top-ups when the subscriber's balance is insufficient. + +Both providers use the same `DelegationConfig` interface with `spendingLimitCents` and `durationSecs` to control delegation scope. -The extension is designed to be **fully compatible** with existing x402 clients and servers, requiring only the addition of the `nvm:card-delegation` extension payload. +The fiat scheme identifier is: `nvm:card-delegation` + +The extension is designed to be **fully compatible** with existing x402 clients and servers, requiring only the addition of the delegation extension payload. ``` Version: 0.1 @@ -42,7 +47,7 @@ Many real-world payment scenarios involve users and organizations that prefer or - **Budgetary controls** --- organizations need spending limits, approval workflows, and reconciliation against traditional accounting systems - **Instant onboarding** --- new users can start consuming services immediately with an existing payment card, without acquiring tokens -This extension enables these use cases by funding on-chain credit purchases via Stripe PaymentIntents, authorized through pre-established card delegations managed by the facilitator. Settlement itself uses the same on-chain credit burn mechanism as the smart accounts extension. +This extension enables these use cases by funding on-chain credit purchases via Stripe PaymentIntents, authorized through pre-established delegations managed by the facilitator. Settlement itself uses the same on-chain credit burn mechanism as the smart accounts extension. The delegation model (`DelegationConfig`) is shared across both crypto and fiat providers, providing a unified interface for creating and managing payment permissions. ### 1.3 Design Goals @@ -73,7 +78,7 @@ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "S | Term | Definition | |------|------------| -| **Delegation** | A pre-authorized permission granting the facilitator the ability to charge a client's card within defined spending limits and time constraints. | +| **Delegation** | A pre-authorized permission granting the facilitator the ability to execute payment operations on behalf of a client, within defined spending limits and time constraints. Delegations use the unified `DelegationConfig` interface for both crypto (`erc4337`) and fiat (`stripe`) providers. | | **SetupIntent** | A Stripe object used to collect and confirm a customer's payment method for future off-session charges, without immediately charging the card. | | **PaymentIntent** | A Stripe object representing the intent to collect a payment. Used during settlement to charge the delegated card. | | **Off-session payment** | A Stripe payment executed without the cardholder being actively present, using a previously saved payment method. | @@ -220,7 +225,7 @@ The `token` field contains a signed JWT with the following claims: | `iat` | `number` | Yes | Issued-at timestamp (Unix epoch seconds). | | `exp` | `number` | Yes | Expiration timestamp (Unix epoch seconds). | | `nvm.delegationId` | `string` | Yes | Unique delegation identifier (UUID). MUST match the `jti` claim. | -| `nvm.provider` | `string` | Yes | Payment provider identifier. MUST be `"stripe"`. | +| `nvm.provider` | `string` | Yes | Payment provider identifier. MUST be `"stripe"` or `"erc4337"`. | | `nvm.providerCustomerId` | `string` | Yes | Stripe Customer ID (e.g., `"cus_PaBcDeFgHiJk"`). | | `nvm.providerPaymentMethodId` | `string` | Yes | Stripe PaymentMethod ID for the enrolled card (e.g., `"pm_1AbCdEfGhIjKlM"`). | | `nvm.spendingLimitCents` | `number` | Yes | Maximum amount in cents that can be charged over the delegation lifetime. | @@ -387,7 +392,30 @@ Card enrollment only needs to happen once per payment method. A single enrolled #### Phase 1: Delegation Creation (Steps 9-12) -**Step 9.** Client requests a delegation from the facilitator, specifying the scheme, spending limits, and optional constraints. The request body follows the `GenerateX402TokenDto` structure, where `accepted` identifies the payment scheme and `delegationConfig` contains card-delegation-specific options. +**Step 9.** Client creates a delegation using the `createDelegation` API, specifying the `provider` (required: `'stripe'` or `'erc4337'`), spending limits, and optional constraints. The `DelegationConfig` interface is shared across both providers. + +Alternatively, the client can use `getX402AccessToken` with an inline `delegationConfig` to auto-create a delegation during token generation. + +**Using the `createDelegation` API directly:** + +```http +POST /api/v1/payments/delegation HTTP/1.1 +Host: facilitator.example.com +Authorization: Bearer +Content-Type: application/json + +{ + "provider": "stripe", + "spendingLimitCents": 10000, + "durationSecs": 2592000, + "providerPaymentMethodId": "pm_1AbCdEfGhIjKlM", + "currency": "usd", + "maxTransactions": 100, + "merchantAccountId": "acct_1AbCdEfGhIjKlM" +} +``` + +**Using `getX402AccessToken` with inline delegation (combines Steps 9-12):** ```http POST /x402/permissions HTTP/1.1 @@ -410,12 +438,7 @@ Content-Type: application/json } }, "delegationConfig": { - "providerPaymentMethodId": "pm_1AbCdEfGhIjKlM", - "spendingLimitCents": 10000, - "durationSecs": 2592000, - "currency": "usd", - "maxTransactions": 100, - "merchantAccountId": "acct_1AbCdEfGhIjKlM" + "delegationId": "deleg-8f14e45f-ce34-4797-b88e-968374b0d4b6" } } ``` diff --git a/skills/nevermined-payments/SKILL.md b/skills/nevermined-payments/SKILL.md index 91599b0..81de4f9 100644 --- a/skills/nevermined-payments/SKILL.md +++ b/skills/nevermined-payments/SKILL.md @@ -126,7 +126,7 @@ Every Nevermined payment integration follows this 5-step pattern: 1. **Client sends request** without a payment token 2. **Server returns 402** with `payment-required` header (base64-encoded JSON with plan info) -3. **Client acquires x402 token** via `payments.x402.getX402AccessToken(planId, agentId)` +3. **Client acquires x402 token** via `payments.x402.getX402AccessToken(planId, agentId, { delegationConfig })` — requires a delegation (either an existing `delegationId` or `spendingLimitCents` + `durationSecs` to auto-create one) 4. **Client retries** with `payment-signature` header containing the token 5. **Server verifies → executes → settles** (burns credits), returns response with `payment-response` header @@ -142,7 +142,7 @@ Choose the integration that matches your stack: | **MCP Server** | TypeScript | `references/mcp-paywall.md` | `payments.mcp.start()` / `payments.mcp.registerTool()` | | **Google A2A** | TS / Python | `references/a2a-integration.md` | `payments.a2a.start()` / `payments.a2a.buildPaymentAgentCard()` | | **Any HTTP** | Any | `references/x402-protocol.md` | Manual verify/settle via facilitator API | -| **Client-side** | TS / Python | `references/client-integration.md` | `payments.x402.getX402AccessToken()` | +| **Client-side** | TS / Python | `references/client-integration.md` | `payments.x402.getX402AccessToken()` with `delegationConfig` | ## SDK Quick Reference @@ -160,7 +160,9 @@ const { agentId, planId } = await payments.agents.registerAgentAndPlan( // Subscriber: order plan and get token await payments.plans.orderPlan(planId) const balance = await payments.plans.getPlanBalance(planId) -const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId) +const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId, { + delegationConfig: { spendingLimitCents: 10000, durationSecs: 604800 } +}) // Server: verify and settle const verification = await payments.facilitator.verifyPermissions({ @@ -189,6 +191,8 @@ await client.sendMessage("Hello", accessToken) ### Python (`payments-py`) ```python +from payments_py.x402 import DelegationConfig, X402TokenOptions + # Initialize payments = Payments.get_instance(PaymentOptions(nvm_api_key=key, environment="sandbox")) @@ -200,7 +204,12 @@ result = payments.agents.register_agent_and_plan( # Subscriber: order plan and get token payments.plans.order_plan(plan_id) balance = payments.plans.get_plan_balance(plan_id) -token_res = payments.x402.get_x402_access_token(plan_id, agent_id) +token_res = payments.x402.get_x402_access_token( + plan_id, agent_id, + token_options=X402TokenOptions( + delegation_config=DelegationConfig(spending_limit_cents=10000, duration_secs=604800) + ) +) # Server: verify and settle verification = payments.facilitator.verify_permissions( @@ -400,7 +409,9 @@ const client = payments.a2a.getClient({ planId: PLAN_ID, }) -const { accessToken } = await payments.x402.getX402AccessToken(PLAN_ID, AGENT_ID) +const { accessToken } = await payments.x402.getX402AccessToken(PLAN_ID, AGENT_ID, { + delegationConfig: { spendingLimitCents: 10000, durationSecs: 604800 } +}) const response = await client.sendMessage("Analyze this data", accessToken) ``` @@ -508,7 +519,7 @@ curl -X POST http://localhost:3000/chat \ | Symptom | Cause | Fix | |---|---|---| -| HTTP 402 returned | No `payment-signature` header or invalid/expired token | Generate a fresh token via `getX402AccessToken` | +| HTTP 402 returned | No `payment-signature` header or invalid/expired token | Generate a fresh token via `getX402AccessToken` with `delegationConfig` | | MCP error `-32003` | Payment Required — no token, invalid token, or insufficient credits | Check subscriber has purchased plan and has credits remaining | | MCP error `-32002` | Server misconfiguration | Verify `NVM_API_KEY`, `NVM_PLAN_ID`, and `NVM_AGENT_ID` are set correctly | | `verification.isValid` is false | Token expired, wrong plan, or insufficient credits | Re-order the plan or generate a new token | diff --git a/skills/nevermined-payments/references/a2a-integration.md b/skills/nevermined-payments/references/a2a-integration.md index 2fcdc9d..5af4a06 100644 --- a/skills/nevermined-payments/references/a2a-integration.md +++ b/skills/nevermined-payments/references/a2a-integration.md @@ -222,8 +222,10 @@ client = payments_subscriber.a2a.get_client( // Purchase the plan await paymentsSubscriber.plans.orderPlan(planId) -// Get the x402 access token -const { accessToken } = await paymentsSubscriber.x402.getX402AccessToken(planId, agentId) +// Get the x402 access token (requires delegationConfig) +const { accessToken } = await paymentsSubscriber.x402.getX402AccessToken(planId, agentId, { + delegationConfig: { spendingLimitCents: 10000, durationSecs: 604800 } +}) // Send an A2A message const response = await client.sendMessage("Hello, analyze this data!", accessToken) diff --git a/skills/nevermined-payments/references/client-integration.md b/skills/nevermined-payments/references/client-integration.md index 3b2e345..52dd112 100644 --- a/skills/nevermined-payments/references/client-integration.md +++ b/skills/nevermined-payments/references/client-integration.md @@ -30,8 +30,10 @@ await payments.plans.orderPlan(PLAN_ID) const balance = await payments.plans.getPlanBalance(PLAN_ID) console.log(`Credits remaining: ${balance}`) -// Generate x402 access token -const { accessToken } = await payments.x402.getX402AccessToken(PLAN_ID, AGENT_ID) +// Generate x402 access token (requires delegationConfig) +const { accessToken } = await payments.x402.getX402AccessToken(PLAN_ID, AGENT_ID, { + delegationConfig: { spendingLimitCents: 10000, durationSecs: 604800 } +}) ``` ### Python @@ -39,6 +41,7 @@ const { accessToken } = await payments.x402.getX402AccessToken(PLAN_ID, AGENT_ID ```python import os from payments_py import Payments, PaymentOptions +from payments_py.x402 import DelegationConfig, X402TokenOptions payments = Payments.get_instance( PaymentOptions(nvm_api_key=os.environ["NVM_API_KEY"], environment="sandbox") @@ -51,8 +54,13 @@ payments.plans.order_plan(plan_id) balance = payments.plans.get_plan_balance(plan_id) print(f"Credits remaining: {balance}") -# Generate x402 access token -token_res = payments.x402.get_x402_access_token(plan_id, agent_id) +# Generate x402 access token (requires delegationConfig) +token_res = payments.x402.get_x402_access_token( + plan_id, agent_id, + token_options=X402TokenOptions( + delegation_config=DelegationConfig(spending_limit_cents=10000, duration_secs=604800) + ) +) access_token = token_res["accessToken"] ``` @@ -91,8 +99,10 @@ async function callProtectedAPI() { const { planId, extra } = paymentRequired.accepts[0] const agentId = extra?.agentId - // Step 3: Generate x402 token - const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId) + // Step 3: Generate x402 token (requires delegationConfig) + const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId, { + delegationConfig: { spendingLimitCents: 10000, durationSecs: 604800 } + }) // Step 4: Request with token → 200 const response2 = await fetch(`${SERVER_URL}/ask`, { @@ -127,6 +137,7 @@ import base64 import json import httpx from payments_py import Payments, PaymentOptions +from payments_py.x402 import DelegationConfig, X402TokenOptions payments = Payments.get_instance( PaymentOptions( @@ -156,8 +167,15 @@ def call_protected_api(): plan_id = payment_required["accepts"][0]["planId"] agent_id = payment_required["accepts"][0].get("extra", {}).get("agentId") - # Step 3: Generate x402 token - token_result = payments.x402.get_x402_access_token(plan_id, agent_id) + # Step 3: Generate x402 token (requires delegationConfig) + token_result = payments.x402.get_x402_access_token( + plan_id, agent_id, + token_options=X402TokenOptions( + delegation_config=DelegationConfig( + spending_limit_cents=10000, duration_secs=604800 + ) + ) + ) access_token = token_result["accessToken"] # Step 4: Request with token → 200 @@ -190,7 +208,9 @@ if __name__ == "__main__": import { Client } from "@modelcontextprotocol/sdk/client" import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp" -const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId) +const { accessToken } = await payments.x402.getX402AccessToken(planId, agentId, { + delegationConfig: { spendingLimitCents: 10000, durationSecs: 604800 } +}) const transport = new StreamableHTTPClientTransport( new URL("http://localhost:3000/mcp"), @@ -215,6 +235,7 @@ const result = await client.callTool({ ### Python ```python +from payments_py.x402 import DelegationConfig, X402TokenOptions from payments_py.x402.strands import extract_payment_required from agent import agent, payments @@ -229,8 +250,15 @@ if payment_required: plan_id = chosen_plan["planId"] agent_id = (chosen_plan.get("extra") or {}).get("agentId") - # Step 3: Get token - token_response = payments.x402.get_x402_access_token(plan_id, agent_id) + # Step 3: Get token (requires delegationConfig) + token_response = payments.x402.get_x402_access_token( + plan_id, agent_id, + token_options=X402TokenOptions( + delegation_config=DelegationConfig( + spending_limit_cents=10000, duration_secs=604800 + ) + ) + ) access_token = token_response["accessToken"] # Step 4: Retry with token diff --git a/skills/nevermined-payments/references/mcp-paywall.md b/skills/nevermined-payments/references/mcp-paywall.md index 5943e09..5aa3d62 100644 --- a/skills/nevermined-payments/references/mcp-paywall.md +++ b/skills/nevermined-payments/references/mcp-paywall.md @@ -154,7 +154,9 @@ After each paywall-protected call, the SDK injects a `_meta` field into the resp ### Get Access Token ```typescript -const { accessToken } = await paymentsClient.x402.getX402AccessToken(planId, agentId) +const { accessToken } = await paymentsClient.x402.getX402AccessToken(planId, agentId, { + delegationConfig: { spendingLimitCents: 10000, durationSecs: 604800 } +}) ``` ### Connect with MCP Client diff --git a/skills/nevermined-payments/references/strands-integration.md b/skills/nevermined-payments/references/strands-integration.md index 55aa739..bc63417 100644 --- a/skills/nevermined-payments/references/strands-integration.md +++ b/skills/nevermined-payments/references/strands-integration.md @@ -67,6 +67,7 @@ In Strands, the LLM sees the error and relays it to the user in natural language ```python from payments_py import Payments, PaymentOptions +from payments_py.x402 import DelegationConfig, X402TokenOptions from payments_py.x402.strands import extract_payment_required from agent import agent, payments @@ -85,6 +86,11 @@ if payment_required: token_response = payments.x402.get_x402_access_token( plan_id=plan_id, agent_id=agent_id, + token_options=X402TokenOptions( + delegation_config=DelegationConfig( + spending_limit_cents=10000, duration_secs=604800 + ) + ), ) access_token = token_response["accessToken"]