@itpay/cli 2.1.9 → 2.1.10
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/dist/src/commands/buy.js +1 -0
- package/dist/src/commands/checkout.js +16 -1
- package/dist/src/commands/checkout_handoff.js +41 -9
- package/dist/src/commands/services.js +1 -0
- package/dist/src/main.js +3 -0
- package/dist/src/state/config.js +1 -1
- package/docs/agent/buyer/render-hosts.json +8 -7
- package/package.json +1 -1
- package/skills/itpay/SKILL.md +7 -0
package/dist/src/commands/buy.js
CHANGED
|
@@ -203,6 +203,7 @@ function buildBuyEnvelope(input) {
|
|
|
203
203
|
const presentationHandoff = buildCheckoutHandoff({
|
|
204
204
|
platform,
|
|
205
205
|
url: input.plan.linkOnlyURL ?? input.checkoutURL,
|
|
206
|
+
mobileUrl: input.checkoutURL,
|
|
206
207
|
amount,
|
|
207
208
|
plan: input.plan,
|
|
208
209
|
...(input.agentType ? { agentType: input.agentType } : {}),
|
|
@@ -22,7 +22,7 @@ export async function runCheckoutPresentation(backend, options) {
|
|
|
22
22
|
});
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
|
-
const checkoutURL = checkoutPageURL(options.baseURL, options.checkoutID, options.displayToken);
|
|
25
|
+
const checkoutURL = savedCheckoutURLOrFallback(options.savedCheckoutURL, options.checkoutID, options.displayToken, checkoutPageURL(options.baseURL, options.checkoutID, options.displayToken));
|
|
26
26
|
const cardURL = localizeCardURL(absolutePublicURL(options.baseURL, presentation.card_url ?? checkoutCardURL(options.baseURL, options.checkoutID, options.displayToken)), locale);
|
|
27
27
|
const qrPNGURL = absolutePublicURL(options.baseURL, presentation.card_png_url ?? presentation.qr_png_url ?? checkoutCardPNGURL(options.baseURL, options.checkoutID, options.displayToken));
|
|
28
28
|
const localizedPNGURL = localizeCardURL(qrPNGURL, locale);
|
|
@@ -68,6 +68,7 @@ function pendingCheckoutEnvelope(presentation, checkoutURL, plan, nextCommand, a
|
|
|
68
68
|
const presentationHandoff = buildCheckoutHandoff({
|
|
69
69
|
platform,
|
|
70
70
|
url: plan.linkOnlyURL ?? checkoutURL,
|
|
71
|
+
mobileUrl: checkoutURL,
|
|
71
72
|
amount,
|
|
72
73
|
plan,
|
|
73
74
|
...(agentType ? { agentType } : {}),
|
|
@@ -164,6 +165,20 @@ function checkoutPageURL(baseURL, checkoutID, displayToken) {
|
|
|
164
165
|
const root = publicRoot(baseURL);
|
|
165
166
|
return `${root}/checkout/${encodeURIComponent(checkoutID)}?display_token=${encodeURIComponent(displayToken)}`;
|
|
166
167
|
}
|
|
168
|
+
function savedCheckoutURLOrFallback(savedURL, checkoutID, displayToken, fallback) {
|
|
169
|
+
if (!savedURL) {
|
|
170
|
+
return fallback;
|
|
171
|
+
}
|
|
172
|
+
try {
|
|
173
|
+
const parsed = new URL(savedURL);
|
|
174
|
+
const belongsToCheckout = parsed.pathname === `/checkout/${checkoutID}`;
|
|
175
|
+
const sameToken = parsed.searchParams.get("display_token") === displayToken;
|
|
176
|
+
return belongsToCheckout && sameToken ? savedURL : fallback;
|
|
177
|
+
}
|
|
178
|
+
catch {
|
|
179
|
+
return fallback;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
167
182
|
function checkoutCardURL(baseURL, checkoutID, displayToken) {
|
|
168
183
|
const root = publicRoot(baseURL);
|
|
169
184
|
return `${root}/v1/checkouts/${encodeURIComponent(checkoutID)}/card?display_token=${encodeURIComponent(displayToken)}`;
|
|
@@ -17,8 +17,26 @@ export function buildWorkBuddyPresentFilesAction(url) {
|
|
|
17
17
|
arguments: { files: [url] },
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
+
function hasCompleteCheckoutTokens(url) {
|
|
21
|
+
if (!url) {
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
const parsed = new URL(url);
|
|
26
|
+
return Boolean(parsed.searchParams.get("display_token") && parsed.searchParams.get("complete_exchange_token"));
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
20
32
|
export function buildCheckoutHandoff(input) {
|
|
21
33
|
const handoff = { url: input.url };
|
|
34
|
+
// mobile_url is only emitted when the link carries the complete token pair;
|
|
35
|
+
// a display-token-only link cannot finish the same-device exchange after payment.
|
|
36
|
+
const mobileUrl = hasCompleteCheckoutTokens(input.mobileUrl) ? input.mobileUrl : undefined;
|
|
37
|
+
if (mobileUrl) {
|
|
38
|
+
handoff.mobile_url = mobileUrl;
|
|
39
|
+
}
|
|
22
40
|
const workBuddyAction = isWorkBuddyPlainChat(input.agentType, input.platform);
|
|
23
41
|
if (workBuddyAction) {
|
|
24
42
|
handoff.agent_action = buildWorkBuddyPresentFilesAction(input.url);
|
|
@@ -40,27 +58,41 @@ export function buildCheckoutHandoff(input) {
|
|
|
40
58
|
}
|
|
41
59
|
return {
|
|
42
60
|
handoff,
|
|
43
|
-
instruction: checkoutHandoffInstruction(input.agentType, input.platform, input.amount),
|
|
61
|
+
instruction: checkoutHandoffInstruction(input.agentType, input.platform, input.amount, Boolean(mobileUrl)),
|
|
44
62
|
};
|
|
45
63
|
}
|
|
46
|
-
function checkoutHandoffInstruction(agentType, platform, amount) {
|
|
64
|
+
function checkoutHandoffInstruction(agentType, platform, amount, hasMobileLink) {
|
|
47
65
|
if (isWorkBuddyPlainChat(agentType, platform)) {
|
|
48
|
-
|
|
66
|
+
const mobileSentence = hasMobileLink
|
|
67
|
+
? ",然后把 handoff.mobile_url 作为可点击链接发给用户,说明“手机端点开可直接跳转支付宝或微信支付;小程序内打不开时复制到手机浏览器打开即可”"
|
|
68
|
+
: "";
|
|
69
|
+
const failLinks = hasMobileLink ? "发送 handoff.url 与 handoff.mobile_url" : "发送原始 handoff.url";
|
|
70
|
+
return `Backend 尚未确认付款。立即严格按 handoff.agent_action.tool 和 handoff.agent_action.arguments 原样执行一次,在右侧打开 handoff.url;确认工具调用成功后说明金额 ${amount}${mobileSentence},然后停止等待。若工具失败,只${failLinks},报告未自动打开并停止。不要用 present_files 打开本地文件或二维码 PNG,不要下载或重建二维码,不要调用 pay,不要创建新 Checkout、Payment Intent 或 Execution。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
49
71
|
}
|
|
50
72
|
if (isZCodePlainChat(agentType, platform)) {
|
|
51
|
-
|
|
73
|
+
const mobileSentence = hasMobileLink
|
|
74
|
+
? ",并把 handoff.mobile_url 发给用户备用(手机端点开可直接跳转支付宝或微信支付)"
|
|
75
|
+
: "";
|
|
76
|
+
const fallbackLinks = hasMobileLink ? "同一个可点击 handoff.url 与 handoff.mobile_url" : "同一个可点击 handoff.url";
|
|
77
|
+
return `Backend 尚未确认付款。立即用 ZCode 内置浏览器打开 handoff.url,让用户完成付款;确认已发起打开后说明金额 ${amount}${mobileSentence},然后停止等待。不要只粘贴文字链接,不要下载、解析或重建二维码,不要创建新 Checkout、Payment Intent 或 Execution。只有内置浏览器明确不可用时,才展示${fallbackLinks}。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
52
78
|
}
|
|
53
79
|
if (agentType?.trim().toLowerCase() === "doubao-work" && platform === "plain_chat") {
|
|
54
|
-
|
|
80
|
+
const directLink = hasMobileLink ? "handoff.mobile_url" : "handoff.url";
|
|
81
|
+
const directLabel = hasMobileLink ? "手机直接打开收银台(可跳转支付宝/微信)" : "手机直接打开收银台";
|
|
82
|
+
return `Backend 尚未确认付款。在当前豆包工作对话中展示两个有标签的官方入口:将 ${directLink} 标为“${directLabel}”,将 handoff.qr_image_url 标为“二维码图片(保存或用另一台设备扫码)”。说明金额 ${amount} 后停止等待。不要解析或单独输出 URL credential,不要下载或重建二维码,不要调用 pay,不要创建新 Checkout、Payment Intent 或 Execution。只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
55
83
|
}
|
|
56
84
|
if (platform === "markdown") {
|
|
57
|
-
|
|
85
|
+
const mobileSentence = hasMobileLink ? ",并附 handoff.mobile_url 供手机端点击直达支付" : "";
|
|
86
|
+
return `Backend 尚未确认付款。把 handoff.markdown 原样发送到当前桌面对话,确认二维码、付款链接和金额 ${amount} 均已实际对用户可见${mobileSentence},然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
58
87
|
}
|
|
59
88
|
if (platform === "terminal") {
|
|
60
|
-
|
|
89
|
+
const mobileSentence = hasMobileLink ? ",并展示 handoff.mobile_url 供手机端点击直达支付" : "";
|
|
90
|
+
return `Backend 尚未确认付款。在用户可见终端展示当前 Checkout 的二维码、handoff.url 和金额 ${amount}${mobileSentence},然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
61
91
|
}
|
|
62
92
|
if (agentType?.trim().toLowerCase() === "openclaw" && platform === "telegram") {
|
|
63
|
-
|
|
93
|
+
const failLinks = hasMobileLink ? "handoff.qr_image_url、金额、handoff.url 与 handoff.mobile_url" : "handoff.qr_image_url、金额和 handoff.url";
|
|
94
|
+
return `Backend 尚未确认付款或授权状态。立即使用 OpenClaw 原生 message tool,严格按 handoff.agent_action.tool 和 handoff.agent_action.arguments 原样执行;它会发送金额 ${amount}、二维码、📱 手机点这儿支付和 📋 已授权给我读。不得改写 presentation、换用其他消息工具、拆开按钮发送或用普通回复声称按钮已经发送。确认 message tool 调用成功后停止等待。收到 callback_data "itp:grant_confirmed:<checkout_id>" 时,表示用户声明已在收银台授权读取;立即执行 next.command 查询同一 Checkout,再只执行 Backend 返回的 next.command 进入同一 Execution 的 grant 流程。该 callback 不证明付款成功或 grant 已生效;Backend 未返回 grant_active 前不得读取或猜测结果。若原生 message tool 明确失败或当前 Telegram 未启用 inline buttons,只发送现有 ${failLinks},报告按钮不可用并停止;不要创建新的 Checkout、Payment Intent 或 Execution。`;
|
|
64
95
|
}
|
|
65
|
-
|
|
96
|
+
const urlList = hasMobileLink ? "handoff.url、handoff.mobile_url(手机端点击直达支付)和可用的 handoff.qr_image_url" : "handoff.url 和可用的 handoff.qr_image_url";
|
|
97
|
+
return `Backend 尚未确认付款。把 ${urlList} 实际发送到当前会话,说明金额 ${amount},然后停止等待。不要创建新 Checkout、Payment Intent 或 Execution;只有用户明确表示已付款或要求查询状态时,才执行 next.command;用户的话不是付款成功证明。`;
|
|
66
98
|
}
|
|
@@ -1500,6 +1500,7 @@ function buildServicesCheckoutEnvelope(response, checkoutURL, plan, agentType, t
|
|
|
1500
1500
|
const presentationHandoff = buildCheckoutHandoff({
|
|
1501
1501
|
platform,
|
|
1502
1502
|
url: plan.linkOnlyURL ?? checkoutURL,
|
|
1503
|
+
mobileUrl: checkoutURL,
|
|
1503
1504
|
amount,
|
|
1504
1505
|
plan,
|
|
1505
1506
|
...(agentType ? { agentType } : {}),
|
package/dist/src/main.js
CHANGED
|
@@ -910,6 +910,9 @@ program
|
|
|
910
910
|
await runCheckoutPresentation(backend, {
|
|
911
911
|
checkoutID,
|
|
912
912
|
displayToken,
|
|
913
|
+
...(snap.lastCheckoutID === checkoutID && snap.lastCheckoutURL
|
|
914
|
+
? { savedCheckoutURL: snap.lastCheckoutURL }
|
|
915
|
+
: {}),
|
|
913
916
|
host,
|
|
914
917
|
...(options.target ? { target: options.target } : {}),
|
|
915
918
|
...(config.agentType ? { agentType: config.agentType } : {}),
|
package/dist/src/state/config.js
CHANGED
|
@@ -15,7 +15,7 @@ import { OperationJournal } from "./operation_journal.js";
|
|
|
15
15
|
export const DEFAULT_BASE_URL = "https://app.itpay.ai";
|
|
16
16
|
export const DEV_BASE_URL = "https://dev.itpay.ai";
|
|
17
17
|
export const SANDBOX_BASE_URL = "https://sandbox.itpay.ai";
|
|
18
|
-
export const CLI_VERSION = "2.1.
|
|
18
|
+
export const CLI_VERSION = "2.1.10";
|
|
19
19
|
export const API_CONTRACT_REVISION = "sha256:66a95bbc5ec9d7b35747cf87206c63dd45983f26de21afab582d8dd63bf198f7";
|
|
20
20
|
const CART_SESSION_DEFAULT_DIR = ".itpay-v3";
|
|
21
21
|
const CART_SESSION_FILENAME = "cart.json";
|
|
@@ -33,17 +33,17 @@
|
|
|
33
33
|
{
|
|
34
34
|
"agent_type": "workbuddy",
|
|
35
35
|
"default_host": "plain-chat",
|
|
36
|
-
"responsibility": "execute handoff.agent_action exactly once to open the fully rendered handoff.url with present_files; show the amount and stop"
|
|
36
|
+
"responsibility": "execute handoff.agent_action exactly once to open the fully rendered handoff.url with present_files, then send handoff.mobile_url as a clickable link that jumps straight into Alipay or WeChat Pay on a phone; show the amount and stop"
|
|
37
37
|
},
|
|
38
38
|
{
|
|
39
39
|
"agent_type": "zcode",
|
|
40
40
|
"default_host": "plain-chat",
|
|
41
|
-
"responsibility": "open handoff.url immediately in the ZCode built-in browser
|
|
41
|
+
"responsibility": "open handoff.url immediately in the ZCode built-in browser, also send handoff.mobile_url as the phone-friendly entry, then stop; when the built-in browser is unavailable show both clickable links"
|
|
42
42
|
},
|
|
43
43
|
{
|
|
44
44
|
"agent_type": "doubao-work",
|
|
45
45
|
"default_host": "plain-chat",
|
|
46
|
-
"responsibility": "show handoff.
|
|
46
|
+
"responsibility": "show handoff.mobile_url as the preferred same-phone checkout or authorization link and handoff.qr_image_url as the labeled save-or-scan fallback; state the amount for Checkout and stop"
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
"agent_type": "kimi-code",
|
|
@@ -73,10 +73,11 @@
|
|
|
73
73
|
"agent_rules": [
|
|
74
74
|
"Agent Type is stable identity; Host is presentation and may be explicitly overridden.",
|
|
75
75
|
"A local QR path is not visible until a desktop Agent attaches or renders that file on the human-facing surface; WorkBuddy never receives a local QR path.",
|
|
76
|
-
"For workbuddy with plain-chat, execute handoff.agent_action exactly once; it calls present_files with handoff.url as the only files element and opens the rendered Card Link in the right-side browser.",
|
|
77
|
-
"For zcode with plain-chat, open handoff.url in the built-in browser.
|
|
78
|
-
"For doubao-work with plain-chat, show two labeled official links: handoff.
|
|
79
|
-
"If the WorkBuddy action fails, send the original handoff.url, report that it did not auto-open, and stop without creating another payment resource.",
|
|
76
|
+
"For workbuddy with plain-chat, execute handoff.agent_action exactly once; it calls present_files with handoff.url as the only files element and opens the rendered Card Link in the right-side browser, then send handoff.mobile_url so phone users can tap straight into Alipay or WeChat Pay.",
|
|
77
|
+
"For zcode with plain-chat, open handoff.url in the built-in browser and also send handoff.mobile_url as the phone entry; do not only paste text, download or rebuild a QR, or invent a private browser-tool payload; when the built-in browser is unavailable show both clickable links.",
|
|
78
|
+
"For doubao-work with plain-chat, show two labeled official links: handoff.mobile_url for direct same-phone opening and handoff.qr_image_url for saving or scanning on another device; do not parse credentials, download or rebuild the QR, or create an alternative resource.",
|
|
79
|
+
"If the WorkBuddy action fails, send the original handoff.url and handoff.mobile_url, report that it did not auto-open, and stop without creating another payment resource.",
|
|
80
|
+
"handoff.mobile_url is the phone-friendly Checkout URL; when the current client cannot open links in place (for example a mobile mini program), send it and tell the human to copy it into the phone browser where it can jump into Alipay or WeChat Pay. It is only returned when the handoff carries the complete token pair; when absent, handoff.url remains the entry.",
|
|
80
81
|
"Do not claim the handoff was shown until both a usable payment image or QR and the Checkout URL are visible.",
|
|
81
82
|
"IM Hosts that require a target must receive --target before Checkout creation. OpenClaw has no default Host; Kimi Code uses terminal.",
|
|
82
83
|
"OpenClaw Telegram uses flat url/value buttons for deployed-version compatibility. A grant_confirmed callback carries only the Checkout ID, never a display token, and never proves payment or an active grant."
|
package/package.json
CHANGED
package/skills/itpay/SKILL.md
CHANGED
|
@@ -76,6 +76,13 @@ platform whenever possible. Only show the same clickable URL when no browser
|
|
|
76
76
|
or native action is available; never ask the human to run a command or rebuild
|
|
77
77
|
a QR code.
|
|
78
78
|
|
|
79
|
+
Checkout handoffs also carry `handoff.mobile_url`: send it alongside the main
|
|
80
|
+
link and tell the human that on a phone it opens the cashier and can jump
|
|
81
|
+
straight into Alipay or WeChat Pay. When the current client cannot open links
|
|
82
|
+
itself (for example a mobile mini program without a side panel), send
|
|
83
|
+
`mobile_url` and tell the human to copy it into the phone's browser if it does
|
|
84
|
+
not open in place.
|
|
85
|
+
|
|
79
86
|
## Serve The Human
|
|
80
87
|
|
|
81
88
|
- Ask the human only to choose, authorize, pay, provide required contact
|