Skip to main content
Use this guide to integrate Meridian x402 payments on Solana, where the buyer pays USDC (an SPL token) and settlement runs through the Meridian 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-tokenno 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

Meridian-specific rules:
  • paymentRequirements.asset is the USDC mint address, not a token account
  • paymentRequirements.payTo is the seller’s recipient wallet (the owner, not a token account); it must match the Solana recipient on your Meridian organization
  • paymentRequirements.network is solana or solana-devnet
  • paymentRequirements.extra.feePayer is the facilitator pubkey from GET /v1/solana/facilitator; it pays the transaction fee and must equal the on-chain config.backendSigner
  • paymentRequirements.extra.name is "USDC" and extra.decimals is 6
  • 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 paymentPayload from the buyer
Buyer:
  • A Solana wallet (Phantom, Solflare, Backpack, …)
  • A USDC balance on the selected network — on solana-devnet, get test USDC from Circle’s faucet at https://faucet.circle.com (select Solana Devnet)
  • An existing USDC associated token account (the recipient’s must exist too)
Install the client dependencies:

Network constants

The public RPC endpoints above are heavily rate-limited. Use a dedicated devnet/mainnet RPC (Helius, QuickNode, Triton, …) for anything beyond a quick test. The buyer only needs the RPC for a recent blockhash.

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):
The enriched fields (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:
  1. Connect the seller wallet at https://mrdn.finance
  2. Configure your organization’s Solana recipient wallet
  3. Open https://mrdn.finance/dev/api-keys and create an organization-scoped key
  4. Store the public pk_... key on the server
Use the key in the 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.
If you expose a standard HTTP 402 challenge, return:

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.
Clients usually deliver this base64-encoded in an X-PAYMENT header; decode it before settling:

4. Settle with Meridian

Call POST /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

Because the info is fetched directly from the facilitator over HTTPS, it is the trust anchor for the payment — a tampered 402 from a malicious seller cannot redirect fees or swap the mint. (The paranoid can additionally verify the returned values against the on-chain Config PDA at info.configPda, but this is not required.)

2. Build the settlement transaction

Build one transfer_with_authorization instruction with plain @solana/web3.js — no Anchor, no IDL. This helper is self-contained; copy-paste it as-is:
Then use it:
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.
Confirm the recipient’s USDC associated token account exists before paying — the settlement transaction does not create it. Getting a TokenAccountNotFoundError at settle time means the recipient (or platform) ATA is missing.

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):
Account order — always 9 accounts, all non-signer/read-only unless noted: 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.
Do not omit the platform_token slot when there is no platform fee. Anchor resolves optional accounts positionally: an 8-account list misaligns treasury_token and token_program and the facilitator rejects it. Pass the program ID in slot 6 as the “no platform” placeholder.

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) from GET /v1/solana/facilitator; hardcode nothing
  • Set extra.feePayer to the facilitator pubkey and tx.feePayer to it too
  • Check info.paused === false and that the 402’s feePayer/asset match 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.transaction as base64; keep amounts as decimal strings

Common mistakes

  • Using EIP-3009 typed data, Permit2, or a token approve on Solana — none apply
  • Setting asset to a token account instead of the USDC mint
  • Setting payTo to a token account instead of the recipient wallet owner
  • Hardcoding the program ID instead of reading it from the facilitator endpoint
  • Omitting the platform_token slot (8 accounts) instead of passing the program ID placeholder — the account list must always be 9 long
  • Setting tx.feePayer to 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: true before the facilitator co-signs
  • Reusing a nonce, or expecting cross-chain (Across) routing on Solana

References