HTTP-native payment

x402 payment guide

PhotoMatchFun uses x402 v2 for exact USDC payments on Base. Your wallet signs the server-issued requirement; PhotoMatchFun verifies and settles it before sending the physical order to fulfillment.

Protocol

x402 v2

Scheme

exact

Network

Base Mainnet

Asset

USDC

Treat the live PAYMENT-REQUIRED response and order-intent receipt as authoritative. Do not hardcode an amount or copy a recipient from documentation.

Payment handshake

Review first, authorize second

  1. 01

    Request

    POST the payment URL returned by create_order_intent. For a positive total, the server answers 402 with a PAYMENT-REQUIRED header for the discounted amount. A zero-total coupon order is confirmed by the same POST without a wallet signature.

  2. 02

    Validate and sign

    Your wallet verifies the amount, network, asset, recipient, order digest, and expiry against the intent the customer approved, then creates PAYMENT-SIGNATURE.

  3. 03

    Settle and fulfill

    Repeat the same POST with the signed header. A successful settlement returns PAYMENT-RESPONSE and queues the physical order.

Buyer-side example

Use an x402-capable client inside a wallet environment with an explicit spending policy. The wrapper handles the 402 challenge and signed retry.

Important: walletAccount is a signer object held by your wallet runtime. It is not a private key string and must never be sent to PhotoMatchFun.
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";

// walletAccount stays inside your trusted wallet runtime.
const paidFetch = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [{
    network: "eip155:*",
    client: new ExactEvmScheme(walletAccount),
  }],
});

const response = await paidFetch(paymentUrl, { method: "POST" });
if (!response.ok && response.status !== 202) {
  throw new Error(await response.text());
}

const receipt = await response.json();
// Payment accepted: now poll get_order_status with intentId and statusToken.
// Do not report fulfillment until status is fulfilled and providerOrderId is set.

Before signing

  • Before create_order_intent or payment, collect the actual recipient's first and last name in customer.name, separated by a space. Do not use a single first name, wallet label, or agent nickname. Ask the customer for missing name details; never invent a surname. If the recipient has only one name, stop and contact support before ordering.
  • The amount and currency match the order-intent receipt.
  • The order digest and intent ID match the order under review.
  • The requirement uses x402 v2, the exact scheme, USDC, and the expected Base network.
  • The intent has not expired and remains within the customer's approved spending limit.

Retry safely

  • For a busy response, respect Retry-After and retry the same payment URL.
  • If settlement is reported as unknown, retry the same signed payment exactly as instructed.
  • Never create a second authorization just because fulfillment is still processing.
  • Payment success is not fulfillment success. After HTTP 200/202, poll get_order_status with the original intentId and statusToken. Report provider acceptance only when status is fulfilled and fulfillment.providerOrderId is present; this does not mean delivery. If status is fulfillment_failed or fulfillment.needsOperatorReview is true, report that payment succeeded but fulfillment failed, stop automatic ordering, and do not create another order or payment.
  • If status requests operator review, retain the intent and status capability for support.

Start from the MCP order contract

The MCP guide covers product discovery, artwork uploads, consent, and order intents.

Read the MCP guide