@itpay/cli 0.2.7 → 0.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/docs/agent/buyer/payment-qr.json +4 -2
- package/docs/agent/buyer/payment-wait.json +1 -1
- package/docs/agent/buyer/quickstart.json +1 -1
- package/lib/buyer.js +1 -0
- package/lib/render-human.js +74 -8
- package/lib/runtime.js +1 -1
- package/package.json +1 -1
- package/skills/itpay-buyer/SKILL.md +7 -7
package/README.md
CHANGED
|
@@ -239,8 +239,9 @@ Payment QR rules:
|
|
|
239
239
|
- Otherwise render the ItPay-hosted `qr_png_url` / `preferred_qr_url`.
|
|
240
240
|
- Use `mobile_wallet_url` only as a human mobile fallback.
|
|
241
241
|
- Do not generate your own QR from payment URLs.
|
|
242
|
-
-
|
|
243
|
-
-
|
|
242
|
+
- If you know the current chat channel and `render_plan.platforms.<channel>` exists, use that branch before `render_plan.selected`.
|
|
243
|
+
- In OpenClaw Telegram, send `render_plan.platforms.telegram.openclaw_message` with OpenClaw `message send --media ... --presentation ...`; do not put legacy inline media directives inside human prose.
|
|
244
|
+
- If the channel is unsupported, use `render_plan.selected.fallback` / plain chat. Do not rewrite the chosen plan into a custom table or omit the real URL.
|
|
244
245
|
- If status is `payment_handoff_required`, `next` is the human reply step, not payment wait.
|
|
245
246
|
- Treat only `payment_intent.verified` as payment success.
|
|
246
247
|
|
|
@@ -41,10 +41,12 @@
|
|
|
41
41
|
"Payment QR display requires both a valid/saved buyer session and payment_intent_id. If payment_intent_id appears before buyer_session is saved, run buyer checkout resume/status instead of showing payment QR.",
|
|
42
42
|
"Do not stop after showing auth_qr unless the human explicitly asks you to pause. Keep waiting/resuming the same checkout so the post-auth payment handoff can continue.",
|
|
43
43
|
"Do not describe auth_qr as payment success or payment proof. Only payment_intent.verified proves payment.",
|
|
44
|
-
"If
|
|
45
|
-
"
|
|
44
|
+
"If you know the current chat channel and response.render_plan.platforms.<channel> exists, use that channel branch before response.render_plan.selected.",
|
|
45
|
+
"If response.render_plan.selected.platform is current_channel_required, choose the branch matching the current channel; selected.fallback is only for unsupported plain chat clients.",
|
|
46
|
+
"Do not rewrite the chosen render plan into a custom table. Use its text/markdown, media, links, and buttons as provided.",
|
|
46
47
|
"If selected.links exists, include the real URL. Do not only show the link label.",
|
|
47
48
|
"If selected.buttons exists but the current adapter cannot render native buttons, degrade to selected.links plus short text; do not fake payment success or skip the status check.",
|
|
49
|
+
"In OpenClaw Telegram, prefer response.render_plan.platforms.telegram.openclaw_message: send its message/media/presentation through OpenClaw message send. Do not place legacy inline media directives inside prose; that fallback cannot render inline buttons.",
|
|
48
50
|
"In Codex or Claude Code app clients, send response.human_visible_markdown or response.render_plan.platforms.codex_app.markdown to the human first. Do not start a hidden long wait before the human-visible message is sent.",
|
|
49
51
|
"For Telegram-style message clients, use response.render_plan.platforms.telegram: send photo media first, include links, and expose native buttons for refresh/status when the adapter supports them.",
|
|
50
52
|
"For plain chat clients, show response.human_visible_markdown and stop unless the human asks you to check status or returns with a payment question.",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
}
|
|
36
36
|
],
|
|
37
37
|
"agent_rules": [
|
|
38
|
-
"If payment wait returns payment_handoff_required, render response.render_plan.
|
|
38
|
+
"If payment wait returns payment_handoff_required, render response.render_plan.platforms.<current channel> when available; otherwise use response.render_plan.selected/fallback before running any later status check.",
|
|
39
39
|
"Start payment status checks only after the QR image/link has already been sent to the human or the human asks to check status.",
|
|
40
40
|
"If you run payment wait without a short timeout while payment is still pending, CLI returns payment_handoff_required and human_visible_markdown instead of long-polling.",
|
|
41
41
|
"Payment wait/status check is a recovery loop around the same payment_intent_id, not a one-shot command.",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
"Show the returned payment QR exactly as provided before starting payment wait.",
|
|
69
69
|
"Payment truth comes only from payment_intent.verified.",
|
|
70
70
|
"Secure delivery goes to the human first; report redacted delivery status only.",
|
|
71
|
-
"When a response includes render_plan.selected
|
|
71
|
+
"When a response includes render_plan, first use render_plan.platforms.<current channel> if it exists. If selected.platform is current_channel_required, selected.fallback is only for unsupported plain chat. In OpenClaw Telegram, use render_plan.platforms.telegram.openclaw_message instead of handwritten legacy media directive prose."
|
|
72
72
|
],
|
|
73
73
|
"forbidden": [
|
|
74
74
|
"Do not call ops commands from the buyer path.",
|
package/lib/buyer.js
CHANGED
|
@@ -1649,6 +1649,7 @@ function compactHumanAction(action = null) {
|
|
|
1649
1649
|
qr_image_url: action.qr_image_url,
|
|
1650
1650
|
preferred_qr_url: action.preferred_qr_url,
|
|
1651
1651
|
mobile_wallet_url: action.mobile_wallet_url,
|
|
1652
|
+
presentation: action.presentation,
|
|
1652
1653
|
agent_display_hint: action.agent_display_hint,
|
|
1653
1654
|
expires_at: action.expires_at
|
|
1654
1655
|
});
|
package/lib/render-human.js
CHANGED
|
@@ -324,17 +324,19 @@ function buildHumanActionRenderPlan(action = {}, intent = {}, flags = {}) {
|
|
|
324
324
|
print_links: true
|
|
325
325
|
}
|
|
326
326
|
});
|
|
327
|
-
const
|
|
328
|
-
const selected = {
|
|
329
|
-
platform: selectedPlatform,
|
|
330
|
-
...platforms[selectedPlatform]
|
|
331
|
-
};
|
|
327
|
+
const selected = selectedRenderPlan(host, platforms);
|
|
332
328
|
return compactObject({
|
|
333
329
|
kind,
|
|
334
330
|
proof_rule: kind === "payment_qr"
|
|
335
331
|
? "Only payment_intent.verified proves payment."
|
|
336
332
|
: "This human action is not payment proof.",
|
|
337
333
|
host: host || undefined,
|
|
334
|
+
platform_selection: {
|
|
335
|
+
rule: host
|
|
336
|
+
? "selected was chosen from the detected host. If your actual channel has a more specific branch in platforms, use that branch."
|
|
337
|
+
: "CLI could not detect the chat channel. If your current channel appears in platforms, use that branch; selected.fallback is only for unsupported plain chat.",
|
|
338
|
+
telegram: "For Telegram/OpenClaw, use platforms.telegram.openclaw_message instead of selected.fallback."
|
|
339
|
+
},
|
|
338
340
|
required_outputs: requiredOutputs,
|
|
339
341
|
selected,
|
|
340
342
|
platforms,
|
|
@@ -377,8 +379,48 @@ function telegramRenderPlan(action = {}, { localQRPath = "", preferredQRURL = ""
|
|
|
377
379
|
? "请扫码或点击链接完成支付。付款后点“我已付款,查询状态”。"
|
|
378
380
|
: "请打开 ItPay 授权入口,完成后回到当前对话。",
|
|
379
381
|
links,
|
|
380
|
-
buttons
|
|
382
|
+
buttons,
|
|
383
|
+
openclaw_message: openclawTelegramMessage({ action, media, links, buttons })
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function openclawTelegramMessage({ action = {}, media = [], links = [], buttons = [] } = {}) {
|
|
388
|
+
const mediaURL = media[0]?.local_path || media[0]?.fallback_url || "";
|
|
389
|
+
const text = action.kind === "payment_qr"
|
|
390
|
+
? "请扫码或点击链接完成支付。付款完成后点“我已付款,查询状态”,或直接回复“我已付款”。"
|
|
391
|
+
: "请打开 ItPay 授权入口,完成后回到当前对话。";
|
|
392
|
+
const presentationButtons = links.map((link) => ({ label: link.label, url: link.url }));
|
|
393
|
+
if (action.kind === "payment_qr") {
|
|
394
|
+
presentationButtons.push(
|
|
395
|
+
{ label: "支付遇到问题 / 刷新", action: { type: "command", command: "刷新付款二维码" } },
|
|
396
|
+
{ label: "我已付款,查询状态", action: { type: "command", command: "我已付款,查询状态" } }
|
|
397
|
+
);
|
|
398
|
+
} else {
|
|
399
|
+
for (const button of buttons) {
|
|
400
|
+
if (button.url && !presentationButtons.some((item) => item.url === button.url)) {
|
|
401
|
+
presentationButtons.push({ label: button.text || "打开链接", url: button.url });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
const presentation = {
|
|
406
|
+
blocks: [
|
|
407
|
+
{ type: "text", text },
|
|
408
|
+
...(presentationButtons.length ? [{ type: "buttons", buttons: presentationButtons }] : [])
|
|
409
|
+
]
|
|
381
410
|
};
|
|
411
|
+
return compactObject({
|
|
412
|
+
adapter: "openclaw",
|
|
413
|
+
channel: "telegram",
|
|
414
|
+
use: "message send --media/--presentation; do not use legacy inline media directives",
|
|
415
|
+
message: text,
|
|
416
|
+
media: mediaURL || undefined,
|
|
417
|
+
presentation,
|
|
418
|
+
command_args: compactObject({
|
|
419
|
+
message: text,
|
|
420
|
+
media: mediaURL || undefined,
|
|
421
|
+
presentation
|
|
422
|
+
})
|
|
423
|
+
});
|
|
382
424
|
}
|
|
383
425
|
|
|
384
426
|
function selectedRenderPlatform(host = "") {
|
|
@@ -387,7 +429,22 @@ function selectedRenderPlatform(host = "") {
|
|
|
387
429
|
if (["claude", "claude_code", "claudecode"].includes(normalized)) return "claude_code";
|
|
388
430
|
if (["telegram", "tg"].includes(normalized)) return "telegram";
|
|
389
431
|
if (terminalQRHostAliases().has(normalized)) return "terminal";
|
|
390
|
-
return "
|
|
432
|
+
return "";
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
function selectedRenderPlan(host = "", platforms = {}) {
|
|
436
|
+
const selectedPlatform = selectedRenderPlatform(host);
|
|
437
|
+
if (selectedPlatform && platforms[selectedPlatform]) {
|
|
438
|
+
return { platform: selectedPlatform, ...platforms[selectedPlatform] };
|
|
439
|
+
}
|
|
440
|
+
return compactObject({
|
|
441
|
+
platform: "current_channel_required",
|
|
442
|
+
rule: "Use render_plan.platforms.<current channel> when available; fallback is only for unsupported plain chat clients.",
|
|
443
|
+
telegram: platforms.telegram,
|
|
444
|
+
codex_app: platforms.codex_app,
|
|
445
|
+
claude_code: platforms.claude_code,
|
|
446
|
+
fallback: platforms.plain_chat
|
|
447
|
+
});
|
|
391
448
|
}
|
|
392
449
|
|
|
393
450
|
function compactObject(value = {}) {
|
|
@@ -495,7 +552,16 @@ function shouldReturnAfterAgentTextQR(flags = {}) {
|
|
|
495
552
|
}
|
|
496
553
|
|
|
497
554
|
function agentHost(flags = {}) {
|
|
498
|
-
const explicit = String(
|
|
555
|
+
const explicit = String(
|
|
556
|
+
process.env.ITP_HOST ||
|
|
557
|
+
flags.host ||
|
|
558
|
+
flags.channel ||
|
|
559
|
+
process.env.OPENCLAW_CURRENT_CHANNEL_PROVIDER ||
|
|
560
|
+
process.env.OPENCLAW_CHANNEL ||
|
|
561
|
+
process.env.CURRENT_CHANNEL_PROVIDER ||
|
|
562
|
+
process.env.AGENT_CHANNEL ||
|
|
563
|
+
""
|
|
564
|
+
).toLowerCase();
|
|
499
565
|
if (explicit) return explicit;
|
|
500
566
|
if (process.env.CODEX_THREAD_ID || process.env.CODEX_SHELL || process.env.CODEX_CI) return "codex";
|
|
501
567
|
if (process.env.CLAUDECODE || process.env.CLAUDE_CODE || process.env.CLAUDECODE_SESSION_ID) return "claude-code";
|
package/lib/runtime.js
CHANGED
|
@@ -11,7 +11,7 @@ import { accountOverviewStatus, nextActionForAuthStatus, outputAccountStatus, re
|
|
|
11
11
|
import {
|
|
12
12
|
CREDENTIALS_PATH, CONFIG_DIR, RUNS_DIR, VERSION, apiBase, apiTimeoutMs, appendURLQuery, booleanFlag, cliCommand, commandExists,
|
|
13
13
|
cryptoRandom, csvValues, currentExecutable, deleteGrantCredential, deleteSessionCredential, detectNativeCredentialStore,
|
|
14
|
-
escapeTomlString, fileMode, intFlag, listRuns, maskSecret, mergeRun, output, parseFlags, positional, prepareSetupRun,
|
|
14
|
+
escapeTomlString, fileMode, intFlag, listRuns, maskSecret, mergeRun, normalizePurchaseFlags, output, parseFlags, positional, prepareSetupRun,
|
|
15
15
|
queryString, quoteShell, readConfig, readCredentials, readGrantCredential, readJSON, readRun, readSessionToken, readState,
|
|
16
16
|
readText, replaceManagedBlock, safeErrorMessage, sanitizeAuthResponse, shellQuote, sleep, storeGrantCredential,
|
|
17
17
|
updateCurrentRun, updateRun, withStateLock, writeConfig, writeCredentials, writeJSONWithBackup, writeRun,
|
package/package.json
CHANGED
|
@@ -211,13 +211,13 @@ itp buy <variant_id> --email <buyer_email> --phone <buyer_phone> --display agent
|
|
|
211
211
|
|
|
212
212
|
This keeps JSON output machine-readable while allowing the CLI to prepare a
|
|
213
213
|
local QR image path for clients that cannot render remote SVG reliably. In
|
|
214
|
-
|
|
215
|
-
`render_plan.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
the
|
|
219
|
-
|
|
220
|
-
not invent a table or rewrite the UI plan.
|
|
214
|
+
agent/chat clients, prefer `--no-wait-payment`: if you know the current channel
|
|
215
|
+
and `render_plan.platforms.<channel>` exists, use that branch first. In Codex
|
|
216
|
+
or Claude Code app clients, send the platform markdown exactly. In OpenClaw
|
|
217
|
+
Telegram, use `render_plan.platforms.telegram.openclaw_message` so media and
|
|
218
|
+
native buttons are sent through the adapter. If `selected.platform` is
|
|
219
|
+
`current_channel_required`, `selected.fallback` is only for unsupported plain
|
|
220
|
+
chat clients. Do not invent a table or rewrite the UI plan.
|
|
221
221
|
|
|
222
222
|
If a response has `status=payment_handoff_required`, `next` is the user-visible
|
|
223
223
|
reply step, not payment wait. Do not run `buyer payment wait` until the human
|