Accept Payments

Accept both protocols

Accept MPP and x402 so buyers can use either protocol to purchase the same result. Share your product pricing and fulfillment code while keeping each request on one payment path.

Start with Accept MPP and Accept x402. This page brings those integrations into one application; it does not replace their account setup or payment checks.

Offer both protocols on one endpoint

One endpoint can accept either MPP or x402. For example, GET /api/report sells a report for 0.01 USDC. An unpaid request receives both payment choices. The buyer chooses one, submits its payment credential to the same URL, and receives the report after that payment succeeds.

RequestWhat the endpoint does
No payment credentialReturn one 402 response with MPP choices in WWW-Authenticate and x402 choices in PAYMENT-REQUIRED. Do not return the report.
MPP credentialValidate and accept the MPP payment, then return the report with its MPP receipt.
x402 credentialUse the x402 verification and settlement flow, then return the report with its x402 payment response.
Both payment credentialsReject the ambiguous request before attempting payment. The buyer must submit one choice.

The Node.js example below composes the existing Express adapters for a fixed-price GET request. Keep the Seller client configuration from Accept MPP and Accept x402: options contains your Sandbox environment and Seller API key; secretKey is the MPP challenge-signing secret. The x402 route advertises 0.01 USDC using the balance scheme, with its matching facilitator and registrations. Use one Express app.

Set resourceUrl to the full public URL of this report endpoint. Keep the same MPP method, realm, and secret for challenge generation and credential verification. Run application authentication and input checks before this route. This example is application wiring, not a combined-protocol SDK helper.

import { Mppx } from 'mppx/express';
import { Mppx as MppxServer } from 'mppx/server';
import { inflow, paymentHttpTransport } from '@inflowpayai/mpp-seller';
import { paymentMiddlewareFromConfig } from '@x402/express';
import type { RequestHandler } from 'express';

const method = inflow(options);
const realm = 'report-service';
const payments = Mppx.create({
  methods: [method], realm, secretKey, transport: paymentHttpTransport(),
});
const challenges = MppxServer.create({
  methods: [method], realm, secretKey, transport: paymentHttpTransport(),
});
const terms = {amount: '0.01', currency: 'USDC'};
const mppPayment = payments.charge(terms);
const mppChallenge = challenges.charge(terms);
const x402Payment = paymentMiddlewareFromConfig(
  {'GET /api/report': route}, [facilitator], registrations,
);
const report: RequestHandler = (_request, response) => {
  response.set('Cache-Control', 'no-store');
  response.json({report: 'Your sample report is ready.'});
};

app.get('/api/report', async (request, response, next) => {
  response.set('Cache-Control', 'no-store');
  const authorization = request.get('Authorization') ?? '';
  const hasMpp = /^Payment(?:\s|$)/i.test(authorization);
  const hasX402 = request.get('Payment-Signature') !== undefined
    || request.get('X-Payment') !== undefined;
  const duplicate = ['authorization', 'payment-signature', 'x-payment']
    .some(name => (request.headersDistinct[name]?.length ?? 0) > 1);
  if (duplicate || (hasMpp && hasX402)
    || (request.get('Payment-Signature') !== undefined
      && request.get('X-Payment') !== undefined)) {
    response.status(400).json({error: 'Send one payment credential.'});
    return;
  }
  if (hasMpp) return mppPayment(request, response, next);
  if (hasX402) return x402Payment(request, response, next);

  const unpaid = await mppChallenge(new Request(resourceUrl));
  if (unpaid.status !== 402) throw new Error('Expected an MPP challenge.');
  const challenge = unpaid.challenge;
  if (challenge.status !== 402) {
    challenge.headers.forEach((value, name) => response.set(name, value));
    response.status(challenge.status).send(await challenge.text());
    return;
  }
  const header = challenge.headers.get('WWW-Authenticate');
  if (header === null) throw new Error('Missing MPP challenge header.');
  response.append('WWW-Authenticate', header);
  return x402Payment(request, response, next);
}, report);

Without a payment credential, the MPP handler builds a challenge and the x402 adapter produces the 402 response, retaining the MPP header. With a credential, only the selected adapter runs. Keep response.json so the MPP Express adapter can attach its receipt. Do not also register the x402 middleware globally for this route.

Keep the setup guides’ startup checks and Express error handling. Other languages need the same request selection and combined unpaid response; the four-language examples below show the alternative with separate endpoints, not a shared combined-adapter API.

Check response-header size

Advertising both protocols puts both sets of payment terms in one HTTP response. More methods, currencies, networks, and longer descriptions can increase the headers enough to exceed a web server, reverse proxy, or client limit. The application may generate a valid 402 response while the buyer receives a proxy error instead.

Check the unpaid response through the same public hostname and proxy path that buyers use. If it fails there but succeeds directly against the application, inspect the proxy logs for an oversized upstream response header. For example, NGINX can report upstream sent too big header and return 502. Its proxy_buffer_size setting controls the buffer that normally holds the upstream response headers.

Response-header limits are separate from limits on incoming request headers. Raising a request-header limit does not fix an oversized 402 response. Check the components you actually deploy; there is no single header-size budget that every deployment shares.

You can keep one endpoint by advertising only the payment options your product needs and configuring adequate response-header capacity. If that is impractical, expose separate MPP and x402 endpoints for the same product. Each then advertises only its own protocol’s choices. Test those responses too: a long list of options can exceed a limit even with one protocol.

Use separate endpoints when needed

Separate endpoints are an alternative when combined response headers are too large for your deployment. Both routes below sell the same report for 0.01 USDC and call shared report-generation code. The buyer chooses one URL and makes one purchase.

RoutePayment handlingResult
GET /api/mpp/reportMPP onlyThe purchased report
GET /api/x402/reportx402 onlyThe purchased report

These routing fragments replace the report route registration in the two setup guides. Keep their client initialization, configuration checks, and server lifetime management. The shared report code only generates a response; it does not create an order or perform another irreversible operation.

Name the initialized MPP client mppClient and import its seller package as mppseller. Keep the x402 route, facilitator, and registrations from Accept x402. This fragment also uses encoding/base64 for the MPP route label.

report := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Cache-Control", "no-store")
    w.Header().Set("Content-Type", "application/json")
    fmt.Fprintln(w, `{"report":"Your sample report is ready."}`)
})
opaque := base64.RawURLEncoding.EncodeToString([]byte("GET /api/mpp/report"))
mppPaid, err := mppClient.Protect(mppseller.Route{
    Realm: "report-service", SecretKey: secret,
    Offers: []mppseller.Offer{{Charge: &mpp.ChargeRequest{
        Amount: "0.01", Currency: "USDC",
    }}},
    Opaque: &opaque,
}, report)
if err != nil {
    return nil, err
}
resource := xhttp.Newx402HTTPResourceServer(
    xhttp.RoutesConfig{"GET /api/x402/report": route},
    foundation.WithFacilitatorClient(facilitator),
)
for _, registration := range registrations {
    resource.Register(registration.Network, registration.Server)
}
if err := resource.Initialize(ctx); err != nil {
    return nil, err
}
mux := http.NewServeMux()
mux.Handle("GET /api/mpp/report", mppPaid)
mux.Handle("GET /api/x402/report",
    nethttp.PaymentMiddlewareFromHTTPServer(resource,
        nethttp.WithSyncFacilitatorOnStart(false))(report))

Serve this mux. The x402 middleware wraps only the x402 handler, and its configured route matches that handler’s URL. Keep the existing MPP offer preparation check during startup.

Keep the configured MPP payments instance and the x402 route, facilitator, and registrations. Use one Express app and import RequestHandler as a type from express.

const report: RequestHandler = (_request, response) => {
  response.set('Cache-Control', 'no-store');
  response.json({ report: 'Your sample report is ready.' });
};

app.use(paymentMiddlewareFromConfig(
  { 'GET /api/x402/report': route },
  [facilitator],
  registrations,
));
app.get('/api/mpp/report',
  payments.charge({ amount: '0.01', currency: 'USDC' }),
  report,
);
app.get('/api/x402/report', report);

The x402 route map contains only the x402 URL. Unmatched requests pass through that middleware, so the MPP route still uses only its own payment check. Keep response.json: the MPP Express adapter uses that response method to attach the receipt.

Name the MPP Seller client mpp_seller. Keep the x402 route and registered resource from Accept x402. Install both routes on one FastAPI app, inside the clients’ lifespan. Retain the imports for pay, Request, Credential, Receipt, and JSONResponse from the MPP example.

def report_data() -> dict[str, str]:
    return {"report": "Your sample report is ready."}


app.middleware("http")(
    payment_middleware({"GET /api/x402/report": route}, resource)
)
terms = mpp_seller.charge_request({"amount": "0.01", "currency": "USDC"})


@app.get("/api/mpp/report")
@pay(
    intent=mpp_seller, method=mpp_seller.method, request=terms,
    realm="report-service", secret_key=secret,
)
async def mpp_report(
    request: Request, credential: Credential, receipt: Receipt,
) -> JSONResponse:
    return JSONResponse(report_data(), headers={
        "Payment-Receipt": receipt.to_payment_receipt(),
        "Cache-Control": "no-store",
    })


@app.get("/api/x402/report")
async def x402_report() -> JSONResponse:
    return JSONResponse(report_data(), headers={"Cache-Control": "no-store"})

The handlers share report data, but construct their own responses. The MPP handler includes its receipt; the x402 middleware attaches its payment response after settlement. Register middleware before the application starts serving requests.

Keep the MPP offer and its paid handler from the linked Rust MPP example. Change the offer description to Report and rename that example’s widgets handler to mpp_report. In its payment helper, replace the successful JSON body with Json(report_data()). Retain its credential validation, receipt headers, and error handling.

Build the x402 layer as shown in Accept x402. Set its resource URL to the full public URL ending in /api/x402/report. Put these functions alongside the MPP handler, then call report_router(offer, layer) during startup.

use axum::{Json, Router, routing::get};
use inflow_mpp_seller::Offer;
use inflow_x402_axum::PaymentLayer;
use serde_json::json;

fn report_data() -> serde_json::Value {
    json!({"report": "Your sample report is ready."})
}

pub fn report_router(offer: Offer, layer: PaymentLayer) -> Router {
    let mpp_routes = Router::new()
        .route("/api/mpp/report", get(mpp_report))
        .with_state(offer);
    let x402_routes = Router::new().route(
        "/api/x402/report",
        get(|| async {
            (
                [("cache-control", "no-store")],
                Json(report_data()),
            )
        }).route_layer(layer),
    );
    mpp_routes.merge(x402_routes)
}

Serve the returned router. The MPP offer is state for the MPP handler only; the x402 layer applies only to the x402 route.

Keep each request on one payment path

On a combined endpoint, select the payment integration from the submitted credential. With separate endpoints, select it from the route. Keep free endpoints outside both payment integrations.

Compose the adapters as alternatives, as in the combined example above. Simply running the two standalone middleware functions in sequence does not advertise both choices: the first can finish an unpaid request before the second runs. A paid request must not then face a second payment requirement.

Do not automatically retry a purchase through the other protocol after a payment failure. A timeout can leave the first payment’s outcome unknown. Resolve that outcome before inviting another payment, even if the other route is available.

Reuse pricing and result generation

Keep the product, quantity rules, and price in your application. Both protocol integrations should use those same decisions. Convert the price separately through each protocol’s helper, as described in Prices and offers.

  • Configure both integrations for the intended seller and environment. Two integrations in one application do not automatically share payment configuration.
  • Run your application’s authentication, authorization, and input checks before accepting payment. A valid payment is not permission to access another customer’s report.
  • Share the code that produces the purchased result. Keep payment credentials and receipt handling in the selected protocol’s integration.
  • Preserve the payment-result headers and prevent shared caches from serving paid responses to other buyers. The examples use Cache-Control: no-store for report responses.

For an order that can be paid through either protocol, use the same application purchase reference. Check whether that purchase is already paid or being processed. Independent payment identifiers do not prevent two separate requests from purchasing or fulfilling the same order twice.

Understand payment timing

The integrations on these setup pages do not run payment and application code in the same order.

Integration shownSuccessful paid requestFailure to plan for
MPP payment middlewareAccept payment, then run the result handler.Payment can succeed before result generation fails.
x402 payment middlewareVerify payment, run the result handler, then settle payment before returning success.The handler can run before settlement fails.

A read-only report function can serve both paths. Creating an order, shipping goods, or granting a lasting entitlement needs a separate delivery decision. Choose when that work becomes final and how a buyer recovers a paid result after a failed response. See Deliver paid results.

Test both purchase paths

Test both payment choices, then check that the integrations do not interfere with each other. Retain your project’s tests for permissions, pricing, delivery, and retries.

  • Request the combined endpoint without payment. Confirm one 402 response with both protocols’ payment terms and no paid report. If using separate endpoints, confirm that each advertises only its own terms. Repeat through the public proxy path to catch header-size limits.
  • Complete one controlled Sandbox purchase through each protocol. Check the amount, seller, result, and protocol-specific payment receipt or response. Confirm that the other payment integration did not execute.
  • Submit an invalid credential for each protocol. On the combined endpoint, also send both credentials or duplicate payment headers. Confirm rejection without returning the report or trying another payment path.
  • Test failed settlement, a lost response, and a repeated purchase request. Confirm that an uncertain outcome does not create a second payment through the other route.
  • Request a free endpoint and an unknown path. Confirm that the free endpoint remains free and the unknown path does not become a paid route.
  • If both protocols can pay for one order, test simultaneous requests against that order. Confirm that your application prevents duplicate fulfillment across protocols.

Use the payment commands from Accept MPP and Accept x402, with the same URL for a combined endpoint, or the corresponding URL for separate endpoints. Execute payment tests only with approval for the test account, amount, and environment.

Continue your integration

Deliver paid results

Decide when to fulfill a purchase and how buyers recover results after payment.

Test your integration

Check the complete purchase flow, including failures and repeat requests.