TransX402 Docs
Integrations

Server Library

@transx402/server — merchant backend SDK for canonical x402 settlement (402 responses, processGate, config proxy).

Overview

@transx402/server is a zero-dependency Node package for merchant backends. Use it with @transx402/client in settlement: "server" mode (the default for fetch()).

Canonical flow:

  1. Client hits your API → return 402 from payments.processGate / payments.buildRequired
  2. Client wallet signs → retries with PAYMENT-SIGNATURE
  3. Your Route Handler calls tx402.payments.processGate → POST /facilitate with server X-API-Key
  4. On success, return paid content

TransX402 uses a combined POST /facilitate (verify + settle in one call). Keep ipk_sandbox_ / ipk_live_ keys on the server.

For paywall / static sites without a merchant settle endpoint, use client settlement: "direct" instead.

Requires Node.js 20.19+. There are no Express / Hono / Fastify middleware packages — pass Web Standard Request / Headers (or a Node http header record) yourself. Next.js App Router is the primary example path.

Install

npm install @transx402/server
# Peer workflow:
npm install @transx402/client

Source: github.com/campinvestment/transx402-server

You must pass environment and/or facilitatorUrl to the constructor. The key family must match (ipk_sandbox_ ≠ environment: "base").

Quick start (Route Handler)

import { TransX402 } from "@transx402/server";
 
const tx402 = new TransX402(process.env.TRANSX402_API_KEY!, {
  environment: "camp", // or "local" | "base"
});
 
export async function GET(request: Request) {
  const gate = await tx402.payments.processGate({
    headers: request.headers,
    payTo: process.env.MERCHANT_WALLET!,
    priceIdr: "5000",
    resourceUrl: request.url,
  });
 
  if (gate.kind === "paymentRequired") {
    return Response.json(gate.body, { status: 402 });
  }
  if (gate.kind === "failed") {
    return Response.json(
      { error: gate.error, code: gate.code, details: gate.details },
      { status: gate.status }
    );
  }
 
  return Response.json({
    paid: true,
    txHash: gate.txHash,
    content: "Premium unlocked",
  });
}

priceIdr is whole IDR (converted ×100 to IDRX base units). payTo must match the dashboard merchant wallet.

Browser / agent:

import { createBrowserClient } from "@transx402/client/browser";
 
const client = createBrowserClient({
  environment: "camp",
  settlement: "server",
  configProxyPath: "/api/transx402",
});
 
await client.fetch("/api/premium");

Config proxy (server settlement)

Browsers load chain params via GET /config. Without a merchant-domain CORS allowlist on the hosted facilitator, that cross-origin call fails. Expose a same-origin proxy and point the browser client at it.

Default proxy base path: /api/transx402 (client appends /config).

// app/api/transx402/config/route.ts
import { TransX402 } from "@transx402/server";
 
const tx402 = new TransX402(process.env.TRANSX402_API_KEY!, {
  environment: "camp",
});
 
export async function GET(request: Request) {
  return tx402.config.handleRequest(request, {
    isConfigured: () => Boolean(process.env.TRANSX402_API_KEY?.trim()),
  });
}

Your payment Route Handler still talks to the facilitator directly for 402 bodies and POST /facilitate. Only the browser uses the proxy.

API

Client

MemberPurpose
new TransX402(apiKey, options)Configure once (environment and/or facilitatorUrl)
tx402.payments.processGateNo header → 402; header → facilitate
tx402.payments.facilitateDecode header → POST /facilitate (throws FacilitationError)
tx402.payments.verifyPost-settlement GET /payments/:txHash
tx402.payments.buildRequiredBuild x402 v2 402 JSON from facilitator /config
tx402.config.handleRequestWeb Standard handler for merchant config proxy routes
tx402.config.fetchUpstream GET /config

processGate result:

| { kind: "paymentRequired"; status: 402; body: PaymentRequiredResponse }
| { kind: "settled"; status: 200; txHash: string | null }
| { kind: "failed"; status: 402 | 500; error: string; code?: string; details?: Record<string, string> }

Free helpers

ExportPurpose
hasPaymentHeader / getPaymentHeaderRead PAYMENT-SIGNATURE / X-PAYMENT
decodePaymentSignatureBase64 JSON decode of payment payload
toIdrxBaseUnitsWhole IDR → IDRX base units (×100)
detectApiKeyFamilyipk_sandbox_ / ipk_live_ → section
browserFacilitatorProxyBase / DEFAULT_FACILITATOR_CONFIG_PROXY_BASESame-origin base path ("/api/transx402")
FACILITATOR_PRESETSNamed facilitator hosts
FacilitationErrorError class from facilitate failures

Prefer PAYMENT-SIGNATURE; X-PAYMENT is a fallback.

Migration from 0.2.x

// Before (removed)
const { facilitatorUrl, configSection } = resolveServerConfig({ apiKey, environment });
await processPaymentGate({ headers, facilitatorUrl, apiKey, configSection, ... });
 
// After
const tx402 = new TransX402(apiKey, { environment });
await tx402.payments.processGate({ headers, payTo, priceIdr, resourceUrl });

These are no longer public: resolveServerConfig, processPaymentGate, facilitatePayment, verifyPayment, buildPaymentRequired, fetchFacilitatorConfig, handleFacilitatorConfigRequest.

On this page