@itpay/cli 0.2.1 → 0.2.2

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 CHANGED
@@ -158,6 +158,19 @@ Before starting a new purchase, agents should inspect recoverable local state:
158
158
  itp status --refresh --json
159
159
  ```
160
160
 
161
+ Humans can use the default account overview:
162
+
163
+ ```bash
164
+ itp status
165
+ ```
166
+
167
+ ```text
168
+ Account: buyer_7xK2mP9vQ4
169
+ Linked: alipay, wechat
170
+ Orders: 12
171
+ Device: Codex on MacBook-Pro (active)
172
+ ```
173
+
161
174
  If an unfinished run exists, continue it:
162
175
 
163
176
  ```bash
@@ -0,0 +1,157 @@
1
+ import { coreApi } from "./http.js";
2
+ import { noRecoverableContext } from "./buyer.js";
3
+ import { cliCommand, output, readConfig, readCredentials, readSessionToken, safeErrorMessage } from "./env.js";
4
+
5
+ async function accountOverviewStatus(flags = {}) {
6
+ const config = readConfig();
7
+ const credentials = readCredentials();
8
+ const hasLocalSession = Boolean(readSessionToken(credentials) || flags.access_token);
9
+ const base = {
10
+ schema_version: "itp.agent.v1",
11
+ status: hasLocalSession ? "account_status_unavailable" : "unauthenticated",
12
+ phase: hasLocalSession ? "account_status_unavailable" : "unauthenticated",
13
+ authenticated: false,
14
+ session_verified: !hasLocalSession,
15
+ auth_source: hasLocalSession ? "local_files_unverified" : "local_files",
16
+ account_id: config.account_id || null,
17
+ buyer_account_id: config.account_id || null,
18
+ device_id: config.device_id || null,
19
+ agent_device_id: config.device_id || null,
20
+ linked_providers: [],
21
+ order_count: 0,
22
+ created_at: null,
23
+ device: config.device_id ? { agent_device_id: config.device_id } : null,
24
+ next: hasLocalSession
25
+ ? { type: "retry_status", command: cliCommand("status", "--json"), safe_for_agent: true }
26
+ : { type: "start_auth", command: cliCommand("setup", "--plan", "credit-300", "--method", "alipay", "--json"), safe_for_agent: true },
27
+ recoverable_context: noRecoverableContext(),
28
+ agent_next_actions: hasLocalSession
29
+ ? ["run_status_refresh", "view_orders", "create_refund_if_needed", "list_agent_read_grants"]
30
+ : ["start_human_auth_if_unauthenticated"],
31
+ secrets: { raw_key_included: false, session_token_included: false }
32
+ };
33
+ if (!hasLocalSession) return base;
34
+
35
+ try {
36
+ const me = await coreApi("/v1/me", { method: "GET" }, flags);
37
+ const authenticated = Boolean(me.authenticated);
38
+ const accountID = me.buyer_account_id || config.account_id || null;
39
+ const deviceID = me.agent_device_id || me.device?.agent_device_id || config.device_id || null;
40
+ return {
41
+ ...base,
42
+ status: authenticated ? "idle" : "unauthenticated",
43
+ phase: authenticated ? "idle" : "unauthenticated",
44
+ authenticated,
45
+ session_verified: true,
46
+ auth_source: "core",
47
+ account_id: accountID,
48
+ buyer_account_id: accountID,
49
+ device_id: deviceID,
50
+ agent_device_id: deviceID,
51
+ linked_providers: Array.isArray(me.linked_providers) ? me.linked_providers : [],
52
+ order_count: Number.isFinite(Number(me.order_count)) ? Number(me.order_count) : 0,
53
+ created_at: me.created_at || null,
54
+ device: me.device || (deviceID ? { agent_device_id: deviceID } : null),
55
+ next: authenticated
56
+ ? { type: "buyer_ready", command: cliCommand("buyer", "auth", "status", "--json"), safe_for_agent: true }
57
+ : base.next,
58
+ agent_next_actions: authenticated
59
+ ? ["search_catalog", "view_orders", "create_refund_if_needed", "list_agent_read_grants"]
60
+ : ["start_human_auth_if_unauthenticated"],
61
+ backend: {
62
+ environment: me.environment || null,
63
+ backend_version: me.backend_version || null
64
+ }
65
+ };
66
+ } catch (error) {
67
+ return {
68
+ ...base,
69
+ error: safeErrorMessage(error)
70
+ };
71
+ }
72
+ }
73
+
74
+ function outputAccountStatus(status, flags = {}) {
75
+ if (flags.json) {
76
+ output(status);
77
+ return;
78
+ }
79
+ console.log(formatStatusText(status));
80
+ }
81
+
82
+ function formatStatusText(status = {}) {
83
+ if (!status.authenticated) {
84
+ const lines = [
85
+ `Account: ${status.account_id ? `${status.account_id} (unverified)` : "not signed in"}`,
86
+ "Linked: -",
87
+ "Orders: -",
88
+ `Device: ${formatDeviceLine(status.device, status.device_id) || "-"}`
89
+ ];
90
+ if (status.error) lines.push(`Error: ${status.error}`);
91
+ return lines.join("\n");
92
+ }
93
+ const linked = Array.isArray(status.linked_providers) && status.linked_providers.length
94
+ ? status.linked_providers.join(", ")
95
+ : "-";
96
+ return [
97
+ `Account: ${status.buyer_account_id || status.account_id || "-"}`,
98
+ `Linked: ${linked}`,
99
+ `Orders: ${Number.isFinite(Number(status.order_count)) ? Number(status.order_count) : 0}`,
100
+ `Device: ${formatDeviceLine(status.device, status.device_id) || "-"}`
101
+ ].join("\n");
102
+ }
103
+
104
+ function formatDeviceLine(device, fallbackID = "") {
105
+ const displayName = String(device?.display_name || "").trim();
106
+ const deviceID = String(device?.agent_device_id || fallbackID || "").trim();
107
+ const label = displayName || deviceID;
108
+ if (!label) return "";
109
+ const status = String(device?.status || "").trim();
110
+ return status ? `${label} (${status})` : label;
111
+ }
112
+
113
+ async function refreshBuyerSessionForStatus(flags = {}) {
114
+ const config = readConfig();
115
+ const credentials = readCredentials();
116
+ if (!readSessionToken(credentials)) {
117
+ return {
118
+ authenticated: false,
119
+ session_verified: true,
120
+ account_id: config.account_id || null,
121
+ device_id: config.device_id || null,
122
+ auth_source: "core_no_session"
123
+ };
124
+ }
125
+ try {
126
+ const status = await coreApi("/v1/me/auth/status", { method: "GET" }, flags);
127
+ return {
128
+ authenticated: true,
129
+ session_verified: true,
130
+ account_id: status.buyer_account_id || config.account_id || null,
131
+ device_id: config.device_id || null,
132
+ account_status: status.account_status || null,
133
+ auth_source: "core"
134
+ };
135
+ } catch (error) {
136
+ return {
137
+ authenticated: false,
138
+ session_verified: true,
139
+ account_id: config.account_id || null,
140
+ device_id: config.device_id || null,
141
+ auth_source: "core",
142
+ error: safeErrorMessage(error)
143
+ };
144
+ }
145
+ }
146
+
147
+ function nextActionForAuthStatus(auth = {}, flags = {}) {
148
+ if (auth.authenticated && auth.session_verified === false) {
149
+ return { type: "verify_buyer_session", command: cliCommand("status", "--refresh", "--json"), safe_for_agent: true };
150
+ }
151
+ if (auth.authenticated) {
152
+ return { type: "buyer_ready", command: cliCommand("buyer", "auth", "status", "--json"), safe_for_agent: true };
153
+ }
154
+ return { type: "start_auth", command: cliCommand("setup", "--plan", "credit-300", "--method", "alipay", "--json"), safe_for_agent: true };
155
+ }
156
+
157
+ export { accountOverviewStatus, outputAccountStatus, refreshBuyerSessionForStatus, nextActionForAuthStatus };
@@ -8,7 +8,7 @@ import { apiTimeoutMs, cliCommand, commandExists, mergeRun, readRun, readState,
8
8
  async function renderItPayPaymentAction(intent, flags = {}) {
9
9
  const action = intent?.human_action ? { ...intent.human_action } : (intent?.payment_url ? {
10
10
  id: intent.payment_intent_id,
11
- title: "Scan with Alipay",
11
+ title: "Scan payment QR",
12
12
  url: intent.payment_url,
13
13
  expires_at: intent.qr?.expires_at
14
14
  } : null);
@@ -23,7 +23,7 @@ async function renderItPayPaymentAction(intent, flags = {}) {
23
23
  if (action && (intent?.qr_image_url || intent?.qr?.image_url)) {
24
24
  action.qr_image_url = intent.qr_image_url || intent.qr.image_url;
25
25
  action.display_mode = intent.qr?.scan_mode || "itpay_entry_qr";
26
- action.description = action.description || "Scan the ItPay payment entry QR; ItPay will safely hand off to Alipay.";
26
+ action.description = action.description || "Scan the ItPay payment entry QR; ItPay will safely hand off to the payment provider.";
27
27
  }
28
28
  const result = await renderHumanAction(action, flags);
29
29
  if (intent && action) {
@@ -39,7 +39,7 @@ function humanActionSummaryLines(action) {
39
39
  if (!action?.url) return [];
40
40
  const lines = [
41
41
  "ITP HUMAN ACTION REQUIRED",
42
- `Title: ${action.title || "Scan with Alipay"}`,
42
+ `Title: ${humanActionTitle(action)}`,
43
43
  `URL: ${action.url}`
44
44
  ];
45
45
  if (action.local_qr_path) {
@@ -55,7 +55,7 @@ function humanActionSummaryLines(action) {
55
55
  lines.push(`Mobile wallet link: ${action.mobile_wallet_url}`);
56
56
  }
57
57
  if (action.oauth_start_url) {
58
- lines.push(`Alipay auth fallback: ${action.oauth_start_url}`);
58
+ lines.push(`${humanActionProviderLabel(action)} auth fallback: ${action.oauth_start_url}`);
59
59
  }
60
60
  if (action.fallback_text && !action.fallback_text.includes(action.url)) {
61
61
  lines.push(`Fallback: ${action.fallback_text}`);
@@ -89,7 +89,7 @@ function writeWaitHeartbeat({ kind, idName, idValue, status, action, lastHeartbe
89
89
  action?.qr_png_url ? `QR PNG: ${action.qr_png_url}` : null,
90
90
  action?.qr_image_url ? `QR image: ${action.qr_image_url}` : null,
91
91
  action?.mobile_wallet_url ? `Mobile wallet link: ${action.mobile_wallet_url}` : null,
92
- action?.oauth_start_url ? `Alipay auth fallback: ${action.oauth_start_url}` : null,
92
+ action?.oauth_start_url ? `${humanActionProviderLabel(action)} auth fallback: ${action.oauth_start_url}` : null,
93
93
  command ? `Resume command: ${command}` : null
94
94
  ].filter(Boolean);
95
95
  process.stderr.write(`\n${lines.join("\n")}\n\n`);
@@ -147,9 +147,29 @@ async function renderHumanAction(action, flags = {}) {
147
147
  }
148
148
 
149
149
  const renderResult = { rendered: false, mode, outputs: [] };
150
+ const terminalQRAllowed = shouldRenderTerminalQR(host, mode);
151
+ const providerLabel = humanActionProviderLabel(action);
152
+ const actionTitle = humanActionTitle(action);
150
153
  writeHumanActionSummary(action);
154
+
155
+ if ((mode === "auto" || mode === "terminal") && terminalQRAllowed) {
156
+ const qr = await QRCode.toString(action.url, {
157
+ type: terminalQRType(flags, host),
158
+ small: true,
159
+ errorCorrectionLevel: "M"
160
+ });
161
+ process.stderr.write(`\n${actionTitle}\n`);
162
+ if (action.description) process.stderr.write(`${action.description}\n`);
163
+ process.stderr.write(`${qr}\n`);
164
+ process.stderr.write(`${providerLabel} action URL: ${action.url}\n`);
165
+ if (action.expires_at) process.stderr.write(`Expires at: ${formatActionTime(action.expires_at)}\n`);
166
+ renderResult.rendered = true;
167
+ renderResult.outputs.push("terminal");
168
+ return renderResult;
169
+ }
170
+
151
171
  if ((mode === "auto" || mode === "browser") && shouldOpenBrowser(flags)) {
152
- if (openBrowser(action.mobile_wallet_url || qrImageURL || action.url)) {
172
+ if (openBrowser(qrImageURL || action.mobile_wallet_url || action.url)) {
153
173
  renderResult.rendered = true;
154
174
  renderResult.outputs.push("browser");
155
175
  }
@@ -162,7 +182,7 @@ async function renderHumanAction(action, flags = {}) {
162
182
  await downloadQRImage(qrImageURL, file, flags);
163
183
  action.local_qr_path = file;
164
184
  action.local_qr_mime = qrMimeType(qrImageURL, action);
165
- process.stderr.write(`Alipay QR image: ${file}\n`);
185
+ process.stderr.write(`${providerLabel} QR image: ${file}\n`);
166
186
  renderResult.rendered = true;
167
187
  renderResult.outputs.push(file);
168
188
  if (mode === "file") return renderResult;
@@ -173,44 +193,42 @@ async function renderHumanAction(action, flags = {}) {
173
193
  if (localPath) {
174
194
  attachAgentLocalQR(action, localPath);
175
195
  annotateHumanActionPresentation(action, "");
176
- process.stderr.write(`Alipay action QR image: ${localPath}\n`);
196
+ process.stderr.write(`${providerLabel} action QR image: ${localPath}\n`);
177
197
  renderResult.rendered = true;
178
198
  renderResult.outputs.push(localPath);
179
199
  if (mode === "file") return renderResult;
180
200
  } else {
181
- process.stderr.write(`No QR image available. Open action URL: ${action.url}\n`);
201
+ process.stderr.write(`No branded QR image available. Open action URL: ${action.url}\n`);
182
202
  }
183
203
  }
184
204
  }
185
205
 
186
206
  if (qrImageURL) {
187
- process.stderr.write(`Alipay QR image URL: ${qrImageURL}\n`);
207
+ process.stderr.write(`${providerLabel} QR image URL: ${qrImageURL}\n`);
188
208
  if (action.mobile_wallet_url) process.stderr.write(`Mobile wallet link: ${action.mobile_wallet_url}\n`);
189
209
  renderResult.outputs.push("preferred_qr_url");
190
210
  return renderResult;
191
211
  }
192
212
 
193
- if ((mode === "auto" || mode === "terminal") && shouldRenderTerminalQR(host)) {
194
- const qr = await QRCode.toString(action.url, {
195
- type: terminalQRType(flags, host),
196
- small: true,
197
- errorCorrectionLevel: "M"
198
- });
199
- process.stderr.write(`\n${action.title || "Scan with Alipay"}\n`);
200
- if (action.description) process.stderr.write(`${action.description}\n`);
201
- process.stderr.write(`${qr}\n`);
202
- process.stderr.write(`Alipay action URL: ${action.url}\n`);
203
- if (action.expires_at) process.stderr.write(`Expires at: ${formatActionTime(action.expires_at)}\n`);
204
- renderResult.rendered = true;
205
- renderResult.outputs.push("terminal");
206
- return renderResult;
207
- }
208
-
209
- process.stderr.write(`${action.fallback_text || `Open Alipay URL: ${action.url}`}\n`);
213
+ process.stderr.write(`${action.fallback_text || `Open ${providerLabel} URL: ${action.url}`}\n`);
210
214
  renderResult.outputs.push("url");
211
215
  return renderResult;
212
216
  }
213
217
 
218
+ function humanActionTitle(action = {}) {
219
+ if (action.title) return action.title;
220
+ if (action.kind === "auth_qr") return `${humanActionProviderLabel(action)} authentication`;
221
+ return "Scan payment QR";
222
+ }
223
+
224
+ function humanActionProviderLabel(action = {}) {
225
+ const raw = String(action.provider || action.channel || action.display_mode || "").toLowerCase();
226
+ if (raw.includes("alipay")) return "Alipay";
227
+ if (raw.includes("wechat")) return "WeChat Pay";
228
+ if (raw.includes("fake") || raw.includes("local")) return "ItPay local";
229
+ return "Alipay or WeChat Pay";
230
+ }
231
+
214
232
  function preferredHumanActionQRURL(action) {
215
233
  return action?.qr_png_url ||
216
234
  humanActionPresentationURL(action, "qr_png_url") ||
@@ -416,16 +434,22 @@ function persistHumanAction(action, flags = {}) {
416
434
  writeRun(mergeRun(run, { human_action: action }));
417
435
  }
418
436
 
419
- function shouldRenderTerminalQR(host) {
437
+ function shouldRenderTerminalQR(host, mode = "auto") {
438
+ if (["terminal", "tty"].includes(mode)) return true;
439
+ if (mode && !["auto", ""].includes(mode)) return false;
420
440
  if (process.stderr.isTTY) return true;
421
- return ["gemini", "gemini-cli"].includes(host);
441
+ return terminalQRHostAliases().has(String(host || "").toLowerCase());
442
+ }
443
+
444
+ function terminalQRHostAliases() {
445
+ return new Set(["terminal", "tty", "bash", "zsh", "sh", "shell", "iterm", "iterm2"]);
422
446
  }
423
447
 
424
448
  function terminalQRType(flags = {}, host = "") {
425
449
  const requested = String(flags.qr_format || process.env.ITP_QR_FORMAT || "").toLowerCase();
426
450
  if (requested === "unicode" || requested === "utf8") return "utf8";
427
451
  if (requested === "ansi" || requested === "terminal") return "terminal";
428
- if (["gemini", "gemini-cli"].includes(host)) return "utf8";
452
+ if (terminalQRHostAliases().has(String(host || "").toLowerCase())) return "utf8";
429
453
  return "terminal";
430
454
  }
431
455
 
package/lib/runtime.js CHANGED
@@ -7,6 +7,7 @@ import {
7
7
  recoverableBuyerContextForStatus, recoverableIntentCheckGuidance
8
8
  } from "./buyer.js";
9
9
  import { renderHumanAction, shouldReturnAfterAgentTextQR, writeWaitHeartbeat } from "./render-human.js";
10
+ import { accountOverviewStatus, nextActionForAuthStatus, outputAccountStatus, refreshBuyerSessionForStatus } from "./account-status.js";
10
11
  import {
11
12
  CREDENTIALS_PATH, CONFIG_DIR, RUNS_DIR, VERSION, apiBase, apiTimeoutMs, appendURLQuery, booleanFlag, cliCommand, commandExists,
12
13
  cryptoRandom, csvValues, currentExecutable, deleteGrantCredential, deleteSessionCredential, detectNativeCredentialStore,
@@ -131,14 +132,14 @@ async function setup(flags) {
131
132
  grant_id: checkout.grant_id || run.grant?.grant_id || null
132
133
  },
133
134
  human_action: checkout.human_action || null,
134
- safe_summary: checkout.grant_id ? "Payment verified and grant is ready." : "Waiting for Alipay payment scan."
135
+ safe_summary: checkout.grant_id ? "Payment verified and grant is ready." : "Waiting for Alipay or WeChat Pay payment scan."
135
136
  });
136
137
  writeRun(run);
137
138
 
138
139
  if (checkout.human_action) {
139
140
  await renderHumanAction(checkout.human_action, setupFlags);
140
141
  } else if (checkout.payment?.cashier_url) {
141
- process.stderr.write(`Open Alipay payment URL: ${checkout.payment.cashier_url}\n`);
142
+ process.stderr.write(`Open Alipay or WeChat Pay payment URL: ${checkout.payment.cashier_url}\n`);
142
143
  }
143
144
 
144
145
  if (!checkout.grant_id && (setupFlags.no_wait_payment || setupFlags.no_wait)) {
@@ -263,40 +264,7 @@ async function agentStatus(flags) {
263
264
  const runId = flags.run_id || readState().current_run_id;
264
265
  const run = readRun(runId);
265
266
  if (!run) {
266
- const config = readConfig();
267
- const credentials = readCredentials();
268
- const hasLocalSession = Boolean(readSessionToken(credentials));
269
- const auth = flags.refresh
270
- ? await refreshBuyerSessionForStatus(flags)
271
- : {
272
- authenticated: hasLocalSession,
273
- session_verified: false,
274
- account_id: config.account_id || null,
275
- device_id: config.device_id || null,
276
- auth_source: hasLocalSession ? "local_files_unverified" : "local_files"
277
- };
278
- output({
279
- schema_version: "itp.agent.v1",
280
- status: auth.authenticated
281
- ? (auth.session_verified === false ? "local_session_unverified" : "idle")
282
- : "unauthenticated",
283
- phase: auth.authenticated ? "idle" : "unauthenticated",
284
- authenticated: Boolean(auth.authenticated),
285
- session_verified: auth.session_verified !== false,
286
- auth_source: auth.auth_source,
287
- account_id: auth.account_id || null,
288
- device_id: auth.device_id || null,
289
- next: nextActionForAuthStatus(auth, flags),
290
- recoverable_context: noRecoverableContext(),
291
- agent_next_actions: auth.authenticated && auth.session_verified !== false
292
- ? ["search_catalog", "view_orders", "create_refund_if_needed", "list_agent_read_grants"]
293
- : ["run_status_refresh", "start_human_auth_if_unauthenticated"],
294
- note: auth.authenticated && auth.session_verified === false
295
- ? "Local buyer session exists but the server has not verified it. Run the next.command before order/refund/account operations."
296
- : undefined,
297
- error: auth.error || undefined,
298
- secrets: { raw_key_included: false, session_token_included: false }
299
- });
267
+ outputAccountStatus(await accountOverviewStatus(flags), flags);
300
268
  return;
301
269
  }
302
270
  const refreshed = flags.refresh ? await refreshRun(run, flags) : run;
@@ -306,50 +274,6 @@ async function agentStatus(flags) {
306
274
  }));
307
275
  }
308
276
 
309
- async function refreshBuyerSessionForStatus(flags = {}) {
310
- const config = readConfig();
311
- const credentials = readCredentials();
312
- if (!readSessionToken(credentials)) {
313
- return {
314
- authenticated: false,
315
- session_verified: true,
316
- account_id: config.account_id || null,
317
- device_id: config.device_id || null,
318
- auth_source: "core_no_session"
319
- };
320
- }
321
- try {
322
- const status = await coreApi("/v1/me/auth/status", { method: "GET" }, flags);
323
- return {
324
- authenticated: true,
325
- session_verified: true,
326
- account_id: status.buyer_account_id || config.account_id || null,
327
- device_id: config.device_id || null,
328
- account_status: status.account_status || null,
329
- auth_source: "core"
330
- };
331
- } catch (error) {
332
- return {
333
- authenticated: false,
334
- session_verified: true,
335
- account_id: config.account_id || null,
336
- device_id: config.device_id || null,
337
- auth_source: "core",
338
- error: safeErrorMessage(error)
339
- };
340
- }
341
- }
342
-
343
- function nextActionForAuthStatus(auth = {}, flags = {}) {
344
- if (auth.authenticated && auth.session_verified === false) {
345
- return { type: "verify_buyer_session", command: cliCommand("status", "--refresh", "--json"), safe_for_agent: true };
346
- }
347
- if (auth.authenticated) {
348
- return { type: "buyer_ready", command: cliCommand("buyer", "auth", "status", "--json"), safe_for_agent: true };
349
- }
350
- return { type: "start_auth", command: cliCommand("setup", "--plan", "credit-300", "--method", "alipay", "--json"), safe_for_agent: true };
351
- }
352
-
353
277
  async function resume(flags) {
354
278
  const runId = flags.run_id || readState().current_run_id;
355
279
  const run = readRun(runId);
@@ -761,7 +685,7 @@ async function createCheckoutResult(flags) {
761
685
  grant_id: response.grant_id || currentRun?.grant?.grant_id || null
762
686
  },
763
687
  human_action: response.human_action || null,
764
- safe_summary: response.grant_id ? "Payment verified and grant is ready." : "Waiting for Alipay payment scan."
688
+ safe_summary: response.grant_id ? "Payment verified and grant is ready." : "Waiting for Alipay or WeChat Pay payment scan."
765
689
  }, flags);
766
690
  return response;
767
691
  }
@@ -827,7 +751,7 @@ async function paymentWaitResult(checkoutId, flags) {
827
751
  }
828
752
  }
829
753
  lastHeartbeatAt = writeWaitHeartbeat({
830
- kind: "Alipay payment",
754
+ kind: "Alipay or WeChat Pay payment",
831
755
  idName: "checkout_id",
832
756
  idValue: checkoutId,
833
757
  status: response.status || "waiting",
@@ -1376,7 +1300,7 @@ async function refreshRun(run, flags = {}) {
1376
1300
  payment: { provider: next.payment_method, status: checkout.status },
1377
1301
  grant: { ...(next.grant || {}), grant_id: checkout.grant_id || next.grant?.grant_id || null },
1378
1302
  human_action: checkout.human_action || null,
1379
- safe_summary: checkout.grant_id ? "Payment verified and grant is ready." : checkout.status === "waiting_user_payment" ? "Waiting for Alipay payment scan." : `Checkout status: ${checkout.status}`
1303
+ safe_summary: checkout.grant_id ? "Payment verified and grant is ready." : checkout.status === "waiting_user_payment" ? "Waiting for Alipay or WeChat Pay payment scan." : `Checkout status: ${checkout.status}`
1380
1304
  });
1381
1305
  } catch (error) {
1382
1306
  next = mergeRun(next, { last_error: safeErrorMessage(error), safe_summary: "Could not refresh checkout status." });
@@ -1498,7 +1422,7 @@ function appendPassthroughFlag(args, flags, key, flagName = key) {
1498
1422
 
1499
1423
  function safeUserMessageForRun(run) {
1500
1424
  if (run.phase === "waiting_human_auth") return "Please scan the Alipay authentication QR. I will continue automatically after approval.";
1501
- if (run.phase === "waiting_human_payment") return "Please scan the Alipay payment QR. I will continue automatically after payment is verified.";
1425
+ if (run.phase === "waiting_human_payment") return "Please scan the Alipay or WeChat Pay payment QR. I will continue automatically after payment is verified.";
1502
1426
  if (run.grant?.installed) return "Payment verified. The API credential is stored locally.";
1503
1427
  return run.safe_summary || "ITPay setup is in progress.";
1504
1428
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itpay/cli",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "ItPay CLI, buyer skill, and agent-readable docs for agent-native commerce.",
5
5
  "type": "module",
6
6
  "bin": {