Revenir au blog

Suivre et s’abonner

Building Metered Streams with MPP and Fastly Compute

Learn how to build an MPP streaming demo on Fastly Compute comparing Stripe Shared Payment Tokens and native Tempo payment sessions for pay-as-you-go agent APIs.

Shaun Flagg
Shaun FlaggIngénieur cloud senior, Fastly

In the previous post in this series, we built an x402 application that allowed a machine client to receive an HTTP 402 payment challenge, pay it, retry the request, and access a protected resource.

That flow works well when the transaction maps to a single request. However, many automated services will have a longer relationship between payment and delivery. An agent might consume generated output over several seconds, retrieve a sequence of results, subscribe to a data feed, or pay for units of work as they are completed.

Stripe and Tempo’s Machine Payments Protocol, or MPP, is designed for programmatic payments between agents and services. It uses HTTP 402 to communicate payment requirements while supporting different payment methods and intents, including both one-time charges and longer-lived payment sessions.

For this post, you will learn to build an MPP streaming demo on Fastly Compute that compares two approaches:

  • A Stripe Shared Payment Token, or SPT, that authorizes a fiat-backed charge before the stream begins.

  • A native Tempo payment session that increases the authorized amount as the client consumes each unit.

The two flows produce a similar experience in the browser, but the way payment progresses underneath them is different.

A Quick Disclaimer

The code and implementations shared in this post are designed strictly as an experimental reference demo to showcase MPP streaming concepts on Fastly Compute. This project is not production-ready. Please explore, adapt, and test at your own risk, and review the Production Considerations section at the bottom before adapting this for live environments.

When Payment Has to Continue with Delivery

A normal paid API request has a clear boundary. The client receives a challenge, supplies payment, and gets the response.

Streaming makes that boundary less obvious. The service has to know how much the client authorized, whether authorization should increase as delivery continues, and what happens when the client disconnects before the service has finished.

MPP represents these decisions through payment intents.

The Stripe implementation in this demo uses intent=charge. It authorizes a fixed amount before streaming starts, and the application meters delivery within that budget.

The Tempo implementation uses intent=session. It opens a payment channel and advances cumulative authorization as the client consumes additional units.

The Demo Application

The application exposes two streaming routes:

GET /stripe-stream
GET /tempo-stream

The browser interface lets you inspect the unpaid 402 challenge for either route or run the complete paid flow. 

As the response is delivered, it displays the following data:

  • the selected payment rail

  • the number of units consumed

  • the unit price

  • the cumulative amount

  • the final settlement event.

The demo uses server-side payer helpers so it can be shown without a separate agent or wallet extension. Those helpers are only there to make the protocol easier to demonstrate.

Stripe SPT-Funded Streaming

The Stripe flow authorizes the full budget before delivery begins.

An agent first requests /stripe-stream without a payment credential. The service responds with an MPP challenge specifying the Stripe payment method and intent=charge.

The payer uses the challenge to create a test Shared Payment Token. That token includes the payment method, currency, maximum authorized amount, and expiration time. The payer packages it into an MPP credential and retries the request.

The service validates the credential and creates a Stripe PaymentIntent. Once the charge has been authorized, the streamed response begins.

The default demo authorizes $0.50 and divides the stream into ten visual units. When delivery begins, the client receives an event describing the authorized budget:

{
  "type": "session-started",
  "rail": "stripe-spt",
  "intent": "charge",
  "settlement": "preauthorized-budget",
  "authorizedAmount": "0.50",
  "currency": "usd",
  "units": 10
}

Each following event includes the current unit and the cumulative amount represented by the delivered stream. When all units have been sent, the service returns a completion event and closes the connection. The response also includes the MPP Payment-Receipt.

The per-unit meter does not represent a separate Stripe charge for every chunk. One SPT-backed charge authorizes the configured amount before the stream starts. The application then tracks how delivery progresses within that budget.

If the client stops early, the demo does not automatically refund the unused amount. A production implementation would need to define how it handles partial capture, credits, refunds, or unused authorization.

Native MPP Sessions with Tempo

The Tempo flow ties authorization more directly to consumption.

The browser first subscribes to a session-specific endpoint through Fastly Fanout:

GET /fanout/tempo/:sessionId

Fanout holds the long-lived browser connection. Pushpin provides the same behavior during local development.

The payer then requests the first unit from /tempo-stream. The service responds with an MPP challenge specifying method=tempo and intent=session.

The payer opens a payment channel and retries the request. After the first unit has been verified, Compute publishes it to the browser’s Fanout channel.

Additional units reuse the same MPP session. The payer increases its cumulative authorization, the service verifies the new amount, and Compute publishes the next unit.

The session manager preserves the payment channel across a sequence of finite HTTP requests. Fastly KV Store holds the session state so the unit and management requests can observe the same channel.

Fanout is the only long-lived transport in this design. The browser keeps one streaming connection open, while Compute handles the individual payment and delivery operations as bounded requests.

This separation is useful because the MPP session can continue logically without requiring the payer to maintain another internal SSE connection inside Compute.

Once the final unit has been delivered, the payer closes the channel and Tempo settles the cumulative amount.

Two Payment Models, One Streaming Experience

The interface makes the difference between the two flows easier to see.

Stripe SPT

Tempo session

Uses intent=charge

Uses intent=session

Authorizes a fixed budget before delivery

Advances authorization during consumption

Uses a Stripe PaymentIntent

Uses a payment channel and cumulative vouchers

Meters units at the application layer

Associates paid units with the native session

Returns a Payment-Receipt

Settles when the session closes

The Stripe model is a good fit when the service knows the maximum price before delivery begins. The Tempo model is closer to pay-as-you-go consumption, where the authorized amount increases throughout the interaction.

Streaming on Fastly Compute

The demo required a small number of runtime adaptations. The application keeps generated SSE responses tied to the fetch event lifetime, provides compatibility for the MPP library’s use of Response.clone(), and routes the Tempo RPC traffic through a named Fastly backend.

These changes are contained within the demo and allow the payment logic to run without buffering the live stream or creating another long-lived connection inside Compute.

Where This Could Be Used

This pattern could support any service where value is delivered over time or in measurable units.

An AI service could authorize a budget and report consumption as generated output is delivered. A data provider could charge for a premium feed. A crawler could pay for licensed content or structured data. One service could pay another for a sequence of processing operations.

The appropriate payment model depends on the service. Some applications will prefer a fixed budget that is approved before work starts. Others will need payment authorization to advance alongside consumption.

MPP allows both approaches to use the same HTTP payment framework while leaving the payment rail to handle the underlying authorization and settlement.

Production Considerations

This repository is a demonstration rather than a complete production payment proxy.

The server-side payer helpers should be removed before the service is exposed publicly. Stripe secrets, MPP signing secrets, and private keys should be stored in Fastly Secret Store, while pricing and rail configuration should move into Config Store.

The included Fastly KV adapter supports the sequential, single-payer Tempo demo. A production system with concurrent voucher writers or broad multi-POP use would need atomic session-state handling.

A complete implementation would also need request binding, replay protection, spending limits, rate limits, observability, idempotency, and a defined policy for interrupted streams and unused authorized budgets.

From Paid Requests to Paid Streams

The previous x402 demo showed how an automated client can pay for a protected request. This MPP demo extends that model to interactions where payment and delivery continue together.

The Stripe flow authorizes a fiat-backed budget before the stream begins. The Tempo flow opens a native payment session and advances cumulative authorization as units are consumed.

The demo also shows how Fanout can maintain the long-lived client connection while Compute handles the finite payment and delivery operations behind it.

The complete implementation is available in the demo repository on GitHub.

Prêt à commencer ?

Contactez-nous dès aujourd’hui