Skip to content

Subscriptions: Intent + Stripe and Tempo Implementations#230

Open
brendanjryan wants to merge 16 commits into
mainfrom
redraft-subscription-intent
Open

Subscriptions: Intent + Stripe and Tempo Implementations#230
brendanjryan wants to merge 16 commits into
mainfrom
redraft-subscription-intent

Conversation

@brendanjryan

@brendanjryan brendanjryan commented Apr 8, 2026

Copy link
Copy Markdown
Collaborator

Summary

This PR adds the subscription payment intent for recurring fixed-amount Payment authentication, plus Stripe and Tempo method profiles.

The shared intent defines activation, renewal, cancellation, subscription identifiers, canonical period semantics, durable accounting/idempotency, receipts, and error behavior.

Stripe maps the intent to a constrained Stripe Billing profile. Tempo maps it to TIP-1011 periodic access-key authorizations.

Notable design decisions / tightening of spec

  • The shared intent is intentionally narrow: fixed amount, one charge per billing period. It does not model full billing-product behavior like plans, seats, prorations, trials, discounts, metered usage, or plan changes.

  • Payment methods must reject request shapes they cannot represent exactly. Shared periods support fixed elapsed day/week periods and calendar-month periods anchored at activation. Stripe supports exact day/week/month cadence; Tempo supports day/week and rejects month because TIP-1011 periods are fixed elapsed seconds.

  • Activation includes the first billing-period charge. A subscription is not active until setup and the first charge succeed, and the receipt includes a subscriptionId.

  • Renewals require durable per-period accounting. Missed periods do not accumulate extra charge authority, and duplicate requests, retries, webhooks, or concurrent requests must not produce duplicate charges.

  • Stripe is constrained to one customer, one subscription, one recurring price, quantity 1, synchronous first-invoice payment, and validated paid invoices mapped to canonical billing periods.

  • Tempo requires bounded authorization with subscriptionExpires, exact TokenLimit amount/period matching, recipient-scoped transfer selectors, source verification, and access-key isolation guidance.

Control flow

flowchart TD
  A["Client requests protected resource"] --> B{"Active subscription for resource?"}
  B -- "yes, current period paid" --> C["Return receipt + resource"]
  B -- "yes, new unpaid period" --> D["Collect renewal via method profile"]
  D --> E["Durably record paid period"]
  E --> C
  B -- "no / expired / canceled / revoked" --> F["Return 402 subscription challenge"]
  F --> G["Client signs method-specific subscription credential"]
  G --> H["Server verifies challenge, request, payer, method scope"]
  H --> I{"Payment method"}
  I -- "Stripe" --> J["Create/reuse constrained Stripe subscription + paid first invoice"]
  I -- "Tempo" --> K["Register scoped access key + transfer first charge"]
  J --> L["Record subscription state + billing anchor"]
  K --> L
  L --> M["Return Payment-Receipt with subscriptionId"]
Loading

@github-actions

github-actions Bot commented Apr 8, 2026

Copy link
Copy Markdown

Spec Preview

Spec Changed Artifacts
draft-card-charge-00 - HTML · TXT · XML · PDF
draft-evm-charge-00 - HTML · TXT · XML · PDF
draft-httpauth-payment-00 - HTML · TXT · XML · PDF
draft-lightning-charge-00 - HTML · TXT · XML · PDF
draft-lightning-session-00 - HTML · TXT · XML · PDF
draft-payment-discovery-00 - HTML · TXT · XML · PDF
draft-payment-intent-charge-00 - HTML · TXT · XML · PDF
draft-payment-intent-subscription-00 New HTML · TXT · XML · PDF
draft-payment-transport-mcp-00 - HTML · TXT · XML · PDF
draft-solana-charge-00 - HTML · TXT · XML · PDF
draft-stellar-charge-00 - HTML · TXT · XML · PDF
draft-stripe-charge-00 - HTML · TXT · XML · PDF
draft-stripe-subscription-00 New HTML · TXT · XML · PDF
draft-tempo-charge-00 - HTML · TXT · XML · PDF
draft-tempo-session-00 - HTML · TXT · XML · PDF
draft-tempo-subscription-00 New HTML · TXT · XML · PDF

Browse preview release assets

Comment thread specs/methods/tempo/draft-tempo-subscription-00.md Outdated
Comment thread specs/methods/tempo/draft-tempo-subscription-00.md Outdated
Comment thread specs/methods/tempo/draft-tempo-subscription-00.md
@brendanjryan brendanjryan marked this pull request as ready for review April 9, 2026 00:21

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 62334f6e06

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".


| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `methodDetails.chainId` | number | OPTIONAL | Tempo chain ID. If omitted, the default value is 4217 (Tempo mainnet). |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Align default Tempo chain ID across method specs

Set methodDetails.chainId default to the same value used by the other Tempo method drafts (specs/methods/tempo/draft-tempo-charge-00.md:154 and specs/methods/tempo/draft-tempo-session-00.md:543 both use 42431). Keeping 4217 here means clients that omit chainId will derive signatures and did:pkh identifiers on a different chain than existing Tempo payment flows, which can cause valid subscription credentials to fail verification or be charged on the wrong network.

Useful? React with 👍 / 👎.

…on guidance

- Remove placeholder review notes from intent and tempo method specs
- Add T3 network upgrade requirement for TIP-1011 features
- Rename 'Tempo Network' to 'Tempo' in ASCII diagrams
- Add Access Key Isolation section: servers SHOULD use one key per
  subscription for fault isolation; documents risks of key reuse
  including shared TokenLimit, wider blast radius, and bulk revocation
date: 2024-06
---

--- abstract

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm curious if there is a doc on the motivation? Eg, why build subscriptions into the tempo layer vs utilize a billing system and then create payment on Tempo? Not a statement of judgement, just trying to understand the product goals.

| `amount` | string | Fixed payment amount per billing period in base units |
| `currency` | string | Currency or asset identifier (see {{currency-formats}}) |
| `periodSeconds` | string | Billing period duration in seconds |
| `subscriptionExpires` | string | Subscription expiry timestamp in {{RFC3339}} format |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why does a subscription need to expire? Eg, if you sign up for a service like Netflix there is not expiration

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's fair -- I think we could have this be optional and only have a firm expiry if desired

alternative we could remove and add later as an optional field to reduce API surface

|-------|------|-------------|
| `amount` | string | Fixed payment amount per billing period in base units |
| `currency` | string | Currency or asset identifier (see {{currency-formats}}) |
| `periodSeconds` | string | Billing period duration in seconds |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the start date? Often subscriptions do not start now() but anchored to something like UTC midnight.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is defined later -- but start date should be immediately, this could have some variability at the payment method (e..g stripe, tempo) level though


# Terminology

Subscription

@tarasmitran-stripe tarasmitran-stripe Apr 14, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a fairly narrow definition, that does not match the billing perspective, which might be fine, but who is the audience for this concept and would they be confused?

One other thought - given the big definition difference between this subscription and a billing subscription - should we consider another name (maybe 'recurring')?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recurring is interesting -- that may be more semantically correct as this is a charge x interval. Will think about this a bit more

`methodDetails`, but MUST define exact activation semantics if they do
so.

The billing anchor for a subscription is the time activation succeeds.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

services even such as Netflix support resetting the billing cycle anchor - is that a trapdoor decision here?


| Field | Type | Description |
|-------|------|-------------|
| `amount` | string | Fixed payment amount per billing period in base units |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

most subscription systems do not take a fixed amount, but rather a price and quantity, and compute the amount. Eg, you are buying 5 seats to a SaaS system

@brendanjryan brendanjryan changed the title [draft] Subscription intent Draft subscription intent and profiles May 6, 2026
@brendanjryan brendanjryan changed the title Draft subscription intent and profiles Draft subscription intent May 7, 2026
@brendanjryan brendanjryan changed the title Draft subscription intent Subscriptions: Intent + Stripe and Tempo Implementations May 7, 2026

1. Create or reuse a Stripe Customer for the payer
2. Attach or select the challenged `paymentMethod` for that Customer
3. Create or reuse a Stripe Price whose amount, currency, and recurring

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the Stripe Product?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIRC Stripe requires a Price to belong to a Product, but in this case, product selection does not affect the protocol authorization.

I think we can treat product as an implementation detail here? or we could also specify to be very explicit if desired

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, its an implementation detail in general, but it does create a user-facing entity since the price requires the product to exist

billing period, and MUST record that cancellation effective time in
durable local state. The server MAY cancel immediately only if the
application separately handles any already-paid access period without
creating an additional charge.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you un-cancel before period end?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to keep things simple for now let's say no

object still matches this profile before treating the retry as
successful.

## Unsupported Stripe Billing Features

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What did we decide would happen if the user goes and modifies the stripe subscription and adds one of these features?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

at this point we would consider this "out of protocol" and something that I think could be disputed along the given rail (this is easier to do in stripe vs. in crypto which is strictly defined by the protocol)

@chopmob-cloud

This comment was marked as spam.

@chopmob-cloud

This comment was marked as spam.

chopmob-cloud added a commit to chopmob-cloud/AlgoVoi-MCP-Server that referenced this pull request May 20, 2026
Closes the gap surfaced after the gateway-side MPP subscription work
(tempoxyz/mpp-specs#230): agents using the MCP could probe one-shot
MPP endpoints via try_mpp_endpoint but had no way to discover, activate,
or manage MPP-protocol subscriptions. Three new tools added:

  - try_mpp_subscription — probe a public /mpp/sub/{tenant_short_id}/
    {resource_id} URL, parse the 402 dual envelope (RFC 9457 or canonical
    x402 v2), surface subscription-specific extras (periodCount,
    periodUnit, intent) so the agent can present terms before signing.
    Unauthenticated; works against any MPP subscription URL.

  - list_mpp_subscriptions — admin tool, calls
    GET /internal/tenants/{tenant_id}/mpp-subscriptions. Optional status
    filter and pagination. Requires admin-scope API key.

  - cancel_mpp_subscription — admin tool, calls
    POST /internal/tenants/{tenant_id}/mpp-subscriptions/{id}/cancel.
    Idempotent — already-cancelled subscriptions return safely. Current
    period's access (paid) is unaffected.

Also adds MPP_SUBSCRIPTION_EVENT_TYPES to schemas.py covering the four
webhook events queued by the gateway:
  mpp_subscription.activated/charged/revoked/expired

E2E verified against all 7 chain subscription fixtures (Algorand, VOI,
Solana, Hedera, Stellar, Tempo testnets + Base sepolia mainnet-style).
Each returns the correct accepts[] shape with period=1/day, amount and
per-chain payTo populated. Unknown-resource URL correctly returns 404.

Schema-strict-mode parse rejects: non-HTTPS URLs, unknown extra fields.

Schema sanity-check count bumped 25 -> 28 (`assert len(_EXPECTED) == 28`).
chopmob-cloud added a commit to chopmob-cloud/AlgoVoi-MCP-Server that referenced this pull request May 20, 2026
Version bump from 1.4.x to 1.5.0 across Python (algovoi-mcp) and
TypeScript (@algovoi/mcp-server) packages.

What's new in 1.5.0:

1. MPP subscription tools (3 new) for tempoxyz/mpp-specs#230 surface:
   - try_mpp_subscription (unauth probe of public /mpp/sub URLs)
   - list_mpp_subscriptions (admin)
   - cancel_mpp_subscription (admin)
   Already added in 374fe8f; this commit only handles the release-wrap.

2. ARC testnet support — new network entry "arc_testnet" in both
   Python NETWORKS and TypeScript NETWORKS arrays plus NETWORK_INFO
   metadata. Tool count now 28 across 8 chains (was 25 across 7).

3. VOI USDC description fix: "ARC-200 302190" → "native ASA 302190"
   (accuracy correction — 302190 is an Algorand-format ASA, not
   ARC-200 token contract).

4. smoke_mcp_full.py expects 26 list_networks entries (13 mainnet +
   12 testnet + 1 arc_testnet).

5. Package descriptions updated to "28 tools across all 8 AlgoVoi
   chains" in pyproject.toml + typescript/package.json. User-Agent
   bumped to algovoi-mcp/1.5.0 in client.py + MCP server-info
   version bumped in typescript/src/index.ts.

Schema sanity check at import: assert len(SCHEMAS_BY_TOOL) == 28.

Webhook event catalog gains MPP_SUBSCRIPTION_EVENT_TYPES tuple
(mpp_subscription.activated/charged/revoked/expired) for the
verify_webhook tool's event-type catalog visibility.
@brendanjryan

Copy link
Copy Markdown
Collaborator Author

Merging this in as this has now been implemented in Tempo, with Stripe support to follow.

We will open incremental PRs if there are any changes needed

@brendanjryan brendanjryan enabled auto-merge (squash) May 25, 2026 12:54
chopmob-cloud added a commit to chopmob-cloud/AlgoVoi-MCP-Server that referenced this pull request May 29, 2026
Closes the gap surfaced after the gateway-side MPP subscription work
(tempoxyz/mpp-specs#230): agents using the MCP could probe one-shot
MPP endpoints via try_mpp_endpoint but had no way to discover, activate,
or manage MPP-protocol subscriptions. Three new tools added:

  - try_mpp_subscription — probe a public /mpp/sub/{tenant_short_id}/
    {resource_id} URL, parse the 402 dual envelope (RFC 9457 or canonical
    x402 v2), surface subscription-specific extras (periodCount,
    periodUnit, intent) so the agent can present terms before signing.
    Unauthenticated; works against any MPP subscription URL.

  - list_mpp_subscriptions — admin tool, calls
    GET /internal/tenants/{tenant_id}/mpp-subscriptions. Optional status
    filter and pagination. Requires admin-scope API key.

  - cancel_mpp_subscription — admin tool, calls
    POST /internal/tenants/{tenant_id}/mpp-subscriptions/{id}/cancel.
    Idempotent — already-cancelled subscriptions return safely. Current
    period's access (paid) is unaffected.

Also adds MPP_SUBSCRIPTION_EVENT_TYPES to schemas.py covering the four
webhook events queued by the gateway:
  mpp_subscription.activated/charged/revoked/expired

E2E verified against all 7 chain subscription fixtures (Algorand, VOI,
Solana, Hedera, Stellar, Tempo testnets + Base sepolia mainnet-style).
Each returns the correct accepts[] shape with period=1/day, amount and
per-chain payTo populated. Unknown-resource URL correctly returns 404.

Schema-strict-mode parse rejects: non-HTTPS URLs, unknown extra fields.

Schema sanity-check count bumped 25 -> 28 (`assert len(_EXPECTED) == 28`).
chopmob-cloud added a commit to chopmob-cloud/AlgoVoi-MCP-Server that referenced this pull request May 29, 2026
Version bump from 1.4.x to 1.5.0 across Python (algovoi-mcp) and
TypeScript (@algovoi/mcp-server) packages.

What's new in 1.5.0:

1. MPP subscription tools (3 new) for tempoxyz/mpp-specs#230 surface:
   - try_mpp_subscription (unauth probe of public /mpp/sub URLs)
   - list_mpp_subscriptions (admin)
   - cancel_mpp_subscription (admin)
   Already added in 374fe8f; this commit only handles the release-wrap.

2. ARC testnet support — new network entry "arc_testnet" in both
   Python NETWORKS and TypeScript NETWORKS arrays plus NETWORK_INFO
   metadata. Tool count now 28 across 8 chains (was 25 across 7).

3. VOI USDC description fix: "ARC-200 302190" → "native ASA 302190"
   (accuracy correction — 302190 is an Algorand-format ASA, not
   ARC-200 token contract).

4. smoke_mcp_full.py expects 26 list_networks entries (13 mainnet +
   12 testnet + 1 arc_testnet).

5. Package descriptions updated to "28 tools across all 8 AlgoVoi
   chains" in pyproject.toml + typescript/package.json. User-Agent
   bumped to algovoi-mcp/1.5.0 in client.py + MCP server-info
   version bumped in typescript/src/index.ts.

Schema sanity check at import: assert len(SCHEMAS_BY_TOOL) == 28.

Webhook event catalog gains MPP_SUBSCRIPTION_EVENT_TYPES tuple
(mpp_subscription.activated/charged/revoked/expired) for the
verify_webhook tool's event-type catalog visibility.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants