For the complete documentation index, see llms.txt. This page is also available as Markdown.

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 ethers
import { 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 key

Step 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