Order tickets passing through an orange rail switch to a new track.

Nov 3, 2026

Share

Category /

other

13 min read

GOAT Network

Migrating from x402 Middleware Without Losing In-Flight Orders

Plan an x402 middleware migration that preserves client contracts, payment terms, in-flight orders, reconciliation, and rollback responsibilities.

scroll

Table of contents

Migrating from x402 Middleware Without Losing In-Flight Orders

Imagine switching a paid API from basic x402 middleware to a merchant commerce platform while two purchases remain unfinished. One buyer has paid but has not received the report. Another has broadcast a transfer that is still awaiting confirmation. Sending the next request to a new backend resolves neither purchase.

The migration must move responsibility for new purchases without abandoning earlier obligations. That requires more than replacing a facilitator URL: the old and new paths need compatible client contracts, durable identity mappings, explicit payment-state ownership, and a shared account of what was delivered.

The checkpoints below describe a recommended merchant-owned migration architecture. GOAT Flow's specific interfaces provide concrete constraints where relevant; the routing, cross-system mapping, archive, and rollback design are application responsibilities, not a bundled migration feature. Middleware can remain the request edge while a platform takes on order and merchant operations.

Record What the Existing Endpoint Promises

Start with a versioned record of one real paid route. Capture its method and path, application authentication, request parameters, status codes, headers, response body, payment requirements, and retry behavior. Include the SDK and wire versions, configured network and asset, recipient, verification authority, and condition that currently releases the purchased work.

The x402 seller quickstart illustrates how middleware binds protected routes to payment configuration and a resource server/facilitator. Those bindings belong in the inventory. A route that keeps the same URL but changes the expected payment artifact has changed its client contract.

Then assign responsibility by purchase, not just by service. Name the authority for the quoted price, payable-object creation, payment verification, fulfillment, and exception resolution. Different components may perform these jobs, but two components must not independently decide to create another charge or release the same delivery. Record who resolves an ambiguous outcome when neither side can immediately answer.

Keep application access control independent of payment. A valid payment artifact cannot establish that the caller owns the customer account, may retrieve another customer's report, or paid for different request parameters. Preserve the authenticated customer and requested operation before attaching payment evidence.

Destination readiness is a separate check. For the selected GOAT Flow Crypto DIRECT path, use an approved merchant account configured for DIRECT receiving, a supported receiving chain and token, and a payer able to meet that token's payment and gas requirements. Merchant service-fee credits are required only where the applicable deployment and order-fee configuration requires them. Card readiness is different: Card must be enabled for the merchant, the connected Stripe account must be authorized and ready to accept charges, and the deployment must have its Stripe integration configured. A Card-only merchant does not need a Crypto wallet or Crypto receiving tokens, and its buyers do not need Crypto tokens or gas. These prerequisites do not establish that a particular merchant, asset, or environment is ready for live traffic.

Decide which responsibilities actually need to move. If the existing middleware already handles the client-facing challenge correctly, retaining that interface may reduce client changes, provided its connection to the destination is validated. The platform does not need to replace every working component. The migration record should make clear which service remains authoritative and which merely forwards or observes information.

Link Purchases, Payment Attempts and Delivery Records

A useful mapping begins with a merchant-owned business purchase ID. Keep its namespace, merchant, environment, authenticated customer, operation, and input fingerprint together. Preserve the legacy request and correlation IDs as references; do not promote whichever identifier happens to be available into the universal order key.

For an illustrative report purchase, the mapping could look like this:

Record

Illustrative value or relationship

Purpose

Business purchase

merchant-A/prod/report-42

One commercial obligation

Legacy requests

request-17, then retry request-18

Two requests, not automatically two sales

Issued terms

Saved price version, asset, units, recipient and expiry

What this purchase was offered

Payment attempts

Attempt A; any later authorized replacement retained separately

Preserve attempt history

Destination association

Surface name plus returned order or challenge ID

Resolve the correct platform object

Transfer evidence

Network, transaction hash and relevant transfer/log reference

Identify the actual movement

Delivery operation

deliver:report-42, with saved result

Recover the same purchased output

This is an application data model, not a GOAT import format. A purchase can have multiple HTTP requests and payment attempts; a payment attempt can remain unresolved after its initiating request ends. Preserve those relationships rather than flattening everything into a transaction table.

GOAT Flow's custom API example links the application request and saved terms to a stable dappOrderId, the returned payment.order_id, and a persisted delivery result. Preserve the distinction between application identifiers, SDK field names, and wire fields when building your mapping.

Recovery semantics matter as much as names. Ordinary duplicate dapp_order_id creation is rejected rather than replaying the original response. Separately, Checkout session references can conflict without returning the original session. Neither behavior is a substitute for saving the original returned identifiers.

Store raw source states, observation times and provenance alongside your normalized view. Protect customer data and session handles with the same ownership checks as the purchase itself. Otherwise, a convenient migration lookup can become a cross-customer data-access path.

Preserve Client Contracts and Already-Issued Payment Terms

Treat wire-version migration, product-surface selection, and merchant-platform migration as distinct changes. Combining them into one release makes a failed request harder to diagnose: the cause could be payment parsing, signing, pricing, routing, or order association.

The x402 v1-to-v2 migration guide identifies changes including header names and network identifiers. The v2 HTTP transport specification uses PAYMENT-REQUIRED, PAYMENT-SIGNATURE, and PAYMENT-RESPONSE, with direction-appropriate CORS handling for browser clients. Pin the contract actually used by each client cohort rather than treating current documentation as proof of its installed version.

Build fixtures for the initial unpaid request, the challenge, payment submission, pending response, successful retrieval, and retry after a lost response. Include method/path matching, body and query parameters, application authentication, wallet signing, supported schemes, and discovery metadata. Test rejection as well as success: another customer's request or a changed operation must not inherit the original purchase's authorization.

GOAT Flow's MPP profile uses its own challenge/verification interface and signed Payment-Receipt. That concrete contract does not establish drop-in compatibility with an existing x402 client. Keeping the old interface requires a separately validated adapter; changing interfaces requires an explicit client rollout. Neither follows merely from both paths supporting HTTP payments.

Preserve the terms actually issued to each purchase: amount in integer base units, asset contract and decimals, network, recipient, payment window, price version, and fee or gas responsibility. GOAT Flow's chain/token availability depends on runtime configuration, so a protocol-supported asset is not automatically an enabled merchant route.

For example, if an illustrative legacy quote charges 1 unit of a configured asset and the new route charges 2, the routing switch must not reinterpret the earlier payment as underpayment at the new price. Keep the original quote and verification path available, or pause that cohort and resolve its obligations before offering replacements. This is a recommended preservation policy, not a promise of an automatic platform grace period.

GOAT Flow's MPP route configuration includes pricing versions; the receipt itself does not carry route_pricing_version. Save the applicable terms association server-side rather than expecting a later receipt to reconstruct every historical pricing decision.

Run a Shadow Path Before Admitting a Live Cohort

Use shadow operation to compare decisions without creating a second live payment path. Replay sanitized request records and observed outcomes into the candidate logic, but prevent it from creating payable orders, initiating transfers, consuming receipts, or fulfilling purchases. A shadow system that can mutate commercial state is already participating in the sale.

A live canary needs a different rule. Before any payable object is created, persist the purchase's selected path, routing epoch, merchant, environment, and credential reference. A routing epoch is simply your own identifier for a routing decision version. Subsequent retries read that assignment; they do not rerun today's percentage split and potentially move the same purchase to the other system.

Enforce the assignment atomically at the merchant backend. GOAT Flow's custom API example calls for a durable creation claim and saved delivery result. Apply that boundary across your two paths: concurrent workers must not each conclude that they are the first creator. If a worker stops after submitting creation but before saving the response, retain the claim for reconciliation.

Tenant selection must also precede mutation. Resolve credentials from the authenticated merchant context, not an arbitrary merchant ID supplied by a buyer. Reject attempts to look up an order or use a receipt under a different tenant or environment. A successful single-merchant test does not exercise this boundary.

Credential rollout needs its own schedule. In the GOAT Flow Merchant Portal, API-key rotation invalidates the previous key/secret immediately, without an overlap interval. Do not design a rolling deployment around both pairs remaining valid. Drain or pause affected work, distribute the replacement securely, and verify the required workers before resuming admissions. Keep webhook endpoint secrets distinct from merchant API credentials.

Choose the canary cohort and observation period from the merchant's traffic and failure exposure. There is no universal percentage that proves correctness. The useful evidence is whether assigned purchases stay with one owner and whether failures can be recovered without extra charges or deliveries.

Give Every In-Flight Order a Disposition

At cutover, enumerate unresolved purchases rather than copying a status string into the new system. For each one, retain the assigned resolver, original terms, payment attempts, available evidence, and next allowed action. The following categories are proposed migration dispositions, not additional GOAT status values.

Observed situation

Retained owner and evidence

Allowed action; blocked shortcut

Create outcome unknown

Original path's resolver; creation claim, merchant reference and attempt details

Locate/reconcile the original outcome; no blind replacement order

Transfer broadcast, unconfirmed

Original payment resolver; saved transaction and issued terms

Continue observing the original attempt; no second transfer because polling timed out

Payment confirmed, restrictions remain

Assigned platform-state resolver; current authenticated state and business checks

Hold delivery until the applicable gate clears; do not bypass the restriction

Completed purchase, delivery absent

Assigned fulfillment worker; eligible order and saved purchase

Invoke the durable delivery operation; do not charge again

Delivery already completed

Existing delivery ledger; persisted result and operation key

Return/reuse the saved result; do not regenerate paid work

Expired/cancelled record with payment evidence

Original exception owner; terms, transaction and state history

Reconcile the payment and remedy; do not classify it as simply unpaid

GOAT Flow distinguishes completed orders (INVOICED) from completed Checkout sessions (COMPLETED). PAYMENT_CONFIRMED is not universal fulfillment permission. Apply the relevant surface's completion conditions and your purchase checks; a poller need not observe every intermediate state. These distinctions are defined in the API status reference.

Handle an unknown creation outcome as a recovery problem, not a retryable absence. If the order ID is known, query that original order. If it was never saved, retain the creation reference, payer, terms and attempt time for reconciliation; do not invent a lookup method or use repeated creation as discovery. Availability may be delayed while the ambiguity is resolved, but creating another obligation does not remove it.

For a pending GOAT MPP verification, continue with the original challenge and transaction and honor its retry instructions. Expiry is not evidence that funds never moved. Preserve late-payment observation after the front door switches, and keep the opening's paid-but-undelivered purchase assigned to delivery recovery rather than sending it through checkout again.

Make Old and New Notifications Converge on One Delivery Decision

Webhook cutover should change how evidence arrives, not create another independent delivery authority. Keep the old receiver available for outstanding work while the new path begins receiving its own events. Persist which endpoint and secret apply to each source; do not try unrelated secrets until a signature happens to validate.

GOAT Flow authenticates webhook bodies with the endpoint secret over the exact raw bytes, separately from merchant API authentication. Its created_at can change on retry and is not a stable event ID. Authenticate before parsing; use the verified event and applicable schema, resolving missing authorization facts from authoritative state. The webhook contract also requires durable acceptance before a successful acknowledgement.

For the merchant processing path, separate acceptance from execution. Persist an authenticated notification before acknowledging it, then let a worker resolve the purchase, evaluate eligibility, and claim the delivery operation. If persistence fails, do not acknowledge work as safely recorded. If later execution fails, retry the saved work rather than requiring the sender to reproduce the original request.

Several different uniqueness boundaries are involved:

  • An API authentication nonce protects a signed request against replay; it does not identify a commercial purchase.

  • A purchase/create claim coordinates attempts to create its payable object.

  • An event record preserves the notification's source and meaning; one processed flag for an entire order would suppress later legitimate events.

  • A receipt-consumption record, where applicable, controls use of that payment artifact.

  • A delivery-operation key coordinates the actual purchased output across workers and retries.

Design durable uniqueness around the operation that must not repeat, including any external side effect. A local database flag alone cannot coordinate an external delivery that succeeds just before the worker loses its response. Use a downstream idempotent operation or a recoverable execution/result record; if the outcome remains ambiguous, hold further execution for investigation.

Polling and callbacks should enter this same path. A late payment event must not regress a completed purchase, and a valid signature must not override a current hold or mismatched terms. This convergence is the migration design to implement and test, not an end-to-end exactly-once guarantee supplied by a payment API.

Reconcile Obligations Before Retiring Their History

Reconcile at the purchase level across four bodies of evidence: legacy records, destination records, relevant chain evidence, and fulfillment results. Some historical purchases will have no destination order; that is a valid documented relationship, not necessarily a missing migration. Every live obligation still needs an identified resolver.

Use matching merchant, environment, asset, units and time windows. Retain source timestamps separately from ingestion times. Where chain evidence is required by your trust model, check the actual transfer's network, asset, recipient and amount; a stored transaction hash alone cannot explain which obligation it satisfied. Keep unresolved relationships visible rather than forcing a match to make a dashboard balance.

Totals can conceal errors. In an illustrative pair of equal-value purchases, suppose purchase A is omitted from the destination report while purchase B appears twice. The reported total can match the expected total even though one obligation is missing and another duplicated. Joining by commercial identity exposes both exceptions; matching aggregate revenue does not.

Assign an owner and next action for unknown creates, unmatched transfers, late payments, duplicate payments, confirmed-but-undelivered purchases, and delivery without sufficient payment evidence. Distinguish a known pending item from an unexplained discrepancy. Cutover criteria should specify which owned exceptions may remain open, and which block new admissions.

GOAT Flow's portal provides matched, unpaid and late-payment reconciliation views with filtered CSV export. Its Fee Balance is a separate service-fee credit ledger, not buyer funds. Use these views as evidence inputs; the cross-system join and disposition record remain merchant-owned. Do not combine service credits with sales or add different tokens together as one currency.

For history, retain a protected read-only legacy archive linked by the mapping established earlier. Include raw states, terms versions, payment evidence, delivery results and correction history. Test that operations can retrieve a specific purchase after the old application stops accepting traffic. Keep access limited and define retention according to the merchant's actual requirements.

Do not create new payable orders merely to populate a historical screen. Use a documented import contract if one is established for your deployment; otherwise, external archive links preserve provenance without pretending the destination executed those past transactions. Exported reports are not evidence of a native historical importer.

Cut Over New Admissions and Keep Rollback Bounded

Authorize cutover from recorded acceptance evidence. For each retained client version, exercise the unpaid-to-paid flow, pending recovery, and retrieval after a lost response. Include changed-request and cross-tenant rejection, unknown-create recovery, duplicate/out-of-order events, restart during delivery, and paid-but-undelivered recovery. These are tests to perform, not outcomes implied by a configuration screen.

The decision also needs an operations handoff: who owns every outstanding purchase, which credentials and receivers remain necessary, where exceptions are tracked, and what triggers a pause. Record the effective routing epoch and time. Reconcile the last legacy admissions against the first destination admissions so that work around the boundary is not silently omitted from either review window.

Set thresholds with the merchant. Any unexplained competing ownership, altered issued terms, or duplicate delivery should stop the affected admissions until resolved. Known pending confirmations need not automatically block every unrelated route, but they must remain observable with a named resolver. A quiet error dashboard does not establish that the exception workers are alive; verify their ability to look up and advance saved work.

Rollback should reverse new admissions, not rewrite purchase history. Restore future routing to a previously validated path while keeping existing destination orders, transaction watchers, webhook processing and delivery records available. A retry of an existing purchase follows its saved assignment rather than the new default. If the old credential has been invalidated, changing a route does not restore it.

Money movement requires separate remedies. GOAT Flow's eligible-order cancellation is not a refund and does not reverse a submitted on-chain transfer. Do not model routing rollback as a way to recover funds or erase a platform obligation. Preserve exceptions for the appropriate payment and customer-resolution process.

Return to the two opening purchases. The paid-but-undelivered report stays with its existing delivery operation and saved result. The unconfirmed transfer stays with its original payment observer and terms until its disposition is established. New purchases can use the destination without either obligation disappearing.

Retire the old path only when its remaining obligations and late-arrival policy are resolved, historical evidence is retrievable, and any continuing exception work has an explicit supported home. That is the completion test: not whether traffic moved, but whether every purchase can still be explained and completed correctly.

[01]

AI Knowledge base

More Articles

More Articles

More Articles