X402 QR Code Payment
Introduction
Fiat QR code payment by AI.
Process OverviewThe payment process involves two sequential requests:
- Step 1: Request sent without the
PAYMENT-SIGNATUREheader → The system creates an order, returning an HTTP 402 along with the payment requirements (accepts).- Step 2: The client completes the EIP-712 signature locally and retries the request with the
PAYMENT-SIGNATUREheader → The system completes the on-chain settlement via the facilitator, returning an HTTP 200 along with thePAYMENT-RESPONSEheader.
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
| Parameter | Required | Type | Length | Description |
|---|---|---|---|---|
| appId | Yes | string | 64 | App ID |
| qrCode | Yes | string | 512 | QR code string |
| Yes | string | 128 | User email | |
| network | No | string | 64 | Network: BSC/BASE/X (Defaults to BSC if omitted, case-insensitive) |
| token | No | string | 16 | Payment currency: USDT/USDC/USDG (Defaults to USDT if omitted; X network supports USDC/USDG only; case-insensitive) |
| amount | No | string | 20 | Order amount |
Request Headers (Step 2 Only)
| Header | Required | Description |
|---|---|---|
| PAYMENT-SIGNATURE | Required for Step 2 | Base64-encoded string of the payment payload (paymentPayload) JSON. See below for details on structure. |
Supported Networks and Tokens
| network | token | Token Contract | Decimals | eip3009 | Signature Scheme |
|---|---|---|---|---|---|
| BSC(eip155:56) | USDT | 0x55d398326f99059fF775485246999027B3197955 | 18 | false | approve + facilitator deduction |
| BSC(eip155:56) | USDC | 0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d | 18 | false | approve + facilitator deduction |
| BASE(eip155:8453) | USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 | 6 | true | EIP-3009 direct signature (gasless) |
| BASE(eip155:8453) | USDT | 0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2 | 6 | false | approve + facilitator deduction |
| X(eip155:196) | USDC | 0x74b7f16337b8972027f6196a17a631ac6de26d22 | 6 | true | EIP-3009 direct signature (gasless) |
| X(eip155:196) | USDG | 0x4ae46a509f6b1d9056937ba4500cb143933d2dc8 | 6 | true | EIP-3009 direct signature (gasless) |
Facilitator Notes
- Facilitator Contract Address:
0x555e3311a9893c9B17444C1Ff0d88192a57Ef13e- For combinations where
eip3009=false, the client must issue a sufficientapproveauthorization 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
| Parameter | Type | Description |
|---|---|---|
| scheme | string | Payment scheme, fixed as exact |
| network | string | Blockchain network in CAIP-2 format (eip155:56 / eip155:8453 / eip155:196) |
| amount | string | Payment amount (smallest token unit, scaled by tokenDecimals) |
| asset | string | Token contract address |
| payTo | string | Payee receiving address |
| maxTimeoutSeconds | number | Signature authorization validity period (in seconds) |
| extra.name | string | EIP-712 domain name (used for EIP-3009 direct signing) |
| extra.version | string | EIP-712 domain version |
| extra.orderNo | string | System order number |
| extra.tokenSymbol | string | Token symbol |
| extra.tokenDecimals | number | Token decimals |
| extra.eip3009 | boolean | Indicates 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 onextra.eip3009)
- eip3009=true: EIP-712 domain is the token contract itself (
name/versionsourced fromextra,verifyingContractisasset), type isTransferWithAuthorization, with fieldsfrom/to/value/validAfter/validBefore/nonce.- eip3009=false: Ensure prior
approveauthorization to the facilitator contract; EIP-712 domain is{ name: "Facilitator", version: "1", verifyingContract: facilitator address }, type istokenTransferWithAuthorization, with fieldstoken/from/to/value/validAfter/validBefore/nonce/needApprove(needApproveis fixed totrue, andtoken/from/tomust be lowercase addresses).
Validation Rules
authorization.valuemust equalaccepts.amount, andauthorization.tomust equalaccepts.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"
}| Parameter | Type | Description |
|---|---|---|
| success | boolean | Whether the on-chain settlement was successful |
| transaction | string | On-chain transaction hash |
| network | string | Blockchain network (CAIP-2 format) |
| payer | string | Payer'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
| Parameter | Required | Type | Description |
|---|---|---|---|
| orderNo | Yes | string | System 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
| Parameter | Type | Description |
|---|---|---|
| num | string | System order number |
| merchantOrderNo | string | Merchant order number |
| qrCode | string | QR code string |
| usdAmount | string | Amount in USD |
| orderAmount | string | Amount in fiat currency |
| orderCurrency | string | Order fiat currency code |
| fiatExchangeRate | string | Fiat exchange rate |
| status | string | Order status (INIT / SUCCESS / FAIL) |
| createTime | string | Transaction creation timestamp |
| bankData | object | Bank details object |
bankData Bank Information
| Parameter | Type | Description |
|---|---|---|
| bankAccountName | string | Bank account name |
| bankAccountNumber | string | Bank account number |
| bankCode | string | Bank code |
| bankName | string | Bank name |
Additional Notes
- Key Changes from V1:
- Request header changed from
X-PAYMENTtoPAYMENT-SIGNATURE.- Response header changed from
X-Payment-ResponsetoPAYMENT-RESPONSE.x402Versionis fixed to2.- Request parameter
addresschanged totoken.acceptsschema normalized (maxAmountRequired→amount,networkconverted 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
validAfterandvalidBefore. Thenonceis 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.
Updated 14 days ago
Did this page help you?
