okx-a2a-payment
Original:🇺🇸 English
Translated
Use this skill when the user mentions creating a payment link, paying a paymentId / a2a_... link, or checking a2a payment status. Wraps `onchainos payment a2a-pay` agent-to-agent payment protocol: seller-side `create`, buyer-side `pay` via EIP-3009 + TEE signing, and `status` query. Buyer-side trust is delegated to upstream — the skill signs whatever the on-server challenge declares. Do NOT use for external HTTP 402 resources — use okx-x402-payment. Do NOT use for wallet balance / transfer / login — use okx-agentic-wallet.
14installs
Sourceokx/onchainos-skills
Added on
NPX Install
npx skill4agent add okx/onchainos-skills okx-a2a-paymentTags
Translated version includes tags in frontmatterSKILL.md Content
View Translation Comparison →Onchain OS A2A Payment
Wrap the CLI surface end-to-end for both seller and buyer roles. Buyer-side trust is delegated to the upstream caller — when invoked with a , the skill fetches the on-server challenge, TEE-signs it as-is, submits the credential, and auto-polls payment status to a terminal state.
onchainos payment a2a-paypaymentIdSkill Routing
This skill only covers internal a2a payments issued via . If the request fits one of the intents below, route to the corresponding skill instead:
onchainos payment a2a-pay| Intent | Use skill |
|---|---|
| External HTTP 402 payment-gated resource (any non-onchainos URL) | |
| Wallet balance / transfer / login | |
| Task publish / accept / deliver / verify (business layer) — payment sub-step calls back into this skill via the Workflow A contract | upstream task / agent skill (out of repo) |
Internal | this skill |
Triggers
Skill activates on user intents that match any of:
- "create payment link", "create a2a payment", "generate payment", "create payment authorization"
- "pay paymentId", "pay a2a_...", "pay this link", "settle this payment"
- "payment status", "a2a payment status", "check payment status", "where is my payment"
Pre-flight Checks
Both seller () and buyer () require an authenticated wallet session. The CLI calls internally and bails on .
createpayensure_tokens_refreshednot logged inBefore invoking or :
createpaybash
onchainos wallet status- Logged in → proceed.
- Not logged in → ask the user to log in via (AK login, no email) or
onchainos wallet login(OTP login). Do NOT attempt to sign without a live session.onchainos wallet login <email>
statusOperation Flow
Seller — Create a Payment Link (a2a-pay create
)
a2a-pay createInputs:
- Required: (decimal token amount, e.g.
--amount),"0.01"(e.g.--symbol),"USDT"(0x... EVM address — seller wallet)--recipient - Optional: ,
--description,--realm(seconds, default 1800)--expires-in
Steps:
-
Run pre-flight (see above) — the CLI requires a live session.
-
Shell out:bash
onchainos payment a2a-pay create \ --amount <amount> --symbol <symbol> --recipient <recipient> \ [--description <text> --realm <domain> --expires-in <seconds>] -
Parse the response — onlyand
payment_id(optional) are present. The CLI no longer returnsdeliveries.url/amount; the skill echoes the seller's input args back for display.currency -
Display to the user:Payment link created. • paymentId:• Amount:
<id>(decimal as you submitted) • Recipient:<amount input> <symbol input>• Share with buyer:<recipient input>(if returned by the server) or<deliveries.url>paymentId=<id> -
Suggest next: poll status anytime withonce the buyer is expected to have paid.
onchainos payment a2a-pay status --payment-id <id>
Buyer — Pay a Payment Link (a2a-pay pay
)
a2a-pay payRequired input: only. The CLI fetches the seller-issued challenge from the server and signs whatever amount / currency / recipient the challenge declares.
paymentIdTrust model: the buyer signs the seller's challenge as-is. Verifying that the challenge matches what the buyer agreed to pay is the upstream caller's responsibility: the user (or the upstream skill) MUST cross-check the seller's/paymentIdagainst their out-of-band agreement (chat, task spec, prior negotiation) before calling this skill. Once the skill is invoked, it will sign the on-server challenge.deliveries.url
Step 1 — Sign and Submit
The skill does not run its own preview / yes-no gate; trust is delegated to the upstream caller (see the trust-model note above). Shell out directly:
bash
onchainos payment a2a-pay pay --payment-id <paymentId>The CLI fetches the on-server challenge, TEE-signs the EIP-3009 authorization, and submits the credential. The successful response shape:
json
{
"payment_id": "a2a_xxx",
"status": "<status>",
"tx_hash": "<hash or null>",
"valid_after": 0,
"valid_before": 1746000000,
"signature": "0x..."
}Step 2 — Auto-poll Status to Terminal
Status classification:
- Non-terminal (poll): ,
pendingsettling - Terminal (stop): ,
completed,failed,expiredcancelled
If is already terminal → render the result (see table below) and stop.
statusIf non-terminal → poll every 3 seconds, up to a 60-second total budget:
bash
onchainos payment a2a-pay status --payment-id <paymentId>- As soon as a terminal status is observed → render full result (status + tx_hash + block_number) and stop.
- If 60 seconds elapse and the status is still non-terminal → return the current plus the paymentId, and tell the user: "Status is still
statusafter 60s; you can run<status>again later."status
Terminal display strings:
| status | Display |
|---|---|
| "✅ Payment confirmed on-chain. tx_hash: |
| "❌ Payment failed. (include the server-provided reason if any)" |
| "⌛ Payment link expired before settlement. Ask the seller for a new one." |
| "🚫 Seller cancelled this payment." |
Status — Query Payment State (a2a-pay status
)
a2a-pay statusInput: .
paymentIdSteps:
-
Run:bash
onchainos payment a2a-pay status --payment-id <paymentId> -
Map the returnedto a human-readable line:
statusstatus Meaning Display pendingAwaiting buyer signature "⏳ Awaiting buyer signature." settlingCredential received, settling on-chain "🔄 Settling on-chain (credential submitted, awaiting confirmation)." completedConfirmed on-chain "✅ Confirmed on-chain. tx_hash: block:<tx_hash>fee:<block_number>"<fee_decimal> <fee_symbol>failedPayment failed "❌ Failed. (include the server-provided reason if any)" expiredExpired before settlement "⌛ Expired before settlement." cancelledSeller cancelled "🚫 Cancelled by seller." -
Rendering the fee. The CLI returnsas a top-level string in minimal units (and
fee_amountas the basis-points used). To computefee_bps, look up the token decimals in the table under "Amount Display Rules". For<fee_decimal>, reuse the<fee_symbol>the seller passed to--symbolfor the samecreate— the upstream caller (or the seller flow that issued the link) is the source of truth for the token symbol; thepaymentIdresponse itself does not echo it back. If neither is available, displaystatusminimal units as-is.fee_amount -
Suggest next:
- /
pending→ "Check again in a few moments" or wait briefly and re-runsettling.status - → recommend
completedto verify the buyer's post-payment balance delta.okx-agentic-wallet - → recommend checking buyer balance via
failed, and ifokx-agentic-walletis present, inspect it viatx_hash.okx-security tx-scan
Cross-Skill Workflows
Workflow A — Sub-skill called from an upstream agent flow (most common)
Applicable upstream callers: any agent-to-agent task / chat / agent flow that holds the seller-issued payment information.
Contract — upstream MUST hand off (skill stops and asks the user if missing). Upstream is also responsible for confirming, before invoking this skill, that the matches the buyer's agreed terms — once invoked, the skill signs whatever the on-server challenge declares.
paymentIdpaymentId1. <upstream caller> verifies paymentId matches the buyer's agreed terms → hands off paymentId
↓
2. okx-a2a-payment (this skill) onchainos payment a2a-pay pay → auto-poll status → display terminal state
↓
3. okx-agentic-wallet optional: onchainos wallet balance to see post-payment deltaWorkflow B — Seller manually creates a payment link
1. okx-a2a-payment create → paymentId + deliveries.url
2. Seller shares paymentId (and optionally deliveries.url) with the buyer out-of-band (chat / QR / message)
3. Buyer cross-checks the paymentId / deliveries.url against the seller's quoted terms, then runs Workflow A starting from step 2 with the received paymentIdWorkflow C — Payment failure triage
1. okx-a2a-payment status → expired / failed / cancelled
2. Branch on terminal state:
- expired → ask seller to create a new link
- failed → check buyer balance via okx-agentic-wallet; inspect tx_hash via okx-security tx-scan if present
- cancelled → contact seller out-of-bandUpstream Routing — Avoiding create
Loops
createThis skill is stateless per call and has no view of the conversation. If the upstream seller agent routes by surface keywords alone (e.g. matches / / and always calls ), it will loop:
付款paypaymentcreatebuyer: "I want to pay" → seller create → returns paymentId_A
buyer pays via this skill, then sends:
buyer: "payment successful" → seller matches "payment" → create AGAIN → paymentId_B (wrong)The skill cannot break this loop — the fix lives in the upstream caller's intent router. When you wire this skill into a seller-side agent, enforce the following before calling :
create-
Detect existing paymentId in the incoming message. If the buyer's message contains anid (or a
a2a_...you previously issued), route todeliveries.urlfor that id. Do NOT callokx-a2a-payment status.create -
Disambiguate intent beyond keywords. Map upstream intents to commands:
Buyer says Intent Route to "I want to pay" / "请付款" / "怎么付" / "give me a link" request-invoice create"paid" / "payment successful" / "已付" / "已转账" / contains a paymentId or tx hash payment-receipt (or no-op if already terminal)status"cancel" / "refund" cancel/refund out of scope for this skill Plain keyword matching on/付款/payis not enough — both request-invoice and payment-receipt utterances contain those tokens.payment -
Track per-conversation order state upstream. Onceissues a paymentId for a given (buyer, order) context, the upstream agent must remember that paymentId in its own conversation / order state and mark the order as "awaiting payment". Subsequent buyer messages in that context default to
createagainst the remembered paymentId until either the payment reaches a terminal state or the user explicitly asks for a new order.status -
Idempotency on. Before issuing a new
create, the upstream agent must check its own state: if a non-terminal paymentId already exists for the same buyer / order context, reuse it instead of creating a new one.create
This guidance is advisory for upstream agent authors — this skill itself will still execute whichever command you call. Routing correctness is the upstream caller's job.
Amount Display Rules
When converting (or ) from minimal units to a decimal display, use the hardcoded decimals table:
amountfee_amount| Token | Decimals | "1000000" minimal renders as |
|---|---|---|
| USDC | 6 | 1.00 USDC |
| USDT | 6 | 1.00 USDT |
| USDG | 6 | 1.00 USDG |
| ETH | 18 | ( |
For any symbol not in the table: render and append the warning . Do not block the flow.
<minimal> <symbol>unknown decimals — please double-check the seller-provided amountEdge Cases
| Scenario | Handling |
|---|---|
| Prompt the user to run |
User provides no | STOP and ask the user for the seller-issued paymentId. |
CLI reports | Relay the error verbatim and surface it as a terminal failure — do NOT retry signing. |
| Relay the error and ask the user to confirm the paymentId with the seller or upstream caller. |
| Return the current status (verbatim) + paymentId; tell the user |
| Server returns a 5xx | Surface the status code and any |
| Apply the unknown-decimals fallback (see Amount Display Rules). Do not block. |
| |
Command Index
| # | Command | Role | Purpose |
|---|---|---|---|
| 1 | | Seller | Create a payment link, returns paymentId + deliveries |
| 2 | | Buyer | Fetch challenge → TEE-sign EIP-3009 → submit credential |
| 3 | | Either | Query current status (pending / settling / completed / failed / expired / cancelled) |
CLI Command Reference
1. onchainos payment a2a-pay create
onchainos payment a2a-pay createbash
onchainos payment a2a-pay create \
--amount <decimal> --symbol <symbol> --recipient <address> \
[--description <text>] [--realm <domain>] [--expires-in <seconds>]| Param | Required | Default | Description |
|---|---|---|---|
| Yes | - | Decimal token amount (e.g. |
| Yes | - | ERC-20 token symbol (e.g. |
| Yes | - | Seller wallet address (= EIP-3009 |
| No | - | Human-readable description shown to the buyer |
| No | - | Seller / provider domain (e.g. |
| No | 1800 | Payment-link expiration window in seconds |
Return fields: , (object containing when issued by the server).
payment_iddeliveriesurl2. onchainos payment a2a-pay pay
onchainos payment a2a-pay paybash
onchainos payment a2a-pay pay --payment-id <id>| Param | Required | Default | Description |
|---|---|---|---|
| Yes | - | Seller-issued paymentId |
Return fields: , , (optional), , , .
payment_idstatustx_hashvalid_aftervalid_beforesignature3. onchainos payment a2a-pay status
onchainos payment a2a-pay statusbash
onchainos payment a2a-pay status --payment-id <id>| Param | Required | Default | Description |
|---|---|---|---|
| Yes | - | The paymentId to query |
Return fields: , , (optional), (optional), (optional), (optional, minimal units), (optional).
payment_idstatustx_hashblock_numberblock_timestampfee_amountfee_bpsQuickstart
bash
# Seller — create a payment link
onchainos payment a2a-pay create \
--amount 0.01 --symbol USDT \
--recipient 0xSellerWalletAddress
# → { "payment_id": "a2a_xxx", "deliveries": { "url": "..." } }
# Buyer — pay (signs the on-server challenge as-is; trust delegated to upstream)
onchainos payment a2a-pay pay --payment-id a2a_xxx
# Either side — query status (skill auto-polls this for ~60s after pay if non-terminal)
onchainos payment a2a-pay status --payment-id a2a_xxx