@itpay/cli 0.2.3 → 0.2.4

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
@@ -213,7 +213,7 @@ redacted secure delivery status:
213
213
  ```bash
214
214
  itp buyer catalog search --query 企业工商 --json
215
215
  itp buyer cart create --variant var_itpay_enterprise_fuzzy_search_cny01 --input company_name=阿里 --json
216
- itp buyer checkout create --cart <cart_id> --email buyer@example.com --json
216
+ itp buyer checkout create --cart <cart_id> --email <buyer_email> --json
217
217
  itp buyer payment wait <payment_intent_id> --json
218
218
  itp buyer checkout status <checkout_id> --json
219
219
  ```
@@ -221,7 +221,7 @@ itp buyer checkout status <checkout_id> --json
221
221
  For the one-command buyer helper:
222
222
 
223
223
  ```bash
224
- itp buy var_itpay_enterprise_fuzzy_search_cny01 --email buyer@example.com --input company_name=阿里 --display agent --no-wait-payment --json
224
+ itp buy var_itpay_enterprise_fuzzy_search_cny01 --email <buyer_email> --input company_name=阿里 --display agent --no-wait-payment --json
225
225
  ```
226
226
 
227
227
  For multi-item cart tests:
@@ -239,7 +239,7 @@ 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
- - In agent app clients, send `payment_handoff.markdown` to the human first, then run `payment_handoff.wait_command`.
242
+ - In agent app clients, send `human_visible_markdown` to the human first, then run `next.command`.
243
243
  - Treat only `payment_intent.verified` as payment success.
244
244
 
245
245
  If the human wants the agent to analyze delivered content, the human must reveal
package/bin/itp CHANGED
@@ -93,8 +93,8 @@ async function main() {
93
93
  output({
94
94
  version: VERSION,
95
95
  commands: [
96
- "buy var_pubg_couple_skin_cny20 --sandbox --email buyer@example.com --phone +8613800000000 --display agent --no-wait-payment --json",
97
- "buy var_pubg_couple_skin_cny20 --sandbox --email buyer@example.com --phone +8613800000000 --no-wait --json",
96
+ "buy var_pubg_couple_skin_cny20 --email <buyer_email> --phone +8613800000000 --display agent --no-wait-payment --json",
97
+ "buy var_pubg_couple_skin_cny20 --email <buyer_email> --phone +8613800000000 --no-wait --json",
98
98
  "buyer catalog search --query 企业工商 --category business_data_api --provider itpay_enterprise_data --json",
99
99
  "buyer catalog get --variant var_pubg_couple_skin_cny20 --json",
100
100
  "buyer cart create --variant var_pubg_couple_skin_cny20 --json",
@@ -105,7 +105,7 @@ async function main() {
105
105
  "buyer shelf manifest --json",
106
106
  "buyer shelf snapshot --version <catalog_version> --json",
107
107
  "buyer shelf delta --since <catalog_version> --json",
108
- "buyer checkout create --cart <cart_id> --method alipay --email buyer@example.com --phone +8613800000000 --json",
108
+ "buyer checkout create --cart <cart_id> --method alipay --email <buyer_email> --phone +8613800000000 --json",
109
109
  "buyer checkout status <checkout_id> --json",
110
110
  "buyer checkout resume <checkout_id> --json",
111
111
  "buyer auth status --json",
@@ -68,7 +68,7 @@
68
68
  },
69
69
  {
70
70
  "intent": "do both steps through the high-level command",
71
- "command": "itp buy <variant_id> --sandbox --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json",
71
+ "command": "itp buy <variant_id> --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json",
72
72
  "success_signal": "response.status is waiting_human_auth, waiting_user_payment, payment_verified, or delivery_claimable"
73
73
  }
74
74
  ],
@@ -8,7 +8,7 @@
8
8
  "Checkout returns human_action.kind=auth_qr for the first-purchase auth-to-payment entry.",
9
9
  "PaymentIntent has been created.",
10
10
  "The CLI returned human_action.kind=payment_qr, qr_png_url, preferred_qr_url, or qr_image_url.",
11
- "The human needs to scan with Alipay sandbox."
11
+ "The human needs to scan with the payment provider."
12
12
  ],
13
13
  "required_state": {
14
14
  "needs": ["payment_intent_id", "human_action", "qr_png_url/preferred_qr_url or local_qr_path"],
@@ -17,8 +17,8 @@
17
17
  "commands": [
18
18
  {
19
19
  "intent": "create payment intent through the high-level buy flow",
20
- "command": "itp buy <variant_id> --sandbox --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json",
21
- "success_signal": "response.payment_handoff.markdown and response.payment_handoff.wait_command are present"
20
+ "command": "itp buy <variant_id> --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json",
21
+ "success_signal": "response.human_visible_markdown and response.next.command are present"
22
22
  },
23
23
  {
24
24
  "intent": "recover a scanner order-not-found display problem",
@@ -33,17 +33,17 @@
33
33
  "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.",
34
34
  "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.",
35
35
  "Do not describe auth_qr as payment success or payment proof. Only payment_intent.verified proves payment.",
36
- "In Codex or Claude Code app clients, the default UX is show-then-wait: show response.payment_handoff.markdown first, then run response.payment_handoff.wait_command.",
37
- "For Telegram-style message clients, show response.payment_handoff.markdown and stop unless the human asks you to keep waiting or returns with a payment question.",
36
+ "In Codex or Claude Code app clients, the default UX is show-then-wait: send response.human_visible_markdown to the human first, then run response.next.command.",
37
+ "For Telegram-style message clients, show response.human_visible_markdown and stop unless the human asks you to keep waiting or returns with a payment question.",
38
38
  "MUST show local_qr_path first when present. Many agent clients do not reliably render remote QR images; local_qr_path is the most reliable desktop/chat display artifact.",
39
39
  "If local_qr_path is not present, show qr_png_url or preferred_qr_url as the primary scannable QR. This is an ItPay-hosted human QR image; it may render the native provider payment code for scanner reliability, but the agent must not request or decode the raw provider payload.",
40
40
  "Use qr_image_url/SVG only as a fallback when PNG/local rendering is unavailable.",
41
41
  "For mobile users, present mobile_wallet_url as a clickable payment button when present.",
42
42
  "payment_entry_url is the stable ItPay status/payment page. qr_png_url/local_qr_path is usually the better desktop scanner artifact.",
43
- "In Alipay sandbox, the provider can briefly show order not found even after precreate succeeded. Tell the human to wait 30-60 seconds and retry the same QR/page. Do not create a new checkout, do not create a new payment intent, and do not repeatedly refresh unless the ItPay page or CLI explicitly asks for recovery.",
43
+ "The provider can briefly show order not found even after precreate succeeded. Tell the human to wait 30-60 seconds and retry the same QR/page. Do not create a new checkout, do not create a new payment intent, and do not repeatedly refresh unless the ItPay page or CLI explicitly asks for recovery.",
44
44
  "Opening the payment page must not be treated as paid.",
45
45
  "Do not run payment wait before the QR/link has been sent to the human.",
46
- "After showing QR, run payment_handoff.wait_command or buyer payment wait for the same payment_intent_id."
46
+ "After showing QR, run next.command or buyer payment wait for the same payment_intent_id."
47
47
  ],
48
48
  "forbidden": [
49
49
  "Do not encode payment_entry_url into a new QR yourself when qr_png_url/local_qr_path is available.",
@@ -61,7 +61,7 @@
61
61
  "command": "itp docs show payment-wait --role buyer --json"
62
62
  },
63
63
  {
64
- "condition": "Alipay sandbox says order not found after waiting on the same QR/page",
64
+ "condition": "the payment provider says order not found after waiting on the same QR/page",
65
65
  "topic": "qr-refresh",
66
66
  "command": "itp docs show qr-refresh --role buyer --json"
67
67
  }
@@ -5,7 +5,7 @@
5
5
  "title": "QR Refresh Recovery",
6
6
  "purpose": "Teach the agent how to recover scanner problems without changing payment truth or creating a second order.",
7
7
  "when_to_use": [
8
- "The human reports Alipay sandbox says order not found after waiting 30-60 seconds on the same QR/page.",
8
+ "The human reports the payment provider says order not found after waiting 30-60 seconds on the same QR/page.",
9
9
  "The QR expired or the wallet cannot load it.",
10
10
  "The payment intent is still not verified."
11
11
  ],
@@ -29,7 +29,7 @@
29
29
  "QR refresh is display recovery only. ItPay may return the same QR again when the current provider QR is still valid.",
30
30
  "Use the same payment_intent_id.",
31
31
  "If QR does not render, first use the returned local_qr_path, qr_png_url, or preferred_qr_url. Do not generate a new QR from guessed provider payload.",
32
- "For Alipay sandbox order-not-found, first ask the human to wait and scan/open the same QR/page again; use refresh only after that wait has failed.",
32
+ "For provider order-not-found, first ask the human to wait and scan/open the same QR/page again; use refresh only after that wait has failed.",
33
33
  "If refresh succeeds, show only the returned ItPay QR/display URL and continue waiting on the same payment_intent_id.",
34
34
  "After refresh, continue waiting through the normal wait contract."
35
35
  ],
@@ -36,8 +36,8 @@
36
36
  },
37
37
  {
38
38
  "intent": "buy a selected variant with the high-level cart-first flow",
39
- "command": "itp buy <variant_id> --sandbox --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json",
40
- "success_signal": "response.status becomes waiting_human_auth or waiting_user_payment; if waiting_user_payment, response.payment_handoff contains markdown and wait_command"
39
+ "command": "itp buy <variant_id> --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json",
40
+ "success_signal": "response.status becomes waiting_human_auth or waiting_user_payment; if waiting_user_payment, response.human_visible_markdown and response.next.command are present"
41
41
  }
42
42
  ],
43
43
  "agent_rules": [
@@ -54,8 +54,8 @@
54
54
  "After showing auth_qr, keep running/resuming the same checkout unless the human explicitly asks you to pause. Do not stop merely because a QR was displayed.",
55
55
  "When a buyer command returns buyer_session.status=buyer_session_saved, the CLI has stored the buyer account session for this agent device. You may continue with checkout/payment or `buyer vault` commands without asking the human for a token.",
56
56
  "Vault grants are only for reading approved delivered artifacts. They are not buyer session credentials and do not authorize refunds or account/order management.",
57
- "Use high-level itp buy when possible, but in agent app clients split payment into two visible steps: first return and show payment_handoff, then run payment_handoff.wait_command.",
58
- "If response.payment_handoff.markdown is present, send it to the human immediately in the same user-visible message. It includes the QR image and payment link.",
57
+ "Use high-level itp buy when possible, but in agent app clients split payment into two visible steps: first return and show human_visible_markdown, then run next.command.",
58
+ "If response.human_visible_markdown is present, send it to the human immediately in the same user-visible message. It includes the QR image and payment link.",
59
59
  "Show the returned payment QR exactly as provided before starting payment wait.",
60
60
  "Payment truth comes only from payment_intent.verified.",
61
61
  "Secure delivery goes to the human first; report redacted delivery status only."
package/lib/buyer.js CHANGED
@@ -7,9 +7,10 @@ import { coreApi, coreApiBase } from "./http.js";
7
7
  import { renderHumanAction, renderItPayPaymentAction, shouldReturnAfterAgentTextQR, writeWaitHeartbeat } from "./render-human.js";
8
8
 
9
9
  async function buyerBuy(flags) {
10
+ rejectBuyerSandboxFlag(flags);
10
11
  const showThenWait = shouldReturnAfterAgentTextQR(flags);
11
12
  const selectionID = flags.selection || flags.variant || flags.catalog_variant_id || flags.item || flags.catalog_item_id;
12
- if (!selectionID) throw new Error("catalog variant id is required, for example: itp buy var_pubg_couple_skin_cny20 --sandbox --email buyer@example.com --phone +8613800000000 --json");
13
+ if (!selectionID) throw new Error("catalog variant id is required, for example: itp buy <variant_id> --email <buyer_email> --json");
13
14
  const selection = await resolveBuyerCatalogSelection(selectionID, flags);
14
15
  const cart = await createBuyerCart(selection, flags);
15
16
  let checkout = await createBuyerCheckoutFromCart(cart, selection, flags);
@@ -54,7 +55,7 @@ async function buyerBuy(flags) {
54
55
  next: {
55
56
  command: paymentWaitCommand(intent.payment_intent_id, { short: true }),
56
57
  safe_for_agent: true,
57
- instruction: "Send payment_handoff.markdown to the human first, then run this wait command unless the human explicitly asked you to pause."
58
+ instruction: "Send human_visible_markdown to the human first, then run this wait command unless the human explicitly asked you to pause."
58
59
  }
59
60
  }));
60
61
  return;
@@ -81,6 +82,7 @@ async function buyerBuy(flags) {
81
82
  }
82
83
 
83
84
  async function buyer(command, rest, flags) {
85
+ rejectBuyerSandboxFlag(flags);
84
86
  const subcommand = rest[0] && !String(rest[0]).startsWith("--") ? rest[0] : "";
85
87
  if (command === "catalog") {
86
88
  if (subcommand === "search") {
@@ -326,7 +328,7 @@ async function buyer(command, rest, flags) {
326
328
  : {
327
329
  command: paymentWaitCommand(intent.payment_intent_id, { short: true }),
328
330
  safe_for_agent: true,
329
- instruction: "Send payment_handoff.markdown to the human first, then run this wait command unless the human explicitly asked you to pause."
331
+ instruction: "Send human_visible_markdown to the human first, then run this wait command unless the human explicitly asked you to pause."
330
332
  }
331
333
  }));
332
334
  return;
@@ -349,7 +351,7 @@ async function buyer(command, rest, flags) {
349
351
  next: {
350
352
  command: paymentWaitCommand(intent.payment_intent_id, { short: true }),
351
353
  safe_for_agent: true,
352
- instruction: "Send payment_handoff.markdown to the human first, then run this wait command unless the human explicitly asked you to pause."
354
+ instruction: "Send human_visible_markdown to the human first, then run this wait command unless the human explicitly asked you to pause."
353
355
  }
354
356
  }));
355
357
  return;
@@ -400,7 +402,7 @@ async function buyer(command, rest, flags) {
400
402
  : {
401
403
  command: paymentWaitCommand(refreshed.payment_intent_id, { short: true }),
402
404
  safe_for_agent: true,
403
- instruction: "Send payment_handoff.markdown to the human first, then run this wait command unless the human explicitly asked you to pause."
405
+ instruction: "Send human_visible_markdown to the human first, then run this wait command unless the human explicitly asked you to pause."
404
406
  }
405
407
  }));
406
408
  return;
@@ -822,7 +824,7 @@ async function createBuyerCheckoutFromCart(cart, selection = null, flags = {}) {
822
824
  const cartID = typeof cart === "string" ? cart : (cart?.cart_id || cart?.id);
823
825
  if (!cartID) throw new Error("cart_id is required");
824
826
  const deliveryContact = {};
825
- if (flags.email) deliveryContact.email = flags.email;
827
+ if (flags.email) deliveryContact.email = realBuyerEmail(flags.email);
826
828
  if (flags.phone) deliveryContact.phone = flags.phone;
827
829
  const missing = requiredDeliveryContactFields(selection).filter((field) => !deliveryContact[field]);
828
830
  if (missing.length) {
@@ -861,6 +863,21 @@ function requiredDeliveryContactFields(selection) {
861
863
  return Array.isArray(fields) ? fields.map((field) => String(field).trim()).filter(Boolean) : [];
862
864
  }
863
865
 
866
+ function rejectBuyerSandboxFlag(flags = {}) {
867
+ if (!flags?.sandbox) return;
868
+ throw new Error("--sandbox is not used by buyer commands; environment is selected by the ItPay API base. Run the command without --sandbox.");
869
+ }
870
+
871
+ function realBuyerEmail(value) {
872
+ const email = String(value || "").trim();
873
+ if (!email) return "";
874
+ const domain = email.split("@").pop().toLowerCase();
875
+ if (["example.com", "example.net", "example.org"].includes(domain)) {
876
+ throw new Error("placeholder email rejected; ask the human for their real delivery email before checkout.");
877
+ }
878
+ return email;
879
+ }
880
+
864
881
  async function createBuyerPaymentIntent(checkoutID, flags = {}) {
865
882
  if (!checkoutID) throw new Error("checkout_id is required");
866
883
  const method = String(flags.method || flags.payment_method || "alipay").toLowerCase();
@@ -1367,7 +1384,7 @@ async function refreshBuyerPaymentQR(paymentIntentID, flags = {}) {
1367
1384
  if (intent.status === "verified") return intent;
1368
1385
  const refreshURL = intent.qr_refresh_url;
1369
1386
  if (!refreshURL) {
1370
- throw new Error("payment intent does not expose qr_refresh_url; refresh is supported only for refreshable Alipay sandbox QR intents");
1387
+ throw new Error("payment intent does not expose qr_refresh_url; refresh is supported only for refreshable provider QR intents");
1371
1388
  }
1372
1389
  return await coreApi(refreshURL, {
1373
1390
  method: "POST",
@@ -1436,10 +1453,11 @@ function isBuyerDeliveryComplete(result) {
1436
1453
  }
1437
1454
 
1438
1455
  function buyerRunOutput(value = {}) {
1456
+ const body = compactBuyerOutput(value);
1439
1457
  return normalizeBuyerMoneyFields(stripInternalBuyerFields({
1440
1458
  schema_version: "itp.buyer.v1",
1459
+ ...body,
1441
1460
  docs: value.docs || buyerDocsFor(value),
1442
- ...value,
1443
1461
  secrets: {
1444
1462
  raw_content_included: false,
1445
1463
  claim_token_included: false,
@@ -1448,6 +1466,158 @@ function buyerRunOutput(value = {}) {
1448
1466
  }));
1449
1467
  }
1450
1468
 
1469
+ function compactBuyerOutput(value = {}) {
1470
+ const result = {};
1471
+ if (value.status !== undefined) result.status = value.status;
1472
+ if (value.payment_handoff?.markdown) {
1473
+ result.must_reply_to_human_before_next_command = true;
1474
+ result.human_visible_markdown = value.payment_handoff.markdown;
1475
+ }
1476
+ for (const [key, item] of Object.entries(value)) {
1477
+ if (["docs", "status"].includes(key)) continue;
1478
+ result[key] = compactBuyerField(key, item);
1479
+ }
1480
+ return result;
1481
+ }
1482
+
1483
+ function compactBuyerField(key, value) {
1484
+ if (key === "selection") return compactSelection(value);
1485
+ if (key === "selections") return Array.isArray(value) ? value.map(compactSelection) : value;
1486
+ if (key === "product") return compactProduct(value);
1487
+ if (key === "products") return Array.isArray(value) ? value.map(compactProduct) : value;
1488
+ if (key === "cart") return compactCart(value);
1489
+ if (key === "checkout") return compactCheckout(value);
1490
+ if (key === "payment_intent") return compactPaymentIntent(value);
1491
+ return value;
1492
+ }
1493
+
1494
+ function compactSelection(selection = {}) {
1495
+ if (!selection || typeof selection !== "object") return selection;
1496
+ return compactObject({
1497
+ catalog_item_id: selection.catalog_item_id,
1498
+ catalog_variant_id: selection.catalog_variant_id,
1499
+ ucp_variant_id: selection.ucp_variant_id,
1500
+ offer_id: selection.offer_id,
1501
+ title: selection.title,
1502
+ variant_title: selection.variant_title,
1503
+ amount: selection.expected_amount,
1504
+ currency: selection.currency,
1505
+ required_contact_fields: selection.required_contact_fields,
1506
+ purchasable: selection.purchasable
1507
+ });
1508
+ }
1509
+
1510
+ function compactProduct(product = {}) {
1511
+ if (!product || typeof product !== "object") return product;
1512
+ return compactObject({
1513
+ id: product.id,
1514
+ title: product.title,
1515
+ description: product.description,
1516
+ selected: product.selected,
1517
+ variants: Array.isArray(product.variants) ? product.variants.map(compactVariant) : undefined
1518
+ });
1519
+ }
1520
+
1521
+ function compactVariant(variant = {}) {
1522
+ const metadata = variant.metadata || {};
1523
+ return compactObject({
1524
+ id: variant.id,
1525
+ title: variant.title,
1526
+ description: variant.description,
1527
+ price: variant.price,
1528
+ availability: variant.availability,
1529
+ required_profile_fields: metadata["ai.itpay.required_profile_fields"],
1530
+ input_schema_json: metadata["ai.itpay.api.input_schema_json"],
1531
+ agent_explanation_zh: metadata["ai.itpay.agent_explanation_zh"]
1532
+ });
1533
+ }
1534
+
1535
+ function compactCart(cart = {}) {
1536
+ if (!cart || typeof cart !== "object") return cart;
1537
+ return compactObject({
1538
+ cart_id: cart.cart_id || cart.id,
1539
+ status: cart.status,
1540
+ amount: cart.amount,
1541
+ currency: cart.currency,
1542
+ line_items: Array.isArray(cart.line_items) ? cart.line_items.map(compactCartLine) : undefined
1543
+ });
1544
+ }
1545
+
1546
+ function compactCartLine(line = {}) {
1547
+ return compactObject({
1548
+ id: line.id,
1549
+ quantity: line.quantity,
1550
+ amount: line.amount,
1551
+ currency: line.currency,
1552
+ input: line.input,
1553
+ item: line.item ? compactObject({
1554
+ id: line.item.id,
1555
+ title: line.item.title,
1556
+ catalog_item_id: line.item.catalog_item_id,
1557
+ catalog_variant_id: line.item.catalog_variant_id,
1558
+ offer_id: line.item.offer_id
1559
+ }) : undefined
1560
+ });
1561
+ }
1562
+
1563
+ function compactCheckout(checkout = {}) {
1564
+ if (!checkout || typeof checkout !== "object") return checkout;
1565
+ return compactObject({
1566
+ checkout_id: checkout.checkout_id,
1567
+ cart_id: checkout.cart_id,
1568
+ order_id: checkout.order_id,
1569
+ status: checkout.status,
1570
+ delivery_status: checkout.delivery_status,
1571
+ identity_status: checkout.identity_status,
1572
+ next_required_action: checkout.next_required_action,
1573
+ payment_intent_id: checkout.payment_intent_id,
1574
+ amount: checkout.amount,
1575
+ currency: checkout.currency,
1576
+ delivery: checkout.delivery,
1577
+ human_action: checkout.human_action,
1578
+ agent_next_actions: checkout.agent_next_actions
1579
+ });
1580
+ }
1581
+
1582
+ function compactPaymentIntent(intent = {}) {
1583
+ if (!intent || typeof intent !== "object") return intent;
1584
+ return compactObject({
1585
+ payment_intent_id: intent.payment_intent_id,
1586
+ payment_attempt_id: intent.payment_attempt_id,
1587
+ checkout_id: intent.checkout_id,
1588
+ order_id: intent.order_id,
1589
+ status: intent.status,
1590
+ amount: intent.amount,
1591
+ currency: intent.currency,
1592
+ human_action: compactHumanAction(intent.human_action),
1593
+ qr_png_url: intent.qr_png_url,
1594
+ qr_image_url: intent.qr_image_url,
1595
+ mobile_wallet_url: intent.mobile_wallet_url,
1596
+ agent_wait: intent.agent_wait,
1597
+ agent_next_actions: intent.agent_next_actions
1598
+ });
1599
+ }
1600
+
1601
+ function compactHumanAction(action = null) {
1602
+ if (!action || typeof action !== "object") return action;
1603
+ return compactObject({
1604
+ kind: action.kind,
1605
+ url: action.url,
1606
+ local_qr_path: action.local_qr_path,
1607
+ local_qr_mime: action.local_qr_mime,
1608
+ qr_png_url: action.qr_png_url,
1609
+ qr_image_url: action.qr_image_url,
1610
+ preferred_qr_url: action.preferred_qr_url,
1611
+ mobile_wallet_url: action.mobile_wallet_url,
1612
+ agent_display_hint: action.agent_display_hint,
1613
+ expires_at: action.expires_at
1614
+ });
1615
+ }
1616
+
1617
+ function compactObject(value = {}) {
1618
+ return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined && item !== null));
1619
+ }
1620
+
1451
1621
  function normalizeBuyerMoneyFields(value) {
1452
1622
  if (Array.isArray(value)) return value.map((item) => normalizeBuyerMoneyFields(item));
1453
1623
  if (!value || typeof value !== "object") return value;
package/lib/runtime.js CHANGED
@@ -448,7 +448,7 @@ async function maybeMockApproveDeviceAuth(authId, flags) {
448
448
  return;
449
449
  }
450
450
  if (!fakeTestingAllowed(flags)) {
451
- throw new Error("mock approval is developer-only and disabled for agent runs; use real Alipay sandbox authentication");
451
+ throw new Error("mock approval is developer-only and disabled for agent runs; use real payment-provider authentication");
452
452
  }
453
453
  const alipayUserId = flags.alipay_user_id || process.env.ITPAY_MOCK_ALIPAY_USER_ID || `2088${crypto.randomInt(100000000000, 999999999999)}`;
454
454
  await api(`/api/itp/auth/device/${encodeURIComponent(authId)}/mock-approve`, {
@@ -692,10 +692,10 @@ async function createCheckoutResult(flags) {
692
692
 
693
693
  function validateLivePaymentFlags(method, flags = {}) {
694
694
  if (String(method).toLowerCase() === "fake" && !fakeTestingAllowed(flags)) {
695
- throw new Error("fake payment is developer-only and disabled for agent runs; use --method alipay for local, sandbox, and live testing");
695
+ throw new Error("fake payment is developer-only and disabled for agent runs; use --method alipay for local and live testing");
696
696
  }
697
697
  if ((flags.mock_approve || process.env.ITPAY_MOCK_APPROVE === "true" || process.env.ITPAY_MOCK_APPROVE === "1") && !fakeTestingAllowed(flags)) {
698
- throw new Error("mock approval is developer-only and disabled for agent runs; use real Alipay sandbox authentication");
698
+ throw new Error("mock approval is developer-only and disabled for agent runs; use real payment-provider authentication");
699
699
  }
700
700
  }
701
701
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itpay/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.4",
4
4
  "description": "ItPay CLI, buyer skill, and agent-readable docs for agent-native commerce.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -59,7 +59,7 @@ read this skill
59
59
  The high-level command can wrap this flow:
60
60
 
61
61
  ```bash
62
- itp buy <variant_id> --sandbox --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json
62
+ itp buy <variant_id> --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json
63
63
  ```
64
64
 
65
65
  For step-by-step testing:
@@ -93,7 +93,7 @@ itp buyer cart create --variant var_itpay_enterprise_fuzzy_search_cny01 --input
93
93
  itp buyer cart show <cart_id> --json
94
94
  itp buyer cart add <cart_id> --variant var_itpay_enterprise_fuzzy_search_cny01 --input company_name=美团 --json
95
95
  itp buyer cart create --variant var_itpay_enterprise_precise_lookup_cny05 --input company_name_or_credit_no=北京京东世纪贸易有限公司 --json
96
- itp buy var_itpay_enterprise_fuzzy_search_cny01 --sandbox --email <buyer_email> --input company_name=京东 --display agent --no-wait-payment --json
96
+ itp buy var_itpay_enterprise_fuzzy_search_cny01 --email <buyer_email> --input company_name=京东 --display agent --no-wait-payment --json
97
97
  ```
98
98
 
99
99
  For cart edits, always read the server cart first with `buyer cart show`.
@@ -206,14 +206,14 @@ Each docs page includes `next_docs`. Follow those links as the state changes.
206
206
  For payment creation in an agent/chat client, prefer:
207
207
 
208
208
  ```bash
209
- itp buy <variant_id> --sandbox --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json
209
+ itp buy <variant_id> --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json
210
210
  ```
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
214
  Codex or Claude Code app clients, prefer `--no-wait-payment`: send
215
- `payment_handoff.markdown` to the human first, then run
216
- `payment_handoff.wait_command`. If the human is on mobile, present
215
+ `human_visible_markdown` to the human first, then run
216
+ `next.command`. If the human is on mobile, present
217
217
  `mobile_wallet_url` as a clickable human-only fallback; do not convert it into a
218
218
  QR.
219
219
 
@@ -221,8 +221,8 @@ For first-purchase auth, treat the returned ItPay authorization entry as a
221
221
  single human orchestration entry. It may open Alipay login/registration first
222
222
  and then payment after ItPay receives the OAuth callback. Do not call
223
223
  `oauth_start_url` directly unless the ItPay auth page asks for fallback.
224
- If the payment page says the Alipay sandbox entry is stabilizing/preparing,
225
- or if Alipay sandbox says "order not found", tell the human to wait 30-60
224
+ If the payment page says provider entry is stabilizing/preparing,
225
+ or if the payment provider says "order not found", tell the human to wait 30-60
226
226
  seconds and use the same page/QR again. Do not ask them to refresh repeatedly,
227
227
  and do not create another checkout or payment intent. Use
228
228
  `itp buyer payment refresh-qr ... --reason order-not-found` only after the
@@ -238,7 +238,7 @@ I found the service and selected the matching variant.
238
238
  I created the cart and checkout.
239
239
  Please open the returned ItPay authorization link and approve Alipay login.
240
240
  I am waiting for ItPay account authorization.
241
- Please scan the returned ItPay-hosted QR image with Alipay sandbox.
241
+ Please scan the returned ItPay-hosted QR image with the payment provider.
242
242
  I am waiting for ItPay payment verification.
243
243
  Payment is verified.
244
244
  Delivery is claimable by the human buyer. Please check your email.