Skip to content

πŸ€–πŸ’΅ Agentic Payment Service for Open Agent Skills Ecosystem.

License

Notifications You must be signed in to change notification settings

sentient-agi/agent-payments-skill

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

25 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ€–πŸ’΅ Agentic Payment Service for Open Agent Skills Ecosystem.

A dual-protocol (x402 + AP2) agentic payment service for Open Agent Skills Ecosystem (including OpenClaw, Claude Code, Codex, Junie, OpenCode, GitHub Copilot, Gemini CLI, etc.), with web3 & web2 gateway support, AWS KMS key management, policy engine compliance, audit trail, and human-in-the-loop confirmation.

TypeScript OpenClaw Skill License: Apache 2.0


Table of Contents


Overview

agent-payments-skill is an Open Agent Skills Ecosystem compliant skill that enables AI agents to autonomously initiate, validate, and execute payments across both blockchain (web3) and traditional (web2) payment rails.

Key Capabilities

Capability Details
Dual protocol support x402 (HTTP 402 + onchain settlement) and AP2 (Google's mandate-based agent payments)
Web3 transactions Ethereum, Base, Polygon via Viem β€” native ETH and ERC-20 (USDC, etc.)
Web2 gateways Stripe, PayPal, Visa Direct, Mastercard Send
Key management AWS KMS encryption/decryption; encrypted at-rest storage in SQLite
Policy engine Per-tx limits, daily/weekly/monthly aggregates, time-of-day, blacklist/whitelist, currency restrictions
Human-in-the-loop Automatic escalation on policy violations via CLI prompt, chat prompt, or web API
Audit trail Every action logged to SQLite audit_log table + Winston (stdout/stderr/file)
Three interfaces OpenClaw (or other agent) chat, CLI (agent-payments), REST web API
Fully configurable Single YAML file controls all behavior

Architecture

System Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        Agent Payments Skill                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                  β”‚
β”‚  β”‚  Chat UI  β”‚   β”‚   CLI (term)  β”‚   β”‚ Web API  β”‚                  β”‚
β”‚  β””β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”˜                  β”‚
β”‚        β”‚                 β”‚                β”‚                        β”‚
β”‚        β–Ό                 β–Ό                β–Ό                        β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”               β”‚
β”‚  β”‚              Protocol Router                    β”‚               β”‚
β”‚  β”‚  (AI output parser β†’ PaymentIntent β†’ routing)   β”‚               β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜               β”‚
β”‚              β”‚                 β”‚                                   β”‚
β”‚     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”                           β”‚
β”‚     β”‚  x402 Client  β”‚  β”‚  AP2 Client   β”‚                           β”‚
β”‚     β”‚  (HTTP 402)   β”‚  β”‚  (Mandates)   β”‚                           β”‚
β”‚     β””β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”˜                           β”‚
β”‚              β”‚                β”‚                                    β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”                    β”‚
β”‚  β”‚            Policy Engine                   β”‚                    β”‚
β”‚  β”‚  (compliance checks before execution)      β”‚                    β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚                    β”‚
β”‚  β”‚  β”‚ β€’ Single tx limit    β€’ Blacklist     β”‚  β”‚                    β”‚
β”‚  β”‚  β”‚ β€’ Daily/Weekly/Mo    β€’ Whitelist     β”‚  β”‚                    β”‚
β”‚  β”‚  β”‚ β€’ Time-of-day        β€’ Currency      β”‚  β”‚                    β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚                    β”‚
β”‚  β”‚       β”‚ (violation?) ──► Human Confirm     β”‚                    β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜                    β”‚
β”‚          β”‚                                                         β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚  β”‚              Payment Execution                   β”‚              β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”  β”‚              β”‚
β”‚  β”‚  β”‚  Viem    β”‚  β”‚ Stripe β”‚  β”‚ PayPal β”‚  β”‚ Visa β”‚  β”‚   β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β” β”‚
β”‚  β”‚  β”‚ (ETH/    β”‚  β”‚        β”‚  β”‚        β”‚  β”‚ MC   β”‚  β”‚   β”‚AWS KMS β”‚ β”‚
β”‚  β”‚  β”‚  ERC20)  β”‚  β”‚        β”‚  β”‚        β”‚  β”‚      β”‚  β”‚   β”‚(decryptβ”‚ β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”˜  │◄──│ keys)  β”‚ β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜   β””β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β”‚
β”‚                     β”‚                                              β”‚
β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”              β”‚
β”‚  β”‚                  SQLite                          β”‚              β”‚
β”‚  β”‚  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”  β”‚              β”‚
β”‚  β”‚  β”‚encrypted_keysβ”‚  β”‚ transac- β”‚  β”‚ audit_log  β”‚  β”‚              β”‚
β”‚  β”‚  β”‚              β”‚  β”‚ tions    β”‚  β”‚            β”‚  β”‚              β”‚
β”‚  β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜  β”‚              β”‚
β”‚  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜              β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Directory Structure

agent-payments-skill/
β”œβ”€β”€ SKILL.md                          # Open Agent Skills Ecosystem compliant skill definition (YAML frontmatter + markdown)
β”œβ”€β”€ package.json                      # npm package manifest
β”œβ”€β”€ tsconfig.json                     # TypeScript compiler config
β”œβ”€β”€ config/
β”‚   └── default.yaml                  # Master YAML configuration
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ index.ts                      # Main entry point / orchestrator
β”‚   β”œβ”€β”€ cli.ts                        # CLI interface (Commander.js)
β”‚   β”œβ”€β”€ web-api.ts                    # REST API (Express)
β”‚   β”œβ”€β”€ config/
β”‚   β”‚   └── loader.ts                 # YAML config loader + Zod validation
β”‚   β”œβ”€β”€ protocols/
β”‚   β”‚   β”œβ”€β”€ router.ts                 # Protocol router + AI output parser
β”‚   β”‚   β”œβ”€β”€ x402/
β”‚   β”‚   β”‚   └── client.ts             # x402 HTTP 402 client
β”‚   β”‚   └── ap2/
β”‚   β”‚       └── client.ts             # AP2 mandate-based client
β”‚   β”œβ”€β”€ payments/
β”‚   β”‚   β”œβ”€β”€ web3/
β”‚   β”‚   β”‚   └── ethereum.ts           # Viem-based ETH/ERC-20 tx producer
β”‚   β”‚   └── web2/
β”‚   β”‚       └── gateways.ts           # Stripe, PayPal, Visa, Mastercard
β”‚   β”œβ”€β”€ kms/
β”‚   β”‚   └── aws-kms.ts               # AWS KMS encrypt/decrypt
β”‚   β”œβ”€β”€ db/
β”‚   β”‚   β”œβ”€β”€ sqlite.ts                 # SQLite init + migrations
β”‚   β”‚   β”œβ”€β”€ key-store.ts              # Encrypted key CRUD
β”‚   β”‚   β”œβ”€β”€ transactions.ts           # Transaction records + aggregates
β”‚   β”‚   └── audit.ts                  # Audit trail read/write
β”‚   β”œβ”€β”€ policy/
β”‚   β”‚   β”œβ”€β”€ engine.ts                 # Policy rule evaluator
β”‚   β”‚   └── feedback.ts               # Human confirmation (CLI/chat/API)
β”‚   └── logging/
β”‚       └── logger.ts                 # Winston multi-transport logger
β”œβ”€β”€ data/                             # (created at runtime)
β”‚   └── payments.db                   # SQLite database
└── logs/                             # (created at runtime)
    └── payment-skill.log             # File log output

Data Flow

User/Agent input
       β”‚
       β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”     β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Parse AI Output  │────►│ Validate JSON Schema β”‚
β”‚ (regex + JSON)   β”‚     β”‚ (Zod PaymentIntent)  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                                    β–Ό
                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β”‚   Protocol Router    β”‚
                         β”‚ (x402 or AP2, web3   β”‚
                         β”‚  or web2 detection)  β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                                    β–Ό
                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β”‚ Create Transaction   β”‚
                         β”‚ Record (SQLite)      β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                                    β–Ό
                         β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                         β”‚   Policy Engine       │◄── rules from YAML
                         β”‚ β€’ limits check        │◄── aggregates from SQLite
                         β”‚ β€’ blacklist/whitelist β”‚
                         β”‚ β€’ time restrictions   β”‚
                         β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                                    β”‚
                            β”Œβ”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”
                            β”‚  violations?   β”‚
                            β””β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”˜
                           yes  β”‚        β”‚ no
                                β–Ό        β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”    β”‚
                    β”‚ Human Confirm β”‚    β”‚
                    β”‚ (CLI/Chat/API)β”‚    β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”˜    β”‚
                     reject β”‚ confirm    β”‚
                       β–Ό    β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”˜
                    REJECT  β”‚    β”‚
                            β–Ό    β–Ό
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  Decrypt Keys (KMS)  β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  Execute Payment    β”‚
                    β”‚  (Viem / Stripe /   β”‚
                    β”‚   PayPal / Visa /   β”‚
                    β”‚   Mastercard)       β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
                               β”‚
                    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β–Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
                    β”‚  Update Transaction β”‚
                    β”‚  + Audit Log        β”‚
                    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Supported Protocols

x402 Protocol

x402 is an open payment protocol built by Coinbase that revives the HTTP 402 Payment Required status code for internet-native stablecoin payments. It is stateless, HTTP-native, and developer-friendly.

How the skill uses x402:

  1. Discovery β€” The client sends a GET request to a resource URL. If 402 is returned, the response body (or headers) contains payment details: scheme, network, amount, payTo, asset, and maxTimeoutSeconds.
  2. Payment β€” The skill signs an EIP-3009 transferWithAuthorization using the wallet's private key (decrypted from KMS) via Viem. The signed payload is Base64-encoded and sent as the X-PAYMENT header on a retried GET request.
  3. Settlement β€” The resource server (or its facilitator) verifies and settles the payment onchain. A 200 OK response is returned with the resource and an X-PAYMENT-RESPONSE header containing the settlement receipt (including txHash).

Supported networks: Ethereum Mainnet, Base, Polygon (configurable).

Supported assets: USDC (default), any ERC-20 with known contract addresses.

Reference: x402 GitHub Β· x402 Docs Β· ERC-8004 Spec

AP2 Protocol (Agent Payments Protocol)

AP2 is Google's open protocol for AI agent-driven payments. It uses cryptographically signed Mandates β€” verifiable credentials that capture user intent and constraints β€” to enable agents to transact on behalf of humans.

AP2 Mandate Types:

Mandate Purpose
IntentMandate Captures the user's initial intent (e.g., "buy running shoes under $100") with a max spend ceiling. Signed by the user.
CartMandate Locks a specific cart of items and price. Created after the agent finds products.
PaymentMandate Authorizes actual payment execution. Contains payment method reference and final amount.

How the skill uses AP2:

  1. Create Mandate β€” From a PaymentIntent, the skill constructs an AP2 mandate with intent details, amount constraints, validity window, and delegator info.
  2. Sign Mandate β€” The mandate is sent to a credential provider for user signature (ECDSA-based verifiable credential).
  3. Obtain Credentials β€” Payment credentials are retrieved using the signed mandate and the desired payment method type.
  4. Submit Payment β€” The signed mandate + credentials are sent to the merchant's payment processor for execution.

Supported payment methods: Card (Visa/MC via gateway), PayPal, Stripe, Crypto (transparently routed through the appropriate web2/web3 backend).

Reference: AP2 Specification Β· Google Announcement

Protocol Router

The protocol router (src/protocols/router.ts) is the entry point for all payment requests. It:

  1. Parses AI output β€” Extracts JSON PaymentIntent from free-form AI text using multiple strategies:
    • Fenced JSON code blocks (```json ... ```)
    • Generic code blocks (``` ... ```)
    • Raw JSON containing a "protocol" field
    • The entire text as JSON (for direct API input)
  2. Validates with Zod schema enforcement
  3. Routes to the correct protocol + payment backend based on:
    • Explicit gateway field (if provided)
    • Currency-based heuristic (crypto currencies β†’ web3/viem, fiat β†’ web2/stripe)
    • Protocol hint (x402 β†’ web3, AP2 β†’ either)

Payment Backends

Web3 β€” Ethereum (Viem)

The Viem-based transaction producer (src/payments/web3/ethereum.ts) supports:

Operation Function Details
Send ETH sendEth() Native ETH transfer on any supported EVM chain
Send ERC-20 sendErc20() Token transfer (USDC, USDT, DAI, etc.) using transfer() ABI
Wait for confirmation waitForConfirmation() Polls for on-chain receipt

Supported chains (configurable via YAML):

Chain Chain ID Default RPC
Ethereum Mainnet 1 https://mainnet.infura.io/v3/...
Base 8453 https://mainnet.base.org
Polygon 137 https://polygon-rpc.com

Well-known USDC addresses are built-in per chain. Custom token addresses can be passed directly.

Web2 β€” Stripe

Uses the official Stripe Node.js SDK.

  • Creates a PaymentIntent via stripe.paymentIntents.create()
  • Amount converted to cents (integer)
  • Metadata includes protocol, recipient, and any custom fields
  • API key decrypted from AWS KMS at runtime

Web2 β€” PayPal

Uses PayPal's REST Checkout API v2.

  • OAuth2 client credentials flow for access token
  • Creates an Order via POST /v2/checkout/orders
  • Returns approval URL for user authorization
  • Client ID and secret decrypted from AWS KMS

Web2 β€” Visa Direct

Uses Visa Direct Push Funds Transfer API.

  • POST /visadirect/fundstransfer/v1/pushfundstransactions
  • Basic auth (user/password from KMS)
  • Supports person-to-merchant and person-to-person transfers

Web2 β€” Mastercard Send

Uses Mastercard Send Transfer API.

  • POST /send/v1/partners/transfers/payment
  • OAuth 1.0a authentication (consumer key + signing key from KMS)
  • Supports credit and debit funding sources

Security

AWS KMS Integration

All sensitive credentials (wallet private keys, API tokens, passwords) are encrypted using AWS KMS before storage and decrypted only at the moment of use.

Important: AWS access credentials are only loaded from environment variables. They are never stored in configuration files or the database.

Operation Description
encryptAndStore() Encrypts plaintext via KMS.Encrypt, stores ciphertext blob in SQLite
retrieveAndDecrypt() Reads ciphertext from SQLite, decrypts via KMS.Decrypt, returns plaintext

Plaintext values are never logged or persisted. The audit log records the key alias and type, but never the decrypted value.

Encrypted Key Storage (SQLite)

The encrypted_keys table stores AWS KMS-encrypted blobs:

Column Type Description
id TEXT PK UUID
key_type TEXT web3_private_key, stripe_token, paypal_token, visa_token, mastercard_token
key_alias TEXT UNIQUE Human-readable name (e.g., default_wallet, stripe_api_key)
ciphertext BLOB AWS KMS encrypted payload
kms_key_id TEXT KMS key ARN used for encryption
created_at TEXT ISO 8601 timestamp
updated_at TEXT ISO 8601 timestamp

Environment Variables

Variable Required Description
AWS_ACCESS_KEY_ID βœ… AWS IAM access key for KMS
AWS_SECRET_ACCESS_KEY βœ… AWS IAM secret key for KMS
AWS_SESSION_TOKEN ❌ Optional, for temporary credentials / STS
AWS_KMS_KEY_ID βœ… KMS key ARN or alias (e.g., alias/agent-payments)
AWS_REGION ❌ Overrides kms.region in config (fallback: config value)
CONFIG_PATH ❌ Override default config file path (for web API)

⚠️ Never commit these values to source control. Use a secrets manager, .env file with appropriate .gitignore, or container environment injection.


Policy Engine

The policy engine (src/policy/engine.ts) acts as a compliance interceptor. It evaluates every payment intent against a configurable rule set before any real transaction is executed.

Rule Types

Rule Config Key Description
Single transaction limit policy.rules.single_transaction.max_amount_usd Maximum USD equivalent for any one payment
Daily aggregate limit policy.rules.daily.max_total_usd Max total USD in a rolling 24-hour window
Daily transaction count policy.rules.daily.max_transaction_count Max number of transactions in 24 hours
Weekly aggregate limit policy.rules.weekly.max_total_usd Max total USD in a rolling 7-day window
Weekly transaction count policy.rules.weekly.max_transaction_count Max transactions in 7 days
Monthly aggregate limit policy.rules.monthly.max_total_usd Max total USD in a rolling 30-day window
Monthly transaction count policy.rules.monthly.max_transaction_count Max transactions in 30 days
Time-of-day restrictions policy.rules.time_restrictions Restrict payments to specific UTC hours and days of week
Blacklist policy.rules.blacklist Block payments to specific addresses/merchant IDs
Whitelist policy.rules.whitelist Only allow payments to specific addresses (when enabled)
Currency restrictions policy.rules.allowed_currencies Only allow payments in listed currencies

Time-Based Aggregate Tracking

Aggregate limits are computed against the transactions table in SQLite using rolling windows:

Daily window:   NOW - 24 hours  β†’  NOW
Weekly window:  NOW - 7 days    β†’  NOW
Monthly window: NOW - 30 days   β†’  NOW

The engine queries:

SELECT COALESCE(SUM(amount_usd), 0) as total_usd, COUNT(*) as count
FROM transactions
WHERE status IN ('executed', 'approved', 'pending', 'awaiting_confirmation')
  AND created_at >= ? AND created_at < ?

This ensures that even pending/awaiting-confirmation transactions count toward limits, preventing circumvention by rapid-fire requests.

Human Confirmation Feedback Loop

When a policy violation with severity: "block" is detected and require_human_confirmation_on_violation is true, the system pauses execution and requests human confirmation:

Channel Behavior
CLI Interactive terminal prompt with violation details. User types yes or no.
Chat Returns a Markdown-formatted confirmation prompt. User replies confirm <txId> or reject <txId>.
Web API Returns HTTP 202 with the tx.id. Client must POST /api/v1/confirm/:txId with {"confirmed": true}.

Confirmation details logged include:

  • Transaction ID
  • All violated rules
  • Who confirmed/rejected (CLI, chat, web_api)
  • Optional rejection reason
  • Timestamp

Audit Trail & Logging

The skill implements a dual-write audit strategy: structured records in SQLite and multi-target log output via Winston.

SQLite Audit Log

Every significant action writes to the audit_log table:

Column Type Description
id INTEGER PK Auto-incrementing
timestamp TEXT ISO 8601 UTC
level TEXT info Β· warn Β· error Β· critical
category TEXT payment Β· policy Β· kms Β· protocol Β· auth Β· system
action TEXT Specific action identifier (see table below)
tx_id TEXT Related transaction ID (nullable)
actor TEXT agent Β· human Β· system Β· cli Β· web_api
details TEXT JSON payload with full context
ip_address TEXT Requesting IP (web API only)
user_agent TEXT HTTP User-Agent (web API only)

Tracked actions:

Category Action Trigger
system skill_bootstrapped On startup
protocol intent_routed After protocol router decision
protocol x402_payment_submitted After x402 payment sent
protocol ap2_mandate_signed After AP2 mandate signature
protocol ap2_payment_submitted After AP2 payment execution
payment eth_transfer_sent After Viem ETH tx broadcast
payment erc20_transfer_sent After Viem ERC-20 tx broadcast
payment web3_payment_confirmed After on-chain confirmation
payment stripe_intent_created After Stripe PaymentIntent
payment paypal_order_created After PayPal Order creation
payment visa_payment_submitted After Visa Direct push
payment mastercard_payment_submitted After MC Send transfer
payment payment_rejected_by_human On human rejection
payment payment_execution_failed On any execution error
policy violations_detected When rules are violated
policy human_confirmed Human approved despite violation
policy human_rejected Human rejected
kms key_stored New key encrypted and stored
kms key_encrypted_and_stored Via encryptAndStore()
kms key_decrypted Key decrypted for use
kms key_deleted Key removed from store

Winston Logger (stdout/stderr/file)

Winston is configured with multiple transports (all configurable):

Transport Config Key Default
Console (stdout) logging.stdout true
Console (stderr for errors) logging.stderr_errors true
File logging.file.enabled true
File path logging.file.path ./logs/payment-skill.log
Max file size logging.file.max_size_mb 50 MB
Max file count (rotation) logging.file.max_files 10

Audit log entries are simultaneously written to both SQLite and Winston, ensuring coverage even if one subsystem fails. Audit writes use try/catch internally and never crash the payment flow.


Database Schema

Three tables are created during initialization (src/db/sqlite.ts):

encrypted_keys

CREATE TABLE encrypted_keys (
    id          TEXT PRIMARY KEY,
    key_type    TEXT NOT NULL,
    key_alias   TEXT NOT NULL UNIQUE,
    ciphertext  BLOB NOT NULL,
    kms_key_id  TEXT NOT NULL,
    created_at  TEXT NOT NULL DEFAULT (datetime('now')),
    updated_at  TEXT NOT NULL DEFAULT (datetime('now'))
);

transactions

CREATE TABLE transactions (
    id                TEXT PRIMARY KEY,
    protocol          TEXT NOT NULL,        -- 'x402' | 'ap2'
    gateway           TEXT,                 -- 'viem' | 'stripe' | 'paypal' | 'visa' | 'mastercard'
    action            TEXT NOT NULL,
    amount            REAL NOT NULL,
    amount_usd        REAL NOT NULL,
    currency          TEXT NOT NULL,
    recipient         TEXT NOT NULL,
    network           TEXT,
    status            TEXT NOT NULL,
    tx_hash           TEXT,
    error_message     TEXT,
    policy_violations TEXT,                 -- JSON array
    confirmed_by      TEXT,                 -- 'auto' | 'human'
    metadata          TEXT,                 -- JSON
    created_at        TEXT NOT NULL DEFAULT (datetime('now')),
    executed_at       TEXT,
    completed_at      TEXT
);
-- Indexes: idx_transactions_created, idx_transactions_status, idx_transactions_recipient

Transaction statuses:

pending β†’ policy_check β†’ awaiting_confirmation β†’ approved β†’ executed
                       β†˜ rejected                       β†˜ failed

audit_log

CREATE TABLE audit_log (
    id          INTEGER PRIMARY KEY AUTOINCREMENT,
    timestamp   TEXT NOT NULL DEFAULT (datetime('now')),
    level       TEXT NOT NULL,
    category    TEXT NOT NULL,
    action      TEXT NOT NULL,
    tx_id       TEXT,
    actor       TEXT,
    details     TEXT,             -- JSON
    ip_address  TEXT,
    user_agent  TEXT
);
-- Indexes: idx_audit_timestamp, idx_audit_tx_id, idx_audit_category

Installation

Prerequisites

  • Node.js β‰₯ 18.x (for native fetch)
  • npm β‰₯ 9.x (or pnpm/yarn)
  • AWS Account with KMS key configured
  • SQLite (bundled via better-sqlite3, no system dependency needed)

Steps

# 1. Clone / copy the skill directory
git clone https://github.com/sentient-agi/agent-payments-skill.git
cd agent-payments-skill

# 2. Install dependencies
npm install

# 3. Build TypeScript
npm run build

# 4. Set required environment variables
export AWS_ACCESS_KEY_ID="your-aws-access-key"
export AWS_SECRET_ACCESS_KEY="your-aws-secret-key"
export AWS_KMS_KEY_ID="arn:aws:kms:us-east-1:123456789012:key/your-key-id"

# 5. Customize configuration
cp config/default.yaml config/production.yaml
# Edit config/production.yaml with your RPC URLs, gateway settings, policy rules

# 6. Store encrypted keys (first-time setup)
npx agent-payments keys store --alias default_wallet --type web3_private_key --value "0xYOUR_PRIVATE_KEY"
npx agent-payments keys store --alias stripe_api_key --type stripe_token --value "sk_live_YOUR_STRIPE_KEY"
npx agent-payments keys store --alias paypal_client_id --type paypal_token --value "YOUR_PAYPAL_CLIENT_ID"
npx agent-payments keys store --alias paypal_secret --type paypal_token --value "YOUR_PAYPAL_SECRET"

# 7. Register open agents skill
npx skills add ./agent-payments-skill

OpenClaw Activation

Once installed, activate in your OpenClaw configuration:

{
  "skills": {
    "allow": ["agent-payments"]
  }
}

The agent will now be able to use the payment skill when it detects payment-related prompts.


Configuration Reference (YAML)

Full Annotated Configuration

# ═══════════════════════════════════════════════════════════════════════
# Agent Payments Skill β€” Master Configuration
# File: config/default.yaml
# ═══════════════════════════════════════════════════════════════════════

# ── Skill Metadata ───────────────────────────────────────────────────
skill:
  name: agent-payments            # Skill identifier (matches SKILL.md)
  version: 0.2.0                   # Skill version

# ── SQLite Database ──────────────────────────────────────────────────
database:
  path: "./data/payments.db"       # Path to SQLite database file
                                   # (directory created automatically)
  wal_mode: true                   # Enable WAL journal mode (recommended
                                   # for concurrent reads during writes)
  busy_timeout_ms: 5000            # SQLite busy timeout in milliseconds

# ── Protocols ────────────────────────────────────────────────────────
protocols:
  x402:
    enabled: true                  # Enable/disable x402 protocol
    facilitator_url: "https://x402.org/facilitator"
                                   # Facilitator URL for settlement
                                   # verification. Coinbase default:
                                   # https://x402.org/facilitator
    default_network: "base"        # Default chain if not specified in intent
    default_asset: "USDC"          # Default token if not specified
    timeout_ms: 30000              # HTTP request timeout for x402 ops

  ap2:
    enabled: true                  # Enable/disable AP2 protocol
    mandate_issuer: "https://your-ap2-issuer.example.com"
                                   # URL of your AP2 mandate issuer /
                                   # merchant payment processor
    credential_provider_url: "https://credentials.example.com"
                                   # AP2 credential provider for mandate
                                   # signing and payment credential retrieval
    timeout_ms: 30000              # HTTP request timeout

# ── Web3 Networks ────────────────────────────────────────────────────
# Each key is a network name used in PaymentIntent.network
web3:
  ethereum:
    enabled: true
    rpc_url: "https://mainnet.infura.io/v3/YOUR_INFURA_PROJECT_ID"
    chain_id: 1
  base:
    enabled: true
    rpc_url: "https://mainnet.base.org"
    chain_id: 8453
  polygon:
    enabled: true
    rpc_url: "https://polygon-rpc.com"
    chain_id: 137
  # Add more EVM-compatible chains as needed:
  # arbitrum:
  #   enabled: true
  #   rpc_url: "https://arb1.arbitrum.io/rpc"
  #   chain_id: 42161

# ── Web2 Payment Gateways ───────────────────────────────────────────
web2:
  stripe:
    enabled: true
    api_version: "2025-01-27.acacia" # Stripe API version string

  paypal:
    enabled: true
    environment: "sandbox"           # "sandbox" or "live"
    base_url: "https://api-m.sandbox.paypal.com"
                                     # Live: https://api-m.paypal.com

  visa:
    enabled: true
    base_url: "https://sandbox.api.visa.com"
                                     # Live: https://api.visa.com

  mastercard:
    enabled: true
    base_url: "https://sandbox.api.mastercard.com"
                                     # Live: https://api.mastercard.com

# ── AWS KMS ──────────────────────────────────────────────────────────
# NOTE: AWS credentials (AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY)
# must be set as environment variables. They are NEVER read from this file.
kms:
  enabled: true                    # Enable AWS KMS integration
  region: "us-east-1"             # AWS region for KMS API calls
  key_id_env: "AWS_KMS_KEY_ID"    # Name of env var holding the KMS key ARN

# ── Policy Engine ────────────────────────────────────────────────────
policy:
  enabled: true                    # Master switch for policy enforcement

  rules:
    # Per-transaction limits
    single_transaction:
      max_amount_usd: 1000.00      # Max USD per single payment

    # Rolling daily window (24 hours)
    daily:
      max_total_usd: 5000.00       # Max aggregate USD per day
      max_transaction_count: 50     # Max number of transactions per day

    # Rolling weekly window (7 days)
    weekly:
      max_total_usd: 25000.00
      max_transaction_count: 200

    # Rolling monthly window (30 days)
    monthly:
      max_total_usd: 80000.00
      max_transaction_count: 500

    # Time-of-day restrictions (evaluated in UTC)
    time_restrictions:
      enabled: false               # Set to true to enforce
      allowed_hours:
        start: 8                   # 08:00 UTC (inclusive)
        end: 22                    # 22:00 UTC (exclusive)
      allowed_days: [1, 2, 3, 4, 5]
                                   # JS weekday: 0=Sun, 1=Mon, ... 6=Sat
                                   # Default: Mon–Fri only

    # Blacklist β€” block payments to these addresses/IDs
    blacklist:
      enabled: true
      addresses:
        - "0x0000000000000000000000000000000000000000"
        # Add more:
        # - "0xDEADBEEF..."

    # Whitelist β€” if enabled, ONLY these addresses are allowed
    whitelist:
      enabled: false               # Usually disabled (restrictive)
      addresses: []
        # - "0xALLOWED_ADDRESS_1"
        # - "merchant-id-1"

    # Allowed currencies
    allowed_currencies:
      - "USDC"
      - "ETH"
      - "USD"
      - "EUR"
      # - "DAI"
      # - "USDT"

  # When violations occur, require human confirmation before proceeding
  require_human_confirmation_on_violation: true

# ── Logging & Audit ─────────────────────────────────────────────────
logging:
  level: "info"                    # Minimum log level: debug|info|warn|error

  stdout: true                     # Log to stdout (console)
  stderr_errors: true              # Route error/warn to stderr

  file:
    enabled: true                  # Log to file
    path: "./logs/payment-skill.log"
    max_size_mb: 50                # Max single file size before rotation
    max_files: 10                  # Keep N rotated files

  audit:
    sqlite: true                   # Write audit records to SQLite audit_log
    verbose: true                  # Include full request/response payloads
                                   # in audit details JSON

# ── Web API Server ──────────────────────────────────────────────────
web_api:
  enabled: true
  host: "0.0.0.0"                 # Bind address
  port: 3402                       # Listen port (3402 = "x402" πŸ˜‰)
  cors_origins:
    - "http://localhost:*"         # Allowed CORS origins (wildcard supported)
    # - "https://your-app.example.com"

# ── CLI Behavior ────────────────────────────────────────────────────
cli:
  interactive_confirmation: true   # Prompt for confirmation on violations
  colored_output: true             # ANSI color codes in terminal output

Configuration Sections

Section Purpose Key Settings
skill Metadata name, version β€” must match SKILL.md
database SQLite path, wal_mode, busy_timeout_ms
protocols.x402 x402 client facilitator_url, default_network, default_asset
protocols.ap2 AP2 client mandate_issuer, credential_provider_url
web3.<network> EVM chains rpc_url, chain_id, enabled
web2.<gateway> Payment APIs Gateway-specific URLs, API versions
kms AWS KMS region, key_id_env (credentials from env vars only)
policy Compliance All rule definitions + human confirmation toggle
logging Observability Multi-transport config, audit detail level
web_api REST server host, port, cors_origins
cli Terminal UX Confirmation mode, color output

CLI Reference

The CLI is available as agent-payments (via npm bin) or npx agent-payments.

Global Options

Flag Description Default
-c, --config <path> Path to YAML config file config/default.yaml
-V, --version Print version β€”
-h, --help Show help β€”

pay β€” Execute a Payment

agent-payments pay [options]
Option Required Description
--protocol <x402|ap2> βœ… Protocol to use
--amount <string> βœ… Decimal amount (e.g., "10.50")
--currency <string> βœ… Currency code (USDC, ETH, USD, EUR)
--to <string> βœ… Recipient address or merchant ID
--network <string> ❌ Blockchain network (ethereum, base, polygon, web2)
--gateway <string> ❌ Payment gateway (viem, stripe, paypal, visa, mastercard)
--description <string> ❌ Human-readable description
--wallet <string> ❌ Wallet key alias in encrypted store (default: default_wallet)

Examples:

# x402 USDC payment on Base
agent-payments pay \
  --protocol x402 \
  --amount 5.00 \
  --currency USDC \
  --to 0x742d35Cc6635C0532925a3b844Bc9e7595f2bD65 \
  --network base

# AP2 Stripe payment
agent-payments pay \
  --protocol ap2 \
  --amount 49.99 \
  --currency USD \
  --to merchant-12345 \
  --gateway stripe \
  --description "Monthly subscription"

# PayPal payment with custom config
agent-payments pay \
  --config config/production.yaml \
  --protocol ap2 \
  --amount 25.00 \
  --currency USD \
  --to seller@example.com \
  --gateway paypal

parse β€” Parse AI Output

Extract a PaymentIntent JSON from free-form AI text.

# Direct text
agent-payments parse '{"protocol":"x402","action":"pay","amount":"10","currency":"USDC","recipient":"0x..."}'

# From stdin (pipe AI model output)
echo '... AI response with embedded JSON ...' | agent-payments parse -

keys β€” Key Management

Manage encrypted keys/tokens stored in SQLite via AWS KMS.

# Store a new encrypted key
agent-payments keys store \
  --alias default_wallet \
  --type web3_private_key \
  --value "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

# List all stored keys (metadata only β€” no plaintext)
agent-payments keys list

# Delete a key
agent-payments keys delete stripe_api_key

Key types:

Type Alias Convention Used By
web3_private_key default_wallet Viem transaction signing
stripe_token stripe_api_key Stripe SDK initialization
paypal_token paypal_client_id, paypal_secret PayPal OAuth2
visa_token visa_user_id, visa_password Visa Direct auth
mastercard_token mastercard_consumer_key, mastercard_signing_key MC Send auth

tx β€” Transaction Lookup

agent-payments tx <transaction-id>

Outputs the full transaction record as JSON, including status, policy violations, tx hash, and timestamps.

audit β€” Query Audit Log

agent-payments audit [options]
Option Description
--category <string> Filter: payment, policy, kms, protocol, system
--tx <string> Filter by transaction ID
--since <ISO 8601> Filter by timestamp (e.g., 2026-02-10T00:00:00Z)
--limit <number> Max results (default: 50)

Examples:

# All payment audit entries from today
agent-payments audit --category payment --since 2026-02-10T00:00:00Z

# Audit trail for a specific transaction
agent-payments audit --tx "a1b2c3d4-e5f6-7890-abcd-ef1234567890"

# Recent policy violations
agent-payments audit --category policy --limit 20

Web API Reference

Base URL

http://<host>:<port>/api/v1

Default: http://0.0.0.0:3402/api/v1

Start the server:

# Via npm script
npm run web

# Or directly
node dist/web-api.js

# With custom config
CONFIG_PATH=config/production.yaml node dist/web-api.js

Endpoints


GET /api/v1/health

Health check endpoint.

Response 200:

{
  "status": "ok",
  "skill": "agent-payments",
  "version": "0.2.0"
}

POST /api/v1/payment

Execute a payment from a PaymentIntent.

Request body:

{
  "protocol": "x402",
  "action": "pay",
  "amount": "10.00",
  "currency": "USDC",
  "recipient": "0x742d35Cc6635C0532925a3b844Bc9e7595f2bD65",
  "network": "base",
  "gateway": "viem",
  "description": "API access payment",
  "metadata": {},
  "walletKeyAlias": "default_wallet"
}
Field Type Required Description
protocol "x402" | "ap2" βœ… Payment protocol
action "pay" βœ… Action (only "pay" supported)
amount string βœ… Decimal amount
currency string βœ… Currency code
recipient string βœ… Destination address or merchant ID
network string ❌ Blockchain network name
gateway string ❌ Explicit gateway selection
description string ❌ Human-readable description
metadata object ❌ Arbitrary metadata
walletKeyAlias string ❌ Key alias (default: "default_wallet")

Response 200 (success):

{
  "success": true,
  "tx": {
    "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "protocol": "x402",
    "gateway": "viem",
    "amount": 10.0,
    "amount_usd": 10.0,
    "currency": "USDC",
    "recipient": "0x742d35Cc6635C0532925a3b844Bc9e7595f2bD65",
    "network": "base",
    "status": "executed",
    "created_at": "2026-02-10T12:00:00.000Z"
  },
  "txHash": "0xabc123...",
  "policyResult": {
    "allowed": true,
    "violations": [],
    "requiresHumanConfirmation": false
  },
  "confirmationRequired": false
}

Response 202 (confirmation required):

{
  "success": false,
  "tx": { "id": "...", "status": "awaiting_confirmation", "..." : "..." },
  "policyResult": {
    "allowed": true,
    "violations": [
      {
        "rule": "single_transaction.max_amount_usd",
        "message": "Amount $1500.00 exceeds single transaction limit of $1000.00",
        "severity": "block"
      }
    ],
    "requiresHumanConfirmation": true
  },
  "confirmationRequired": true,
  "confirmationPrompt": "Confirmation required for tx a1b2c3d4... POST /api/v1/confirm/a1b2c3d4..."
}

Response 400 (validation/execution error):

{
  "error": "Amount must be a decimal string"
}

POST /api/v1/parse

Parse a PaymentIntent from free-form AI output text.

Request body:

{
  "text": "I've processed the request. Here's the payment:\n```json\n{\"protocol\":\"x402\",\"action\":\"pay\",\"amount\":\"5.00\",\"currency\":\"USDC\",\"recipient\":\"0x...\"}\n```"
}

Response 200:

{
  "found": true,
  "intent": {
    "protocol": "x402",
    "action": "pay",
    "amount": "5.00",
    "currency": "USDC",
    "recipient": "0x..."
  }
}

Response 200 (no intent found):

{
  "found": false,
  "intent": null
}

POST /api/v1/confirm/:txId

Confirm or reject a pending transaction that requires human approval.

URL parameters:

  • txId β€” Transaction ID from the 202 response

Request body:

{
  "confirmed": true,
  "reason": "Approved by finance team"
}
Field Type Required Description
confirmed boolean βœ… true to approve, false to reject
reason string ❌ Optional note (especially useful for rejections)

Response 200:

{
  "success": true,
  "message": "Transaction a1b2c3d4... confirmed"
}

Response 404:

{
  "error": "No pending confirmation for tx a1b2c3d4..."
}

GET /api/v1/pending

List all transactions awaiting human confirmation.

Response 200:

[
  {
    "txId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "amount": 1500.0,
    "currency": "USD",
    "recipient": "merchant-12345",
    "violations": [
      {
        "rule": "single_transaction.max_amount_usd",
        "message": "Amount $1500.00 exceeds single transaction limit of $1000.00",
        "severity": "block"
      }
    ]
  }
]

GET /api/v1/transactions/:txId

Retrieve a transaction record by ID.

Response 200:

{
  "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "protocol": "x402",
  "gateway": "viem",
  "action": "pay",
  "amount": 10.0,
  "amount_usd": 10.0,
  "currency": "USDC",
  "recipient": "0x742d35Cc6635C0532925a3b844Bc9e7595f2bD65",
  "network": "base",
  "status": "executed",
  "tx_hash": "0xabc123def456...",
  "error_message": null,
  "policy_violations": null,
  "confirmed_by": "auto",
  "metadata": "{}",
  "created_at": "2026-02-10T12:00:00",
  "executed_at": "2026-02-10T12:00:05",
  "completed_at": null
}

Response 404:

{
  "error": "Transaction not found"
}

GET /api/v1/audit

Query the audit log with optional filters.

Query parameters:

Param Type Description
category string Filter: payment, policy, kms, protocol, system
tx_id string Filter by transaction ID
since string ISO 8601 timestamp lower bound
limit number Max results (default: 100)

Example:

GET /api/v1/audit?category=policy&since=2026-02-10T00:00:00Z&limit=20

Response 200:

[
  {
    "id": 42,
    "timestamp": "2026-02-10T12:00:03",
    "level": "warn",
    "category": "policy",
    "action": "violations_detected",
    "tx_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "actor": "system",
    "details": "{\"violations\":[{\"rule\":\"single_transaction.max_amount_usd\",\"message\":\"...\"}]}",
    "ip_address": null,
    "user_agent": null
  }
]

Error Responses

All endpoints return errors in a consistent format:

{
  "error": "Human-readable error description"
}
HTTP Status Meaning
200 Success
202 Accepted β€” payment pending human confirmation
400 Bad request (validation error, execution failure)
404 Resource not found (transaction, pending confirmation)
500 Internal server error

OpenClaw Chat Integration

When the skill is activated in OpenClaw, the agent uses the SKILL.md instructions to output structured payment intents during conversation.

Payment Intent JSON Schema

The agent is instructed to output this exact JSON when a payment is needed:

{
  "protocol": "x402 | ap2",
  "action": "pay",
  "amount": "<decimal string>",
  "currency": "USDC | ETH | USD | EUR",
  "recipient": "<address or merchant ID>",
  "network": "ethereum | base | polygon | web2",
  "gateway": "viem | visa | mastercard | paypal | stripe | null",
  "description": "<human-readable description>",
  "metadata": {}
}

The protocol router extracts this JSON from the agent's message (even when surrounded by natural language) using regex-based extraction.

Protocol Detection Heuristics

The SKILL.md instructs the agent:

Signal Routed To
Target is an HTTP resource returning 402 x402
User mentions "x402", "stablecoin", "USDC", "onchain" x402
Payment involves a mandate, delegated purchase AP2
Traditional card/gateway payment via agent AP2
Crypto currency (USDC, ETH, DAI) web3 (Viem)
Fiat currency (USD, EUR) web2 (Stripe default)

Chat Confirmation Flow

When a policy violation is detected during a chat-initiated payment:

  1. Skill returns a Markdown prompt to the agent, which presents it to the user:

    ⚠️ **Payment Requires Your Confirmation**
    
    | Field | Value |
    |-------|-------|
    | Transaction ID | `a1b2c3d4...` |
    | Protocol | x402 |
    | Amount | 1500 USDC ($1500.00 USD) |
    | Recipient | `0x742d...` |
    | Gateway | viem |
    
    **Policy Violations:**
      - **single_transaction.max_amount_usd**: Amount $1500.00 exceeds limit of $1000.00
    
    Reply **"confirm a1b2c3d4"** to proceed or **"reject a1b2c3d4"** to cancel.
    
  2. User responds in the chat with confirm <txId> or reject <txId>

  3. Skill parses the response and resumes/cancels the payment


Usage Examples

Example 1 β€” x402 USDC Payment via CLI

Send 5 USDC on Base to a recipient:

export AWS_ACCESS_KEY_ID="AKIA..."
export AWS_SECRET_ACCESS_KEY="..."
export AWS_KMS_KEY_ID="arn:aws:kms:..."

agent-payments pay \
  --protocol x402 \
  --amount 5.00 \
  --currency USDC \
  --to 0x742d35Cc6635C0532925a3b844Bc9e7595f2bD65 \
  --network base \
  --wallet default_wallet

Output:

2026-02-10T12:00:00.000Z [info] Transaction created { id: 'a1b2...', protocol: 'x402' }
2026-02-10T12:00:00.010Z [info] Policy check passed { amountUsd: 5 }
2026-02-10T12:00:00.020Z [info] web3: Preparing ERC-20 transfer { to: '0x742d...', amount: '5.00' }
2026-02-10T12:00:02.500Z [info] web3: ERC-20 transfer sent { txHash: '0xdef456...' }
2026-02-10T12:00:15.000Z [info] web3: Transaction confirmed { status: 'success', block: '12345678' }

═══════════════════════════════════════
βœ… Payment executed successfully!
   TX Hash: 0xdef456...
   Internal TX ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
═══════════════════════════════════════

Example 2 β€” AP2 Stripe Payment via Web API

# Start the web API
npm run web

# In another terminal, execute a payment
curl -X POST http://localhost:3402/api/v1/payment \
  -H "Content-Type: application/json" \
  -d '{
    "protocol": "ap2",
    "action": "pay",
    "amount": "49.99",
    "currency": "USD",
    "recipient": "merchant-shop-12345",
    "gateway": "stripe",
    "description": "Premium subscription",
    "metadata": {"plan": "annual"}
  }'

Response:

{
  "success": true,
  "tx": {
    "id": "b2c3d4e5-f678-9012-bcde-f12345678901",
    "protocol": "ap2",
    "gateway": "stripe",
    "amount": 49.99,
    "amount_usd": 49.99,
    "currency": "USD",
    "status": "executed"
  },
  "web2Result": {
    "gateway": "stripe",
    "transaction_id": "pi_3Abc123...",
    "status": "pending",
    "amount": "49.99",
    "currency": "USD"
  },
  "policyResult": {
    "allowed": true,
    "violations": [],
    "requiresHumanConfirmation": false
  },
  "confirmationRequired": false
}

Example 3 β€” AI Chat-Driven Payment

User prompt in OpenClaw chat:

"Pay 10 USDC to 0x742d35Cc6635C0532925a3b844Bc9e7595f2bD65 on Base for API access"

The agent responds with embedded JSON (per SKILL.md instructions):

I'll process that payment for you:

{
  "protocol": "x402",
  "action": "pay",
  "amount": "10.00",
  "currency": "USDC",
  "recipient": "0x742d35Cc6635C0532925a3b844Bc9e7595f2bD65",
  "network": "base",
  "gateway": "viem",
  "description": "API access payment"
}

The skill's protocol router extracts this JSON, validates it, runs policy checks, and executes the payment.

Example 4 β€” Policy Violation & Human Confirmation

Via Web API:

# 1. Submit a payment that exceeds the single-transaction limit
curl -X POST http://localhost:3402/api/v1/payment \
  -H "Content-Type: application/json" \
  -d '{
    "protocol": "ap2",
    "action": "pay",
    "amount": "1500.00",
    "currency": "USD",
    "recipient": "vendor-99",
    "gateway": "stripe"
  }'

# Response: 202 with confirmationRequired: true
# {
#   "confirmationRequired": true,
#   "confirmationPrompt": "Confirmation required for tx abc123... POST /api/v1/confirm/abc123..."
# }

# 2. Check pending confirmations
curl http://localhost:3402/api/v1/pending

# 3. Approve the payment
curl -X POST http://localhost:3402/api/v1/confirm/abc123... \
  -H "Content-Type: application/json" \
  -d '{"confirmed": true, "reason": "One-time approved by CFO"}'

Example 5 β€” Key Management

# Store a wallet private key
agent-payments keys store \
  --alias trading_wallet \
  --type web3_private_key \
  --value "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"

# Store Stripe API key
agent-payments keys store \
  --alias stripe_api_key \
  --type stripe_token \
  --value "sk_live_abcdefghijklmnop"

# List all keys (plaintext is NEVER shown)
agent-payments keys list

# β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
# β”‚ id       β”‚ key_type          β”‚ key_alias       β”‚ kms_key_id                  β”‚ created_at          β”‚
# β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
# β”‚ a1b2...  β”‚ web3_private_key  β”‚ trading_wallet  β”‚ arn:aws:kms:us-east-1:...   β”‚ 2026-02-10 12:00:00 β”‚
# β”‚ c3d4...  β”‚ stripe_token      β”‚ stripe_api_key  β”‚ arn:aws:kms:us-east-1:...   β”‚ 2026-02-10 12:01:00 β”‚
# β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

# Delete a key
agent-payments keys delete trading_wallet

Development

Build

npm run build        # Compile TypeScript to dist/

Run Tests

npm test             # Run Jest test suite

Run in Development

npm run dev          # ts-node src/index.ts

Project Dependencies

Package Version Purpose
@coinbase/x402 ^2.1.0 x402 facilitator SDK
viem ^2.28.0 Ethereum wallet, signing, tx building
stripe ^17.0.0 Stripe payment SDK
@paypal/paypal-server-sdk ^2.0.0 PayPal payments
@aws-sdk/client-kms ^3.750.0 AWS KMS encrypt/decrypt
better-sqlite3 ^11.8.0 SQLite driver (native, synchronous)
yaml ^2.7.0 YAML config parsing
express ^5.1.0 Web API server
commander ^13.1.0 CLI framework
winston ^3.17.0 Multi-transport logging
zod ^3.24.0 Schema validation
uuid ^11.1.0 UUID generation for IDs
readline-sync ^1.4.10 CLI interactive prompts

Troubleshooting

Common Issues

Symptom Cause Fix
AWS KMS key ID not found in env var Missing AWS_KMS_KEY_ID environment variable Export AWS_KMS_KEY_ID before running
Encrypted key not found for alias 'X' Key not stored yet Run agent-payments keys store --alias X ...
Network 'X' is disabled in configuration Chain disabled in YAML Set web3.X.enabled: true in config
x402 protocol is disabled in configuration Protocol toggle Set protocols.x402.enabled: true
Could not parse a valid payment intent AI output doesn't contain valid JSON Ensure agent uses the exact JSON schema from SKILL.md
SQLITE_BUSY errors Concurrent writes Increase database.busy_timeout_ms or ensure WAL mode
Policy violations detected (unexpected) Aggregate limits hit Check agent-payments audit --category policy and adjust policy.rules
Web API not starting Port conflict Change web_api.port in config

Debugging

  1. Set log level to debug in config/default.yaml:

    logging:
      level: "debug"
  2. Check the audit log for full context:

    agent-payments audit --limit 20
  3. Inspect a specific transaction:

    agent-payments tx <transaction-id>
  4. Query SQLite directly:

    sqlite3 data/payments.db "SELECT * FROM transactions ORDER BY created_at DESC LIMIT 10;"
    sqlite3 data/payments.db "SELECT * FROM audit_log ORDER BY timestamp DESC LIMIT 20;"

License

This project is licensed under the Apache 2.0 License. See the LICENSE-APACHE file for the details.


Built with πŸ€–πŸ’΅ for the Open Agent Skills Ecosystem and for the OpenClaw ecosystem. Protocols: x402 Β· AP2

About

πŸ€–πŸ’΅ Agentic Payment Service for Open Agent Skills Ecosystem.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published