GOAT Network
AgentKit

x402 Payments

AgentKit provides two complementary plugin sets for the x402 payment protocol:

  • x402 plugin (payer side) -- 5 actions enabling your agent to create payments, sign EIP-712 authorization data, transfer tokens, check status, and cancel orders
  • x402-merchant plugin (merchant side) -- 30 actions for managing a full merchant portal: auth, orders, balance, webhooks, API keys, and more

Before wiring the plugins, read the protocol-side x402 overview, developer quick start, and payment modes. This page stays focused on AgentKit-specific usage.

Payer Side: x402 Plugin

The payer-side flow uses two adapter interfaces:

  • MerchantGatewayAdapter -- communicates with the merchant's payment API
  • PayerWalletAdapter -- signs EIP-712 typed data and transfers tokens

Payment Flow

x402 Payment Flow
Agent                    Merchant Gateway           Blockchain
  │                           │                        │
  │ 1. createPayment -------->│                        │
  │ <--- paymentId + EIP-712  │                        │
  │                           │                        │
  │ 2. signTypedData -------->│ (local EIP-712 sign)   │
  │ 3. submitSignature ------>│                        │
  │ <--- authorized           │                        │
  │                           │                        │
  │ 4. transferToken ---------│----------------------->│
  │                           │                        │
  │ 5. status/cancel -------->│                        │

Setup

src/x402-setup.ts
import { ActionProvider } from '@goatnetwork/agentkit/providers';
import {
  createPaymentAction,
  submitSignatureAction,
  transferPaymentAction,
  paymentStatusAction,
  cancelPaymentAction,
  HttpMerchantGatewayAdapter,
  EvmPayerWalletAdapter,
} from '@goatnetwork/agentkit/plugins';
import { Wallet } from 'ethers';

// 1. Create the merchant gateway adapter (HTTP client to your merchant)
const merchant = new HttpMerchantGatewayAdapter(
  process.env.MERCHANT_API_BASE_URL!,
  {
    headers: {
      Authorization: `Bearer ${process.env.MERCHANT_API_KEY}`,
    },
    routes: {
      createOrderPath: '/x402/create-order',
      orderStatusPath: '/x402/order-status/:paymentId',
      submitSignaturePath: '/x402/submit-signature',
      cancelOrderPath: '/x402/cancel-order',
    },
  }
);

// 2. Create the payer wallet adapter (signs EIP-712 data)
const signer = new Wallet(process.env.PRIVATE_KEY!);
const payer = new EvmPayerWalletAdapter(signer);

// 3. Register all x402 actions
const provider = new ActionProvider();
provider.register(createPaymentAction(merchant));
provider.register(submitSignatureAction(merchant, payer));
provider.register(transferPaymentAction(payer));
provider.register(paymentStatusAction(merchant));
provider.register(cancelPaymentAction(merchant));

Complete Payment Flow Example

This example is based on the repo's examples/x402-payment-flow/eip712-full-flow.ts:

src/x402-flow.ts
import { Wallet } from 'ethers';
import { EvmPayerWalletAdapter } from '@goatnetwork/agentkit/plugins';

// 1. Create a signer
const signer = Wallet.createRandom();
const payer = new EvmPayerWalletAdapter(signer);

// 2. Merchant creates a payment intent (returns EIP-712 data to sign)
const created = await merchant.createPaymentIntent({
  to: '0xabc...',
  asset: 'USDC',
  amount: '1000000',
});

// 3. Agent signs the EIP-712 calldata
const signature = await payer.signCalldataTypedData(
  created.calldataSignRequest!
);

// 4. Submit the signature to the merchant
const authorized = await merchant.submitPaymentAuthorization(
  created.paymentId,
  signature
);

// 5. Check status
const status = await merchant.getPaymentStatus(created.paymentId);
console.log({ paymentId: created.paymentId, authorized, status });

Using the Runtime

When executing x402 actions through the runtime, pass confirmed: true since payment actions require confirmation:

src/x402-runtime.ts
const context = {
  traceId: 'trace_pay_001',
  network: 'goat-testnet',
  now: Date.now(),
  caller: 'payment-agent',
};

// Create payment
const create = await runtime.run(
  provider.get('goat.x402.payment.create'),
  context,
  { to: '0xabc...', asset: 'USDC', amount: '10' },
  { confirmed: true, idempotencyKey: 'pay-001' }
);

if (!create.ok) {
  console.error('Payment failed:', create.error);
  return;
}

// Submit signature
const sign = await runtime.run(
  provider.get('goat.x402.payment.submitSignature'),
  context,
  {
    paymentId: create.output.paymentId,
    calldataSignRequest: create.output.calldataSignRequest,
  },
  { confirmed: true }
);

// Transfer tokens
const transfer = await runtime.run(
  provider.get('goat.x402.payment.transfer'),
  context,
  {
    tokenAddress: create.output.tokenAddress,
    to: create.output.payToAddress,
    amount: '10',
  },
  { confirmed: true }
);

// Check status
const status = await runtime.run(
  provider.get('goat.x402.payment.status'),
  context,
  { paymentId: create.output.paymentId }
);

MerchantGatewayAdapter Interface

If you need to integrate with a non-standard merchant API, implement the MerchantGatewayAdapter interface:

Interface
interface MerchantGatewayAdapter {
  createPaymentIntent(
    input: CreatePaymentIntentInput,
    signal?: AbortSignal
  ): Promise<CreatePaymentIntentResult>;

  getPaymentStatus(
    paymentId: string,
    signal?: AbortSignal
  ): Promise<{ paymentId: string; status: PaymentStatus }>;

  submitPaymentAuthorization(
    paymentId: string,
    signature: string,
    signal?: AbortSignal
  ): Promise<{ paymentId: string; status: 'authorized' | 'failed' }>;

  cancelPayment(
    paymentId: string,
    signal?: AbortSignal
  ): Promise<{ paymentId: string; status: 'cancelled' | 'failed' }>;
}

Payment Statuses

StatusDescription
createdPayment intent created, awaiting authorization
authorizedSignature submitted and verified
settledOn-chain transfer confirmed
failedPayment failed at any stage
expiredPayment intent timed out

Merchant Side: x402-merchant Plugin

The merchant plugin provides 30 actions for managing a full x402 merchant portal. It uses the HttpMerchantPortalClient HTTP adapter.

Setup

src/merchant-setup.ts
import { HttpMerchantPortalClient } from '@goatnetwork/agentkit/plugins';
import {
  merchantAuthLoginAction,
  merchantAuthRegisterAction,
  merchantDashboardStatsAction,
  merchantOrdersListAction,
  merchantBalanceGetAction,
  // ... (30 actions total)
} from '@goatnetwork/agentkit/plugins';

const merchantClient = new HttpMerchantPortalClient(
  process.env.MERCHANT_PORTAL_BASE_URL ?? 'http://localhost:8080'
);

const provider = new ActionProvider();
provider.register(merchantAuthLoginAction(merchantClient));
provider.register(merchantAuthRegisterAction(merchantClient));
provider.register(merchantDashboardStatsAction(merchantClient));
provider.register(merchantOrdersListAction(merchantClient));
provider.register(merchantBalanceGetAction(merchantClient));
// ... register additional actions as needed

Authentication Flow

Merchant portal actions use bearer token authentication. The auth actions (login, register) return an access_token. Pass it via ActionContext.accessToken in subsequent calls:

Authentication example
// 1. Login
const loginResult = await runtime.run(
  provider.get('x402_merchant.auth.login'),
  context,
  { email: 'merchant@example.com', password: 'secret' },
  { confirmed: true }
);

// 2. Use the token in subsequent calls
const authedContext = {
  ...context,
  accessToken: loginResult.output.access_token,
};

const stats = await runtime.run(
  provider.get('x402_merchant.dashboard.stats'),
  authedContext,
  {}
);

The accessToken field on ActionContext is never included in hook events or logs for security. Auth action outputs containing tokens are automatically marked with sensitiveOutputFields.

Session-Scoped Clients

For multi-tenant deployments, use withAccessToken() to create session-scoped client clones that avoid cross-session token leakage:

Multi-tenant usage
const sessionClient = merchantClient.withAccessToken(userToken);
// sessionClient has independent token state

Environment Variables

.env
# x402 Payer (Merchant Gateway)
MERCHANT_API_BASE_URL=https://merchant.example.com
MERCHANT_API_KEY=your-api-key

# Custom route paths (optional — defaults shown)
MERCHANT_CREATE_ORDER_PATH=/x402/create-order
MERCHANT_ORDER_STATUS_PATH=/x402/order-status/:paymentId
MERCHANT_SUBMIT_SIGNATURE_PATH=/x402/submit-signature
MERCHANT_CANCEL_ORDER_PATH=/x402/cancel-order

# x402 Merchant Portal
MERCHANT_PORTAL_BASE_URL=http://localhost:8080

# GoatAdapter x402 credentials (for direct GOAT x402 API)
GOAT_X402_BASE_URL=https://api.x402.goat.network
GOAT_X402_API_KEY=your-key
GOAT_X402_API_SECRET=your-secret

On this page