meridian_x402 program. It is written for sellers, buyers, and AI coding agents
implementing either side of the payment flow.
This path applies to the solana (mainnet-beta) and solana-devnet networks.
It is same-chain only — there is no Across cross-chain routing on Solana in
this version. Do not use the EIP-3009 or Permit2 flows here: Solana has no
EIP-712 signing, no Permit2, and no token approvals. The buyer signs the whole
settlement transaction instead, and that signature is the authorization.
The integration needs only @solana/web3.js and @solana/spl-token — no
Anchor, no IDL, and no on-chain config reads. Everything the buyer needs to
build the transaction comes from a single API call:
GET /v1/solana/facilitator.
The Solana program is live on both
solana (mainnet) and solana-devnet.
Do not hardcode the program ID. Read it (and everything else)
from GET /v1/solana/facilitator at runtime so your integration keeps working
across any redeploys. See Solana Program.How the flow works
paymentRequirements.assetis the USDC mint address, not a token accountpaymentRequirements.payTois the seller’s recipient wallet (the owner, not a token account); it must match the Solana recipient on your Meridian organizationpaymentRequirements.networkissolanaorsolana-devnetpaymentRequirements.extra.feePayeris the facilitator pubkey fromGET /v1/solana/facilitator; it pays the transaction fee and must equal the on-chainconfig.backendSignerpaymentRequirements.extra.nameis"USDC"andextra.decimalsis6- The buyer signs the full transaction; there is no typed-data signature, no token approval, and no Permit2
- All USDC associated token accounts (payer, recipient, platform, treasury) must already exist — the settlement transaction never creates accounts
- The buyer needs no SOL; the facilitator pays the transaction fee
- The seller keeps the Meridian API key on the backend
Prerequisites
Seller:- A Meridian organization with a Solana recipient wallet configured
- An organization API key from
https://mrdn.finance/dev/api-keys - A backend endpoint that can receive
paymentPayloadfrom the buyer
- A Solana wallet (Phantom, Solflare, Backpack, …)
- A USDC balance on the selected network — on
solana-devnet, get test USDC from Circle’s faucet athttps://faucet.circle.com(select Solana Devnet) - An existing USDC associated token account (the recipient’s must exist too)
Network constants
The facilitator info endpoint
One GET returns everything needed to build the settlement transaction — the fee-payer pubkey, program ID, config PDA, USDC mint, treasury token account, protocol fee, and pause state (read on-chain by the facilitator and cached):usdcMint, treasury, treasuryToken, treasuryFeeBps,
paused) mirror the program’s on-chain Config account, so clients no longer
read it themselves. If they are missing, the facilitator could not reach the
chain — fail closed and retry rather than guessing values.
Seller setup
1. Create a Meridian API key
Seller-side setup starts in Meridian:- Connect the seller wallet at
https://mrdn.finance - Configure your organization’s Solana recipient wallet
- Open
https://mrdn.finance/dev/api-keysand create an organization-scoped key - Store the public
pk_...key on the server
Authorization header when calling Meridian:
2. Build paymentRequirements
Use the USDC mint as asset and your recipient wallet as payTo. Fetch the
fee-payer pubkey from the facilitator and put it in extra.feePayer.
3. Receive paymentPayload
The buyer sends a base64-encoded, already-signed Solana transaction under
payload.transaction. Build or retrieve the matching paymentRequirements
server-side; do not trust buyer-supplied requirements for pricing, recipient,
network, or asset.
X-PAYMENT header; decode it
before settling:
4. Settle with Meridian
CallPOST /v1/settle from the seller backend. Meridian validates the signed
transaction, co-signs as fee payer, submits it, and confirms on-chain. Do not
expose the Meridian API key to an untrusted client.
Buyer setup
1. Select a payment requirement and fetch the facilitator info
Config PDA at info.configPda, but this
is not required.)
2. Build the settlement transaction
Build onetransfer_with_authorization instruction with plain
@solana/web3.js — no Anchor, no IDL. This helper is self-contained;
copy-paste it as-is:
If the monorepo package is available to you (Meridian’s own apps), the same
builder ships as
buildSettleTransaction / fetchSolanaFacilitatorInfo from
@meridian/x402/shared/svm — import it instead of copy-pasting.3. Sign the transaction with the buyer wallet
The buyer signs once, as the USDC token authority. The facilitator adds the fee payer’s signature later, so serialize without requiring all signatures.4. Send paymentPayload to the seller
Instruction reference
For implementations in other languages, the layout (stable across deployments — it is derived from the instruction name):payer (signer, writable), from (signer), config, usdc_mint,
from_token (writable), recipient_token (writable), platform_token
(writable — or the program ID as a read-only placeholder when there is no
platform fee), treasury_token (writable), token_program.
Checklist for AI agents
- Read the seller’s existing route structure before adding payment handling
- Keep settlement on the backend; never ship the Meridian API key to the client
- Use
network = "solana"or"solana-devnet" - Use
asset = USDC mint address - Use
payTo = the seller's recipient wallet(owner, not a token account), matching the org’s configured Solana recipient - Fetch everything (
programId,facilitator,configPda,usdcMint,treasuryToken,paused) fromGET /v1/solana/facilitator; hardcode nothing - Set
extra.feePayerto the facilitator pubkey andtx.feePayerto it too - Check
info.paused === falseand that the 402’sfeePayer/assetmatch the facilitator info before signing - Ensure the recipient (and platform) USDC associated token account exists first
- Generate a fresh 32-byte nonce per payment
- Always pass 9 accounts (program ID placeholder in the platform slot)
- Have the buyer sign once and serialize with
requireAllSignatures: false - Send
payload.transactionas base64; keep amounts as decimal strings
Common mistakes
- Using EIP-3009 typed data, Permit2, or a token
approveon Solana — none apply - Setting
assetto a token account instead of the USDC mint - Setting
payToto a token account instead of the recipient wallet owner - Hardcoding the program ID instead of reading it from the facilitator endpoint
- Omitting the
platform_tokenslot (8 accounts) instead of passing the program ID placeholder — the account list must always be 9 long - Setting
tx.feePayerto the buyer wallet instead of the facilitator - Paying to a recipient whose USDC associated token account does not exist yet
- Expecting the settlement transaction to create token accounts (it never does)
- Serializing with
requireAllSignatures: truebefore the facilitator co-signs - Reusing a nonce, or expecting cross-chain (Across) routing on Solana
References
- Solana Program — program architecture and accounts
- Supported Networks
- Payment Types
- Settle x402 Payment
- x402 SVM scheme