Skip to main content
This guide walks through the client side of the Flex flow on Solana: create an escrow, fund it, register a session key, and use @faremeter/payment-solana/flex/client to pay for an HTTP resource. For a complete reference implementation including transaction confirmation, grace-period waits, and full teardown, see scripts/solana-example/flex-payment.ts in the faremeter monorepo.

Prerequisites

  • Node.js 18+
  • A Solana devnet keypair with some SOL for rent and transaction fees
  • Devnet USDC in the keypair’s token account (Circle faucet, select Solana devnet)
  • The address of a Flex-capable facilitator (the same key the merchant declares as facilitator in their requirements)
1

Install

Set your wallet path and the facilitator address in .env:
.env
2

Set up the RPC, signer, and helpers

Create main.ts. The sendInstructions and confirmSignature helpers below are reused throughout the rest of the steps.
main.ts
3

Create the escrow

main.ts
The escrow PDA is derived from [b"escrow", owner, index]. Pass a unique index per escrow you want to keep open with the same facilitator. Date.now() is used here for convenience — production code should track indices deliberately.
4

Deposit funds

main.ts
The escrow’s vault PDA is created lazily on the first deposit per mint. An escrow can hold up to MAX_MINTS = 8 distinct token mints.
5

Register a session key

main.ts
Save the sessionKeyPair somewhere durable. You’ll use it for every payment authorization until you revoke it. Treat it like any other private key — leaking it lets an attacker sign authorizations, though they still can’t drain funds without colluding with the facilitator.
6

Pay with Faremeter

The @faremeter/payment-solana/flex/client package exposes createPaymentHandler, which plugs into @faremeter/fetch’s wrapper. It signs Flex authorizations on demand whenever a server returns a matching 402 Payment Required response.
main.ts
Run it:

What just happened

The handler:
  1. Inspects the server’s payment requirements and matches on (scheme, network, mint).
  2. Picks a maxAmount from the requirements and a fresh authorizationId.
  3. Builds a serialized authorization using serializePaymentAuthorization and signs it with the session key.
  4. Returns the signed payload; @faremeter/fetch retries the request with the PAYMENT-SIGNATURE header set.
The facilitator validates the authorization, holds the funds in memory, lets the request through, then settles the actual amount asynchronously. The client’s call returns once the response is ready — it does not wait for on-chain confirmation.

Cleanup (optional)

Tearing down state cleanly requires coordination with the facilitator (who must co-sign close_escrow). The end-to-end sequence is:
  1. Wait for in-flight settlements to land on-chain. Poll fetchEscrowAccount until pendingCount reflects everything you expect.
  2. Issue refunds for any pending settlements you don’t want finalized. This is a facilitator-signed instruction (getRefundInstruction); merchants typically trigger it via their own tooling.
  3. Revoke the session key, wait out the grace period, then close it (getRevokeSessionKeyInstructiongetCloseSessionKeyInstruction).
  4. Close the escrow. getCloseEscrowInstruction needs both the owner and the facilitator as TransactionSigners, plus mint_count * 2 writable remaining accounts (alternating vault PDA + owner-controlled destination per mint).
For a complete reference implementation including transaction confirmation and grace-period waits, see flex-payment.ts in the faremeter monorepo. If the facilitator has gone dark, the owner can recover funds without their cooperation. Once current_slot > last_activity_slot + deadman_timeout_slots, run void_pending for each open pending settlement (escrow owner is allowed to call it under deadman conditions), revoke and close every session key, then call emergency_close. emergency_close requires pending_count == 0 AND session_key_count == 0, so the cleanup order matters.

What’s next

  • Concepts — the protocol-level model behind these calls.
  • Facilitator — the operator side: hold management and settlement.
  • Program Reference — every on-chain instruction, account, and constraint.
  • API Reference — the full @faremeter/flex-solana surface.