X402 QR Code Payment

Introduction

Fiat QR code payment by AI.

📘

Process Overview

The payment process involves two sequential requests:

  1. Step 1: Request sent without the PAYMENT-SIGNATURE header → The system creates an order, returning an HTTP 402 along with the payment requirements (accepts).
  2. Step 2: The client completes the EIP-712 signature locally and retries the request with the PAYMENT-SIGNATURE header → The system completes the on-chain settlement via the facilitator, returning an HTTP 200 along with the PAYMENT-RESPONSE header.

Client-side reference implementation: AEOX-X402 demo

Flow Chart

QR Code Payment (x402 Protocol V2)

Request Method

  • HTTP Method: GET
  • Path: open/ai/402/payment

Request Parameters

ParameterRequiredTypeLengthDescription
appIdYesstring64App ID
qrCodeYesstring512QR code string
emailYesstring128User email
networkNostring64Network: BSC/BASE/X (Defaults to BSC if omitted, case-insensitive)
tokenNostring16Payment currency: USDT/USDC/USDG (Defaults to USDT if omitted; X network supports USDC/USDG only; case-insensitive)
amountNostring20Order amount

Request Headers (Step 2 Only)

HeaderRequiredDescription
PAYMENT-SIGNATURERequired for Step 2Base64-encoded string of the payment payload (paymentPayload) JSON. See below for details on structure.

Supported Networks and Tokens

networktokenToken ContractDecimalseip3009Signature Scheme
BSC(eip155:56)USDT0x55d398326f99059fF775485246999027B319795518falseapprove + facilitator deduction
BSC(eip155:56)USDC0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d18falseapprove + facilitator deduction
BASE(eip155:8453)USDC0x833589fCD6eDb6E08f4c7C32D4f71b54bdA029136trueEIP-3009 direct signature (gasless)
BASE(eip155:8453)USDT0xfde4C96c8593536E31F229EA8f37b2ADa2699bb26falseapprove + facilitator deduction
X(eip155:196)USDC0x74b7f16337b8972027f6196a17a631ac6de26d226trueEIP-3009 direct signature (gasless)
X(eip155:196)USDG0x4ae46a509f6b1d9056937ba4500cb143933d2dc86trueEIP-3009 direct signature (gasless)
🚧

Facilitator Notes

  • Facilitator Contract Address: 0x555e3311a9893c9B17444C1Ff0d88192a57Ef13e
  • For combinations where eip3009=false, the client must issue a sufficient approve authorization to the facilitator contract prior to signing (one-time setup, requires a small amount of native tokens for gas). The settlement gas is covered by the facilitator.

Step 1: Obtain Payment Requirements

Request Example

curl --location 'https://ai-api-sbx.aeonpay.ai/open/ai/402/payment?appId=TEST000001&qrCode=00020101021138560010A0000007270126000697041501121170028740400208QRIBFTTA53037045802VN63048A1C&email=user%40example.com&network=BSC&token=USDT'

HTTP 402 Response Example

{
  "x402Version": 2,
  "orderNo": "400017865060843652397",
  "resource": {
    "url": "https://ai-api.aeonpay.ai/open/ai/402/x402payment",
    "description": "AEON payment with x402",
    "mimeType": "application/json"
  },
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:56",
      "amount": "550000000000000000",
      "asset": "0x55d398326f99059fF775485246999027B3197955",
      "payTo": "0xc2c010… (Payment receiving address, dynamically generated per order)",
      "maxTimeoutSeconds": 300,
      "extra": {
        "name": "USDT",
        "version": "1",
        "orderNo": "400017865060843652397",
        "tokenSymbol": "USDT",
        "tokenDecimals": 18,
        "eip3009": false
      }
    }
  ],
  "error": "PAYMENT-SIGNATURE header is required",
  "code": "402",
  "msg": "PAYMENT-SIGNATURE header is required",
  "traceId": "6a7beb596cac03e22b6117f7bce440b6"
}

accepts Field Descriptions

ParameterTypeDescription
schemestringPayment scheme, fixed as exact
networkstringBlockchain network in CAIP-2 format (eip155:56 / eip155:8453 / eip155:196)
amountstringPayment amount (smallest token unit, scaled by tokenDecimals)
assetstringToken contract address
payTostringPayee receiving address
maxTimeoutSecondsnumberSignature authorization validity period (in seconds)
extra.namestringEIP-712 domain name (used for EIP-3009 direct signing)
extra.versionstringEIP-712 domain version
extra.orderNostringSystem order number
extra.tokenSymbolstringToken symbol
extra.tokenDecimalsnumberToken decimals
extra.eip3009booleanIndicates whether EIP-3009 is supported; if false, the client must use the approve + facilitator deduction signature workflow

Step 2: Submit PAYMENT-SIGNATURE to Complete Payment

PAYMENT-SIGNATURE Structure

PAYMENT-SIGNATURE is the Base64-encoded string of the following JSON structure:

{
  "x402Version": 2,
  "payload": {
    "authorization": {
      "from": "0xPayerAddress",
      "to": "0xPayeeAddress (Must match accepts.payTo)",
      "value": "550000000000000000",
      "validAfter": "1786506621",
      "validBefore": "1786506981",
      "nonce": "0x…32-byte random hex string…"
    },
    "signature": "0x…EIP-712 Signature…"
  },
  "resource": {
    "url": "…",
    "description": "…",
    "mimeType": "application/json"
  },
  "accepted": {
    "...The exact accepts[] object returned in Step 1 passed back as-is..."
  }
}
📘

Signature Method Differentiation (based on extra.eip3009)

  • eip3009=true: EIP-712 domain is the token contract itself (name/version sourced from extra, verifyingContract is asset), type is TransferWithAuthorization, with fields from/to/value/validAfter/validBefore/nonce.
  • eip3009=false: Ensure prior approve authorization to the facilitator contract; EIP-712 domain is { name: "Facilitator", version: "1", verifyingContract: facilitator address }, type is tokenTransferWithAuthorization, with fields token/from/to/value/validAfter/validBefore/nonce/needApprove (needApprove is fixed to true, and token/from/to must be lowercase addresses).
🚧

Validation Rules

authorization.value must equal accepts.amount, and authorization.to must equal accepts.payTo. Otherwise, a parameter validation error will be returned.

Request Example

curl --location 'https://ai-api-sbx.aeonpay.ai/open/ai/402/payment?appId=TEST000001&qrCode=00020101021138560010A0000007270126000697041501121170028740400208QRIBFTTA53037045802VN63048A1C&email=user%40example.com&network=BSC&token=USDT' \
--header 'PAYMENT-SIGNATURE: eyJ4NDAyVmVyc2lvbiI6MiwicGF5bG9hZCI6… (Base64 encoded paymentPayload JSON)'

HTTP 200 Body Response Example

{
  "code": "0",
  "model": "Payment successful",
  "msg": "success",
  "traceId": "6a7bf6c0d4cff1f0d0f56b1b40d36271"
}

HTTP 200 Header Response Example

Settlement details are returned in the PAYMENT-RESPONSE response header (Base64-encoded):

PAYMENT-RESPONSE: eyJzdWNjZXNzIjp0cnVl… (Base64 encoded settlement result JSON)

Decoded JSON content from Base64:

{
  "success": true,
  "transaction": "0xc72eabfa… (On-chain transaction hash)",
  "network": "eip155:56",
  "payer": "0x34B7FE…F510"
}
ParameterTypeDescription
successbooleanWhether the on-chain settlement was successful
transactionstringOn-chain transaction hash
networkstringBlockchain network (CAIP-2 format)
payerstringPayer's wallet address

Error Responses

  • Signature Verification Failure: HTTP 403, body contains invalidReason.
  • On-chain Settlement Failure (e.g., Insufficient Balance): HTTP 402, body contains errorReason. Example:
{
  "success": false,
  "errorReason": "Failed to settle transaction: Execution reverted with reason: BEP20: transfer amount exceeds balance…",
  "transaction": "",
  "network": "eip155:56",
  "payer": "0x34B7FE…F510",
  "code": 402,
  "msg": "Failed to settle transaction: …",
  "traceId": "…"
}

Order Query

  • HTTP Method: GET
  • Path: open/ai/402/query
ParameterRequiredTypeDescription
orderNoYesstringSystem order number (orderNo from Step 1 response)

Response Example

{
  "code": "0",
  "msg": "success",
  "model": {
    "num": "400017865068610252406",
    "merchantOrderNo": "400417865068607681723",
    "qrCode": "00020101021138560010A000000727…",
    "usdAmount": 0.53849246,
    "orderAmount": 14070,
    "orderCurrency": "VND",
    "fiatExchangeRate": 0.000038272385,
    "status": "SUCCESS",
    "createTime": "2026-08-12 11:54:21",
    "paymentFlag": "AI_PAYMENT_SUCCESS_ORDER_INFO",
    "bankData": {
      "bankAccountName": "ICB",
      "bankCode": "970415",
      "bankAccountNumber": "970415",
      "bankName": "ICB"
    }
  },
  "traceId": "6a7bee87f318d491aff7f710be22101b"
}

model Payment Receipt Description

ParameterTypeDescription
numstringSystem order number
merchantOrderNostringMerchant order number
qrCodestringQR code string
usdAmountstringAmount in USD
orderAmountstringAmount in fiat currency
orderCurrencystringOrder fiat currency code
fiatExchangeRatestringFiat exchange rate
statusstringOrder status (INIT / SUCCESS / FAIL)
createTimestringTransaction creation timestamp
bankDataobjectBank details object

bankData Bank Information

ParameterTypeDescription
bankAccountNamestringBank account name
bankAccountNumberstringBank account number
bankCodestringBank code
bankNamestringBank name

📘

Additional Notes

  • Key Changes from V1:
    • Request header changed from X-PAYMENT to PAYMENT-SIGNATURE.
    • Response header changed from X-Payment-Response to PAYMENT-RESPONSE.
    • x402Version is fixed to 2.
    • Request parameter address changed to email, and added token.
    • accepts schema normalized (maxAmountRequiredamount, network converted to CAIP-2 format).
    • BSC network no longer accepts transaction hashes directly; all transactions follow the signature + facilitator settlement model.
  • Signature Validity: Signature authorization is only valid within the time window between validAfter and validBefore. The nonce is single-use to prevent replay attacks.
  • Cache Expiration: Order cache expires in 59 minutes; please re-execute Step 1 upon expiration.
  • Error Handling: Refer to the Error Code documentation on the homepage for a comprehensive list of error codes.

Did this page help you?