@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
facilitatorin their requirements)
1
Install
.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
MAX_MINTS = 8 distinct token mints.5
Register a session key
main.ts
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 Run it:
@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
What just happened
The handler:- Inspects the server’s payment requirements and matches on
(scheme, network, mint). - Picks a
maxAmountfrom the requirements and a freshauthorizationId. - Builds a serialized authorization using
serializePaymentAuthorizationand signs it with the session key. - Returns the signed payload;
@faremeter/fetchretries the request with thePAYMENT-SIGNATUREheader set.
Cleanup (optional)
Tearing down state cleanly requires coordination with the facilitator (who must co-signclose_escrow). The end-to-end sequence is:
- Wait for in-flight settlements to land on-chain. Poll
fetchEscrowAccountuntilpendingCountreflects everything you expect. - 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. - Revoke the session key, wait out the grace period, then close it (
getRevokeSessionKeyInstruction→getCloseSessionKeyInstruction). - Close the escrow.
getCloseEscrowInstructionneeds both the owner and the facilitator asTransactionSigners, plusmint_count * 2writable remaining accounts (alternatingvaultPDA + owner-controlled destination per mint).
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-solanasurface.