Solana Enterprise Payment Gateway

MCP serverCommerce & finance

RFC 9110 x402 Solana compliance screening and micropayments gateway for AI agents.

Unavailable. This server has no hosted endpoint yet, so ahel can't serve it.

Add to setup to save this item as a reference. ahel cannot run it, and signing in will not install it.

Getting started

  1. Save this item in Your setup as a reference.
  2. Read the source or reference documentation for its setup requirements. Saving it here does not connect it to your AI.
  3. Check this page for availability before trying to install it through ahel.

From the project's README

As published by msantagiulianab/solana-enterprise-payment-gateway in README.md.

High-Throughput HTTP 402 Payment Channel Middleware

Language / runtime note. The Maven build targets Java 21 bytecode (<java.version>21</java.version>), the Spring Boot 3.4 baseline. Container images build and run on Eclipse Temurin JDK 25 (eclipse-temurin:25-jdk / 25-jre-alpine). Both are stated explicitly because the host toolchain is JDK 25 while the source level remains Java 21.

An institutional-grade, zero-Web3-SDK middleware that meters HTTP APIs with the x402 protocol and RFC 9110 §15.5.3 402 Payment Required semantics. It validates Ed25519-signed, off-chain payment vouchers in-memory in under 5ms on the request hot path, persists every verification to an append-only PostgreSQL audit ledger, and sweeps cumulative channel balances on-chain in batched settlement transactions — all without any Node.js sidecar, Python bridge, or generic Web3 Java wrapper.

AI Agent Integration (Model Context Protocol)

Autonomous AI agents can screen Solana addresses and settle compliance micro-payments through our published Model Context Protocol (MCP) server. The server is a zero-dependency x402 compliance tool: it speaks the RFC 9110 402 Payment Required challenge-and-response protocol natively, signs Ed25519 channel vouchers in-memory (Node.js built-in crypto, no Web3 SDK), and negotiates settlement on every call.

Direct Execution

npx -y @msantagiulianab/x402-mcp-server

Configuration

The server reads two environment variables:

VariableValuePurpose
X402_GATEWAY_URLhttps://msb-solana-enterprise-payment-gateway.duckdns.orgGateway root URL
X402_CHANNEL_IDchan_smoke_test_001x402 payment channel id
Claude Desktop

Add an entry to claude_desktop_config.json.

macOS / Linux

{
  "mcpServers": {
    "solana-x402-compliance": {
      "command": "npx",
      "args": ["-y", "@msantagiulianab/x402-mcp-server"],
      "env": {
        "X402_GATEWAY_URL": "https://msb-solana-enterprise-payment-gateway.duckdns.org",
        "X402_CHANNEL_ID": "chan_smoke_test_001"
      }
    }
  }
}

Windows

{
  "mcpServers": {
    "solana-x402-compliance": {
      "command": "cmd",
      "args": ["/c", "npx", "-y", "@msantagiulianab/x402-mcp-server"],
      "env": {
        "X402_GATEWAY_URL": "https://msb-solana-enterprise-payment-gateway.duckdns.org",
        "X402_CHANNEL_ID": "chan_smoke_test_001"
      }
    }
  }
}
VS Code Cline

Add the server to cline_mcp_settings.json:

{
  "mcpServers": {
    "solana-x402-compliance": {
      "command": "npx",
      "args": ["-y", "@msantagiulianab/x402-mcp-server"],
      "env": {
        "X402_GATEWAY_URL": "https://msb-solana-enterprise-payment-gateway.duckdns.org",
        "X402_CHANNEL_ID": "chan_smoke_test_001"
      }
    }
  }
}

Verified Dual Compliance Screening Outcomes

The screen_solana_address tool returns one of two verified outcomes:

CounterpartyRisk scoreVerdictFlags
Clear counterparty0CLEAR_TO_TRANSACTnone
Malicious / sanctioned counterparty100BLOCKEDOFAC_SANCTIONED / drainer detection (EXPLOIT_DRAINER)

Model Context Protocol (MCP) Server

This repository contains the official open-source MCP server implementation located at /agent-tools/mcp-server.


Table of Contents

  1. Executive Architecture & Core Thesis
  2. System Components
  3. Complete Protocol Sequence Diagram
  4. Cryptographic & Voucher Wire Specification
  5. HTTP Wire Headers & JSON Schemas
  6. Database Schema & State Transitions
  7. Quickstart & Verification
  8. Configuration Parameters
  9. Production Extension & Customization Guide
  10. Project Layout
  11. Testing
  12. Security & Compliance Posture

1. Executive Architecture & Core Thesis

Problem

Base-layer blockchain latency and per-transaction fees are fundamentally incompatible with high-throughput, micro-metered HTTP APIs. Use cases such as:

  • AI inference billed per token or per call,
  • RWA (real-world asset) valuation feeds billed per oracle read,
  • Compliance / sanctions screening billed per address,

…each issue millions of sub-cent requests per day. Settling every call directly on Solana would impose block-confirmation latency (hundreds of milliseconds to seconds) and a per-transaction fee that dwarfs the price of a single metered call. The economics do not close, and the user experience collapses.

Solution

The gateway decouples the metering decision from the settlement transaction using the x402 HTTP challenge-and-response protocol:

  1. Monotonic unidirectional off-chain state channels. A client presents a signed voucher whose cumulativeAmountAtomic only ever increases for a channel. The gateway trusts the voucher only insofar as (a) it is signed by the channel owner, (b) its nonce is strictly monotonic, and (c) its cumulative spend does not exceed the verified on-chain escrow deposit.
  2. RFC 9110 402 Payment Required filter. A Spring OncePerRequestFilter issues a PAYMENT-REQUIRED challenge to unauthenticated callers and accepts a PAYMENT-SIGNATURE voucher on retry, attaching a PAYMENT-RESPONSE receipt on success.
  3. < 5ms hot path. Voucher verification is pure in-memory Ed25519 crypto plus a short-TTL cache of the escrow balance. No synchronous Solana RPC call is ever made on the HTTP request path.
  4. Batched on-chain settlement. An administrative endpoint sweeps a channel's highest-nonce verified cumulative amount to the treasury in a single signed transaction, recorded atomically in the audit ledger.

The result is deterministic, single-digit-millisecond payment gating with fail-closed rejection, a complete audit trail, and on-chain finality only where it matters: at settlement.

Zero-Dependency Philosophy

The gateway deliberately avoids all generic Web3 SDK baggage:

ConcernImplementationDependency
Ed25519 signing/verificationEd25519Signer (BouncyCastle)bcprov-jdk18on only
Base58 codecHand-rolled Base58 (no alphabet mistakes)zero
compact-u16 (shortvec)Hand-rolled CompactU16zero
Canonical account sortingSolanaWireTransactionBuilderzero
Transaction serialization & signingSolanaWireTransactionBuilder + SolanaKeypairServicezero
Solana RPCJDK java.net.http.HttpClient + Jackson JSON-RPC 2.0zero

There is no @solana/web3.js, no solana4j/p4j, and no runtime invocation of an external process. Wire transactions are serialized byte-by-byte from first principles, and the JVM-native crypto stack is the only third-party cryptographic primitive.

2. System Components

The gateway follows a strict layered separation:

HTTP Request
   │
   ▼
X402PaymentFilter (OncePerRequestFilter)          ── 402 challenge / 403 reject / pass-through
   │
   ├──▶ ChannelVoucherVerifier (service)          ── in-memory nonce watermark + Ed25519 + ceiling check
   │        └──▶ EscrowBalanceProvider            ── SolanaEscrowVerifier (short-TTL cache)
   │
   ├──▶ PaymentAuditService (service)             ── append VERIFIED record
   │        └──▶ PaymentAuditRepository (JPA)     ── append-only PostgreSQL ledger
   │
   └──▶ downstream @RestController               ── compliance screening, (your) metered APIs

Settlement (admin, off hot path):
   POST /api/v1/settlement/channels/{id}/sweep
   │
   ▼
ChannelSettlementService (service)
   ├──▶ SolanaRpcClient                          ── getLatestBlockhash / sendTransaction (JSON-RPC 2.0)
   ├──▶ SolanaWireTransactionBuilder             ── serialize + sign (in-process)
   └──▶ PaymentAuditRepository                   ── VERIFIED → SETTLED (markSettled)
ComponentPackageResponsibility
X402PaymentFilterfilter402 challenge, header decode, 403 fail-closed, receipt attach
ChannelVoucherVerifierservicein-memory anti-replay + signature + escrow-ceiling checks
SolanaEscrowVerifierserviceon-chain escrow balance with short-TTL cache (EscrowBalanceProvider)
PaymentAuditServiceserviceappend-only audit persistence
ChannelSettlementServiceserviceon-chain sweep + VERIFIED → SETTLED transition
PaymentAuditRepositoryrepositoryread/insert only (no update/delete declared)
SolanaRpcClientrpcfail-closed JSON-RPC 2.0 (getAccountInfo, getLatestBlockhash, sendTransaction)
SolanaWireTransactionBuilderserializationlegacy tx wire format, account sorting, SOL/SPL instructions
Ed25519SignatureVerifierserializationBouncyCastle Ed25519 verify
SolanaKeypairServiceserializationkeypair derivation + in-process signing
Base58, CompactU16, SolanaAddressValidatorserializationzero-dependency codecs & validation

3. Complete Protocol Sequence Diagram

The full x402 challenge-and-response lifecycle across the client, the gateway (filter / verifier / ledger / settlement), and the Solana RPC node:

 Client                Gateway (Spring Boot)                                Solana RPC        PostgreSQL
   │                           │                                              │                 │
   │ 1. POST /api/v1/...       │                                              │                 │
   │    (no payment headers)   │                                              │                 │
   │ ─────────────────────────▶│                                              │                 │
   │                           │ X402PaymentFilter.shouldNotFilter()          │                 │
   │                           │  · /api/v1/*  → protected                    │                 │
   │                           │  · /api/v1/settlement/* → skip (admin)       │                 │
   │                           │                                              │                 │
   │ 2. HTTP 402 + PAYMENT-REQUIRED (Base64 challenge JSON)                  │                 │
   │ ◀─────────────────────────│                                              │                 │
   │                           │                                              │                 │
   │ 3. POST /api/v1/...       │                                              │                 │
   │    + PAYMENT-SIGNATURE    │                                              │                 │
   │    (Base64 voucher JSON)  │                                              │                 │
   │ ─────────────────────────▶│                                              │                 │
   │                           │ 4. Base64-decode → PaymentVoucher record     │                 │
   │                           │ 5. ChannelVoucherVerifier.verifyVoucher()    │                 │
   │                           │    a. Base58 payer key valid?                │                 │
   │                           │    b. nonce > lastSeenNonce[channel]?        │                 │
   │                           │    c. cumulative ≤ escrow ceiling?           │                 │
   │                           │       (short-TTL cache; NO RPC on hot path)  │                 │
   │                           │    d. Ed25519 verify(canonical bytes)        │                 │
   │                           │    ── any failure → 403 Forbidden (fail-     │                 │
   │                           │       closed) and audit log                  │                 │
   │                           │ 6. PaymentAuditService.recordVerifiedVoucher │                 │
   │                           │ ─────────────────────────────────────────────────────────────────▶  append
   │                           │                                              │              VERIFIED row
   │ 7. HTTP 200 + PAYMENT-RESPONSE (Base64 receipt JSON)                    │                 │
   │ ◀─────────────────────────│                                              │                 │
   │                           │                                              │                 │
   │  [later] POST /api/v1/settlement/channels/{id}/sweep                    │                 │
   │ ─────────────────────────▶│                                              │                 │
   │                           │ 8. ChannelSettlementService.settleChannel()  │                 │
   │                           │    a. load latest VERIFIED record            │                 │
   │                           │    b. getLatestBlockhash() ──────────────────▶│                 │
   │                           │ ◀─────────────────────────────── blockhash   │                 │
   │                           │    c. SolanaWireTransactionBuilder            │                 │
   │                           │       .serializeAndSign() (in-process)       │                 │
   │                           │    d. sendTransaction(wireTx) ───────────────▶│                 │
   │                           │ ◀─────────────────────────────── txSignature │                 │
   │                           │ 9. record.markSettled(txSignature)           │                 │
   │                           │ ─────────────────────────────────────────────────────────────────▶  SETTLED row
   │ 10. HTTP 200 { txSignature, settledAmountAtomic, ... }                  │                 │
   │ ◀─────────────────────────│                                              │                 │

Key invariant: steps 4–6 never touch the network. The only RPC interactions are the off-path settlement sweep (step 8), keeping the request hot path deterministic and sub-5ms.

4. Cryptographic & Voucher Wire Specification

Canonical byte layout

A voucher is signed over a deterministic, domain-separated byte string (see PaymentVoucher#getCanonicalPayload()):

"X402_CHANNEL_V1:" || u16le(channelId.length) || channelId || i64le(cumulativeAmountAtomic) || i64le(nonce)
OffsetSize (bytes)FieldEncoding
016domain tagASCII X402_CHANNEL_V1:
162channelId.lengthunsigned 16-bit little-endian (putShort)
18NchannelIdraw UTF-8 bytes
18 + N8cumulativeAmountAtomicsigned 64-bit little-endian (putLong)
26 + N8noncesigned 64-bit little-endian (putLong)

The domain tag provides cross-protocol disambiguation (a signature over these bytes can never be replayed against another message scheme). Both integer fields are little-endian to match the JVM ByteOrder.LITTLE_ENDIAN buffer and the JavaScript reference signer in smoke-test.sh, which mirrors the layout exactly (writeUInt16LE + writeBigInt64LE).

Ed25519 over Base58 public keys

  • The payer public key is a Base58-encoded 32-byte Ed25519 public key.
  • The signature is a Base58-encoded 64-byte Ed25519 signature.
  • Verification is org.bouncycastle.crypto.signers.Ed25519Signer initialized in verify mode with Ed25519PublicKeyParameters(pubkeyBytes, 0).
  • SolanaAddressValidator.isValid() rejects anything that does not decode to exactly 32 bytes — a structural guard that runs before the crypto check.

Monotonic nonce guarantees & anti-replay mechanics

  1. Strict monotonicity (in-memory). ChannelVoucherVerifier maintains a ConcurrentHashMap<String, Long> of the highest nonce seen per channel. Any voucher whose nonce <= lastSeenNonce[channel] is rejected before any cryptographic work, returning 403 Forbidden.
  2. Constraint-level replay defense (database). The audit ledger enforces a UNIQUE (channel_id, nonce) index (uk_payment_audit_ledger_channel_nonce), so a replayed nonce can never be persisted even if the in-memory watermark is bypassed (e.g. after a restart with an empty map).
  3. Monotonic cumulative amount. Because cumulativeAmountAtomic only grows across a channel's voucher sequence, the gateway never needs per-call payment state — it trusts the latest cumulative value as the running spend ceiling.
  4. Fail-closed ceiling. If the cumulative amount exceeds the verified escrow deposit ceiling (EscrowBalanceProvider), the voucher is rejected. A missing, depleted, or unreadable escrow account yields a ceiling of 0, never a positive balance.

Voucher JSON payload

{
  "channelId": "chan_demo_solana_001",
  "payerPubkey": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "cumulativeAmountAtomic": 5000,
  "nonce": 10,
  "signature": "<Base58 64-byte Ed25519 signature>"
}

5. HTTP Wire Headers & JSON Schemas

All x402 headers are Base64-encoded JSON. The gateway accepts and emits both a canonical and an X--prefixed alias for forward compatibility.

DirectionCanonical headerAliasMeaning
Server → ClientPAYMENT-REQUIREDX-PAYMENT-REQUIRED402 challenge (PaymentRequired)
Client → ServerPAYMENT-SIGNATUREX-PAYMENTsigned voucher (PaymentVoucher)
Server → ClientPAYMENT-RESPONSEX-PAYMENT-RESPONSEsettlement receipt (PaymentSettlementReceipt)

Challenge (PAYMENT-REQUIRED)

{
  "x402Version": 2,
  "scheme": "channel",
  "network": "solana:devnet",
  "escrowAddress": "7xKXtg2CW87d97TXJSDpbD5jBkheTqA83TZRuJosgAsU",
  "asset": "USDC",
  "priceAtomicUnits": 5000,
  "unit": "per-call",
  "message": "Payment required via Solana payment channel or gasless voucher"
}

Receipt (PAYMENT-RESPONSE)

{
  "channelId": "chan_demo_solana_001",
  "settledAmountAtomic": 5000,
  "nonce": 10,
  "timestamp": 1750000000000,
  "status": "VERIFIED"
}

Example curl flows

# 0) Machine-readable x402 discovery document (unauthenticated)
curl -i http://localhost:8080/.well-known/x402.json
# → HTTP/1.1 200 { "x402Version": 2, "name": "...", "services": [ ... ] }

# 1) Unauthenticated request → 402 challenge
curl -i -X POST http://localhost:8080/api/v1/compliance/screen-address \
  -H 'Content-Type: application/json' \
  -d '{"address":"4Nd1mBQtrMJVYVfKf2PJy9NZGibCcTRxpETqdrBHu19Y"}'
# → HTTP/1.1 402, PAYMENT-REQUIRED: <Base64 challenge JSON>

# 2) Replay with a signed voucher header → 200 + receipt
curl -i -X POST http://localhost:8080/api/v1/compliance/screen-address \
  -H 'Content-Type: application/json' \
  -H "PAYMENT-SIGNATURE: <Base64 voucher JSON>" \
  -d '{"address":"4Nd1mBQtrMJVYVfKf2PJy9NZGibCcTRxpETqdrBHu19Y"}'
# → HTTP/1.1 200, PAYMENT-RESPONSE: <Base64 receipt JSON>

# 3) Administrative on-chain settlement sweep
curl -i -X POST http://localhost:8080/api/v1/settlement/channels/chan_smoke_test_001/sweep
# → HTTP/1.1 200 { "channelId": "...", "settledAmountAtomic": 5000, "txSignature": "...", ... }

6. Database Schema & State Transitions

The schema is owned exclusively by versioned Flyway migrations (src/main/resources/db/migration). Production runs with spring.jpa.hibernate.ddl-auto: validate, so Hibernate never emits DDL — it only verifies that the JPA entity maps onto the migrated schema.

payment_audit_ledger (V1 → V2)

ColumnTypeConstraintsNotes
idbigserialPKmonotonically increasing append order
channel_idvarchar(64)NOT NULL, indexedx402 channel identifier
payer_pubkeyvarchar(44)NOT NULL, indexedBase58 32-byte payer public key
cumulative_amount_atomicbigintNOT NULLrunning spend ceiling in atomic units
noncebigintNOT NULL, UNIQUE w/ channelanti-replay monotonic counter
signaturevarchar(88)NOT NULLBase58 Ed25519 voucher signature
statusvarchar(32)NOT NULLVERIFIED or SETTLED
tx_signaturevarchar(88)NULL (V2)on-chain sweep transaction signature
created_attimestamptzNOT NULL DEFAULT NOW()immutable append timestamp

Indexes

IndexColumnsPurpose
pk_payment_audit_ledgeridprimary key
uk_payment_audit_ledger_channel_nonce(channel_id, nonce) UNIQUEconstraint-level replay defense
idx_payment_audit_ledger_payer_pubkeypayer_pubkeypayer lookups
idx_payment_audit_ledger_channel_idchannel_idchannel audit reads
  • V1 — V1__init_payment_audit_ledger.sql creates the table, the unique anti-replay index, and the two lookup indexes.
  • V2 — V2__add_settlement_tx_signature.sql adds the nullable tx_signature column so previously-appended VERIFIED rows remain valid until swept.

Append-only contract

The table is append-only by contract: the application never issues UPDATE or DELETE against it, the PaymentAuditRepository declares no mutation methods beyond save (insert), and the JPA entity exposes no mutators other than the single legal state transition below.

Channel state lifecycle

        voucher verified & appended
   ┌──────────────────────────────────▶  VERIFIED
   │                                        │
   │                                        │  ChannelSettlementService.settleChannel()
   │                                        │  → record.markSettled(txSignature)
   │                                        ▼
   └────────────────────────────────────  SETTLED

PaymentAuditRecord.markSettled(txSignature) enforces the only legal transition:

  • VERIFIED → SETTLED — allowed only once a non-blank txSignature is supplied.
  • Any other transition (e.g. SETTLED → SETTLED, or null → SETTLED) throws IllegalStateException.

Shortened here. Read the whole README on GitHub.

Signals

Last commit
Sep 2026
Weekly_downloads
1k weekly_downloads
Advanced
Delivery
solana-x402-compliance MCP server → your ahel connector (mcp.ahel.ai) → your AI.
Item type
mcp-server
Key
io-github-msantagiulianab-solana-x402-compliance
Source
github.com/msantagiulianab/solana-enterprise-payment-gateway