Payment Flow
The x402 flow has three phases: Initiate → Sign → Submit. The difference between the two integration flows is only in the Sign phase — how you obtain and compute the typed data hash that the wallet signs.
Flow A — Client-Side EIP-712
Use this flow when you want full control over the signing process and prefer not to make the extra /build request. You construct the EIP-712 domain and message yourself using ethers.js.
What you need before starting
npm install ethersimport { ethers } from 'ethers';
import fetch from 'node-fetch'; // or use the built-in fetch in Node 18+
const BASE_URL = 'https://facilitator.ivorypay.io';
const API_KEY = process.env.IVORYPAY_API_KEY; // your merchant API key
const PRIVATE_KEY = process.env.EVM_PRIVATE_KEY; // payer wallet private keyStep 1 — Initiate the payment
Tell the facilitator what resource you are charging for. The response gives you the payment terms the wallet must agree to.
const initiateRes = await fetch(`${BASE_URL}/api/x402/initiate`, {
method: 'POST',
headers: {
'Authorization': API_KEY,
'Content-Type': 'application/json',
},
body: JSON.stringify({
amount: 0.01, // decimal USDC — 0.01 = 1 cent
token: 'USDC',
network: 'BASE_MAINNET',
resourceUrl: 'https://yourapp.com/premium-content', // the resource being paid for
resourcesDescription: 'Access to premium content',
}),
});
const initiateBody = await initiateRes.json();The response body looks like this:
Step 2 — Decode the payment requirement
The PAYMENT-REQUIRED header value is a base64-encoded JSON string. Decode it to get the payment terms.
Step 3 — Build the EIP-712 typed data hash locally
This is where Flow A differs from Flow B. You construct the domain, message, and hash yourself.
Step 4 — Sign the hash
Sign the raw hash using the wallet's ECDSA private key. This does not use eth_signTypedData — it signs the raw bytes directly. The signature is encoded as 65 bytes: r (32) + s (32) + v (1).
Step 5 — Build the payment-signature header
Package the signature and authorization details into an X402SignaturePayload object, JSON-encode it, then base64-encode the result.
Step 6 — Submit the payment
Send the signed payload to the submit endpoint. A 201 response with a sessionId confirms the payment was accepted and the on-chain transfer has been initiated.
Flow B — Facilitator-Assisted Build
Use this flow when you want to avoid constructing the EIP-712 domain yourself. The facilitator's /build endpoint computes the typed data hash for you — you only need to sign the hash it returns.
Flow B has the same Step 1 (Initiate) and Steps 4–6 (Sign + Submit) as Flow A. The difference is Step 2/3: instead of building the hash locally, you ask the facilitator to build it.
What you need before starting
Step 1 — Initiate the payment
Identical to Flow A Step 1.
Step 2 — Decode the payment requirement
Step 3 — Call /build to get the typed data hash
Instead of constructing the EIP-712 hash yourself, send the key parameters to the facilitator and it returns the hash ready to sign. You still generate the nonce and validBefore — this keeps the randomness and time window in your hands.
Step 4 — Sign the hash
Same as Flow A Step 4. Sign the hash returned by /build.
Step 5 — Build the payment-signature header
Same structure as Flow A Step 5. Use validAfter from the /build response (not a locally computed timestamp).
Step 6 — Submit the payment
Identical to Flow A Step 6.
Verify a Payment Reference
After a successful submit you receive a sessionId. If you need to later confirm on-chain proof or share the result with another system, use the verify endpoint:
Last updated