fix order
Make the challenge readable before you promote the endpoint.
1. Answer OPTIONS on every paid route
Return 204 or 200 with the intended origin, the route method, OPTIONS, and the payment headers the client will send. For x402, that usually means x-payment plus content-type. For MPP or mixed surfaces, include the documented payment-signature and receipt headers too.
2. Add CORS to the actual 402 response
The most common miss is fixing preflight while the payment-required response still has no Access-Control-Allow-Origin. That leaves browser clients unable to read the challenge payload. Add the same origin policy to the 402 path that short-circuits before application handlers.
3. Expose payment requirement headers
If your challenge is carried in a header, expose it. Common names are payment-required, x-payment-required, payment-response, and x-payment-response. Without Access-Control-Expose-Headers, browser code may receive the 402 but still be unable to inspect the fields it needs.
4. Put CORS outside payment middleware
In middleware stacks where the x402 layer returns early, CORS has to wrap that layer. If the payment middleware is outermost, it can emit a 402 before the response passes through CORS. The fast test is simple: hit a paid route without payment and inspect the actual 402 headers, not just OPTIONS.
5. Echo the resource on each accept leg
Keep the top-level resource.url, then repeat the charged URL in accepts[N].resource or accepts[N].extra.resource. That makes spend maps, wallet prompts, receipts, and rail selection easier to reconcile.
6. Keep paid responses out of shared caches
Protected responses and payment challenges should not be publicly cacheable. Use Cache-Control: no-store, private, vary on payment headers, and bypass shared proxy caches for paid content.