AgentKit
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
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
Agent Merchant Gateway Blockchain
│ │ │
│ 1. createPayment -------->│ │
│ <--- paymentId + EIP-712 │ │
│ │ │
│ 2. signTypedData -------->│ (local EIP-712 sign) │
│ 3. submitSignature ------>│ │
│ <--- authorized │ │
│ │ │
│ 4. transferToken ---------│----------------------->│
│ │ │
│ 5. status/cancel -------->│ │
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));
This example is based on the repo's examples/x402-payment-flow/eip712-full-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 });
When executing x402 actions through the runtime, pass confirmed: true since payment actions require confirmation:
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 }
);
If you need to integrate with a non-standard merchant API, implement the MerchantGatewayAdapter 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' }>;
}
Status Description createdPayment intent created, awaiting authorization authorizedSignature submitted and verified settledOn-chain transfer confirmed failedPayment failed at any stage expiredPayment intent timed out
The merchant plugin provides 30 actions for managing a full x402 merchant portal. It uses the HttpMerchantPortalClient HTTP adapter.
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
Merchant portal actions use bearer token authentication. The auth actions (login, register) return an access_token. Pass it via ActionContext.accessToken in subsequent calls:
// 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.
For multi-tenant deployments, use withAccessToken() to create session-scoped client clones that avoid cross-session token leakage:
const sessionClient = merchantClient. withAccessToken (userToken);
// sessionClient has independent token state
# 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