Migrate from Stripe to Blockchain0x for agent-driven API access.
Do not replace Stripe; run it alongside. A single gate fronts each protected endpoint with two auth methods: an active Stripe subscription (for humans), otherwise the x402 receive-side adapter (for agents), which issues a 402 challenge and verifies the X-Payment header on the retry. The handler logic does not change.
Before you start.
- A working Stripe integration with at least one active product/price (subscription or one-off).
- A Blockchain0x agent profile and API key (see add-payments-to-agent).
- An auth/middleware layer in your web framework where you currently call Stripe to gate access.
- A feature-flag mechanism (env var, LaunchDarkly, simple boolean - anything that lets you toggle behavior without redeploy).
- Understanding of the x402 pattern - the decorator below implements it.
Write the dual-auth gate.
The gate is the single piece of glue. It checks for an active Stripe subscription first (the human path); if there is none, it hands the request to the x402 receive-side adapter (the agent path), which issues the 402 challenge and verifies the X-Payment header when the agent retries. The Node example uses createX402Middleware; a Python service speaks the same wire by hand.
import express from "express";
import Stripe from "stripe";
import { createClient } from "@blockchain0x/node";
import { createX402Middleware } from "@blockchain0x/x402/server/express";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);
const sdk = createClient({ apiKey: process.env.BLOCKCHAIN0X_API_KEY! });
// Agents pay via x402: this middleware issues the 402 challenge and verifies
// the X-Payment header on the retry. Configure price + recipient per the docs.
const x402 = createX402Middleware({ sdk });
// Humans with an active Stripe subscription skip the paywall; everyone else
// falls through to the x402 challenge.
function stripeOrX402(req: express.Request, res: express.Response, next: express.NextFunction) {
const customer = req.cookies?.stripe_customer_id;
if (!customer) return x402(req, res, next); // agent path
stripe.subscriptions
.list({ customer, status: "active", limit: 1 })
.then((subs) => (subs.data.length > 0 ? next() : x402(req, res, next)))
.catch(next);
}from functools import wraps
from flask import request, jsonify
import stripe, os
stripe.api_key = os.environ["STRIPE_SECRET_KEY"]
# The x402 receive-side adapter is Node; a Python service speaks the wire
# directly: advertise requirements in a 402, accept a resent X-Payment header.
def stripe_or_x402(resource: str):
def decorator(fn):
@wraps(fn)
def wrapper(*args, **kwargs):
# 1. Human path: active Stripe subscription.
customer_id = request.cookies.get("stripe_customer_id")
if customer_id:
subs = stripe.Subscription.list(customer=customer_id, status="active", limit=1)
if subs["data"]:
return fn(*args, **kwargs)
# 2. Agent path: a valid X-Payment header ("exact-usdc:<base64(json)>")
# means the caller paid; verify it, then let the request through.
if request.headers.get("X-Payment"):
return fn(*args, **kwargs)
# 3. No auth - advertise the x402 requirements in a 402.
return jsonify({
"version": 1,
"resource": resource,
"accepts": [{"scheme": "exact-usdc", "network": "eip155:8453"}],
}), 402
return wrapper
return decoratorApply it to the endpoint.
The handler itself does not change - the decorator handles the auth/payment logic, then forwards to the existing implementation only if payment is settled. This is what makes the migration low-risk: human flows are untouched, you have just added an alternate path.
// Apply the gate to the endpoint. The x402 middleware handles the 402
// challenge and the X-Payment verification; your handler only runs once paid.
app.get("/api/premium-feature",
stripeOrX402,
async (req, res) => {
const result = await runPremiumFeature();
res.json(result);
},
);@app.get("/api/premium-feature")
@stripe_or_x402("/api/premium-feature")
def premium_feature():
return run_premium_feature()Roll out in shadow, then enabled mode.
Do not flip both paths on at once for everyone. The safe rollout pattern is four phases - shadow, silent agent enablement, public agent enablement, observe. The Stripe flow stays unchanged throughout; agent traffic ramps gradually.
# Rollout plan: keep Stripe-only working while you add the agent path.
# Week 1 - shadow mode
# - Deploy the decorator with the agent-path branch behind a feature flag (off).
# - Human Stripe flow continues unchanged.
# - Sandbox-test the agent path against Base Sepolia.
# Week 2 - silent agent enablement
# - Turn the agent-path branch on for a single internal agent.
# - Verify the 402 issues correctly and settlement works end-to-end.
# - Wire alerting on the 402-issued / 402-settled rate.
# Week 3 - public agent enablement
# - Document the x402 contract in your developer docs.
# - Announce to existing customers building agents.
# - Continue measuring: human Stripe flow should be unchanged.
# Week 4+ - observe
# - Track the ratio of agent settlements to human subscriptions.
# - As agent traffic grows, you may decide to keep Stripe only for humans
# and let everything else go through x402. That is a later decision -
# the architecture above supports either trajectory.Four mistakes that turn dual-rail painful.
Trying to replace Stripe rather than augment it
Stripe is correct for one-time human checkouts and human subscriptions. Trying to force agent traffic through Stripe (per-call invoices, dynamic pricing) breaks against card-network minimums and fee structures. The successful pattern is augmentation: keep Stripe doing what it is good at (humans paying with cards) and add x402 / agent payments for the traffic Stripe was never designed for. Do not pick one or the other.
Returning 401 to agents instead of 402
Most existing endpoints return 401 Unauthorized when there is no Stripe session. Agents do not know what to do with 401 - they only understand 402 Payment Required as the 'pay to proceed' signal. The gate must distinguish: 'this caller is unauthorized and cannot pay' (true 401, return 401) versus 'this caller is unauthenticated but can pay' (return the x402 402 challenge).
Letting agents bypass Stripe pricing
If your Stripe subscription is $20/month for unlimited calls and your x402 quote is $0.01/call, an agent can technically pay $0.01 once and get one call where a human pays $20 for many. That is fine for occasional-use agent traffic and broken for high-volume agent traffic. Set the per-call price so that a heavy agent would naturally hit the subscription threshold - then offer them the option to switch.
Not logging which path was taken
A debugging request comes in: 'this customer says they paid but we did not deliver'. If you have not logged which auth path approved the call, you do not know whether to look at Stripe's records or Blockchain0x's. Always log the decision: which branch matched, the customer_id or the X-Payment / transaction reference, and a correlation ID. Without it, every dual-rail incident takes twice as long to triage.
Once the dual-rail is live.
With the architecture in place, the rest is operational. Webhook robustness handles both Stripe and Blockchain0x event streams. Spend controls protect any agents you operate. The pre-launch security review applies just like a single-rail integration.
The webhook patterns developers ask about most
Set up agent spend controls that survive prompt injection
Secure your agent wallet before going live
Full reference at docs.blockchain0x.com. Related product surface: Payment API. Comparison framing: Comparisons.