@itpay/cli 0.2.3 → 0.2.5
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 +4 -3
- package/bin/itp +3 -3
- package/docs/agent/buyer/cart-checkout.json +2 -2
- package/docs/agent/buyer/payment-qr.json +10 -9
- package/docs/agent/buyer/payment-wait.json +3 -2
- package/docs/agent/buyer/qr-refresh.json +2 -2
- package/docs/agent/buyer/quickstart.json +5 -4
- package/lib/buyer.js +248 -41
- package/lib/runtime.js +3 -3
- package/package.json +1 -1
- package/skills/itpay-buyer/SKILL.md +13 -8
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
|
|
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
|
|
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,8 @@ 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 `
|
|
242
|
+
- In agent app clients, send `human_visible_markdown` to the human first, then run `after_human_visible_markdown.command`.
|
|
243
|
+
- If status is `payment_handoff_required`, `next` is the human reply step, not payment wait.
|
|
243
244
|
- Treat only `payment_intent.verified` as payment success.
|
|
244
245
|
|
|
245
246
|
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 --
|
|
97
|
-
"buy var_pubg_couple_skin_cny20 --
|
|
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
|
|
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,8 +68,8 @@
|
|
|
68
68
|
},
|
|
69
69
|
{
|
|
70
70
|
"intent": "do both steps through the high-level command",
|
|
71
|
-
"command": "itp buy <variant_id> --
|
|
72
|
-
"success_signal": "response.status is waiting_human_auth,
|
|
71
|
+
"command": "itp buy <variant_id> --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json",
|
|
72
|
+
"success_signal": "response.status is waiting_human_auth, payment_handoff_required, payment_verified, or delivery_claimable"
|
|
73
73
|
}
|
|
74
74
|
],
|
|
75
75
|
"agent_rules": [
|
|
@@ -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
|
|
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> --
|
|
21
|
-
"success_signal": "response.
|
|
20
|
+
"command": "itp buy <variant_id> --email <buyer_email> --phone <buyer_phone> --display agent --no-wait-payment --json",
|
|
21
|
+
"success_signal": "response.status is payment_handoff_required; response.human_visible_markdown is present; response.after_human_visible_markdown.command is the wait command to run after showing the QR/link"
|
|
22
22
|
},
|
|
23
23
|
{
|
|
24
24
|
"intent": "recover a scanner order-not-found display problem",
|
|
@@ -33,17 +33,18 @@
|
|
|
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:
|
|
37
|
-
"For Telegram-style message clients, show response.
|
|
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.after_human_visible_markdown.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
|
+
"When response.status is payment_handoff_required, response.next is the human reply step, not payment wait.",
|
|
38
39
|
"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
40
|
"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
41
|
"Use qr_image_url/SVG only as a fallback when PNG/local rendering is unavailable.",
|
|
41
42
|
"For mobile users, present mobile_wallet_url as a clickable payment button when present.",
|
|
42
43
|
"payment_entry_url is the stable ItPay status/payment page. qr_png_url/local_qr_path is usually the better desktop scanner artifact.",
|
|
43
|
-
"
|
|
44
|
+
"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
45
|
"Opening the payment page must not be treated as paid.",
|
|
45
|
-
"Do not run payment wait before the QR/link has been sent to the human.",
|
|
46
|
-
"After showing QR, run
|
|
46
|
+
"Do not run payment wait before the QR/link has been sent to the human. If you accidentally do, CLI returns payment_handoff_required instead of blocking.",
|
|
47
|
+
"After showing QR, run after_human_visible_markdown.command or buyer payment wait <payment_intent_id> --qr-shown for the same payment_intent_id."
|
|
47
48
|
],
|
|
48
49
|
"forbidden": [
|
|
49
50
|
"Do not encode payment_entry_url into a new QR yourself when qr_png_url/local_qr_path is available.",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
"command": "itp docs show payment-wait --role buyer --json"
|
|
62
63
|
},
|
|
63
64
|
{
|
|
64
|
-
"condition": "
|
|
65
|
+
"condition": "the payment provider says order not found after waiting on the same QR/page",
|
|
65
66
|
"topic": "qr-refresh",
|
|
66
67
|
"command": "itp docs show qr-refresh --role buyer --json"
|
|
67
68
|
}
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"commands": [
|
|
18
18
|
{
|
|
19
19
|
"intent": "wait for payment verified event",
|
|
20
|
-
"command": "itp buyer payment wait <payment_intent_id> --json",
|
|
20
|
+
"command": "itp buyer payment wait <payment_intent_id> --qr-shown --json",
|
|
21
21
|
"success_signal": "response.payment_event.event_type == payment_intent.verified"
|
|
22
22
|
},
|
|
23
23
|
{
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
}
|
|
28
28
|
],
|
|
29
29
|
"agent_rules": [
|
|
30
|
-
"Start payment wait only after the QR image/link has already been sent to the human.",
|
|
30
|
+
"Start payment wait only after the QR image/link has already been sent to the human; use --qr-shown to confirm that step is complete.",
|
|
31
|
+
"If you run payment wait without --qr-shown while payment is still pending, CLI returns payment_handoff_required and human_visible_markdown instead of long-polling.",
|
|
31
32
|
"Payment wait is a recovery loop around the same payment_intent_id, not a one-shot command.",
|
|
32
33
|
"wait.timeout is a heartbeat, not a payment failure.",
|
|
33
34
|
"In Codex or Claude Code app clients, after showing QR, wait online for a short visible cycle first. If the human still has not paid, explain that the same QR/link remains valid and pause for their reply.",
|
|
@@ -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
|
|
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
|
|
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> --
|
|
40
|
-
"success_signal": "response.status becomes waiting_human_auth or
|
|
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 payment_handoff_required; if payment_handoff_required, send response.human_visible_markdown to the human before running response.after_human_visible_markdown.command"
|
|
41
41
|
}
|
|
42
42
|
],
|
|
43
43
|
"agent_rules": [
|
|
@@ -54,8 +54,9 @@
|
|
|
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
|
|
58
|
-
"If response.
|
|
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 after_human_visible_markdown.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
|
+
"If response.status is payment_handoff_required, do not run payment wait from next.command; next is the human-visible reply. Only after the QR/link is visible may you run after_human_visible_markdown.command.",
|
|
59
60
|
"Show the returned payment QR exactly as provided before starting payment wait.",
|
|
60
61
|
"Payment truth comes only from payment_intent.verified.",
|
|
61
62
|
"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
|
|
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);
|
|
@@ -44,18 +45,15 @@ async function buyerBuy(flags) {
|
|
|
44
45
|
|
|
45
46
|
if (flags.no_wait || flags.no_wait_payment || showThenWait) {
|
|
46
47
|
output(buyerRunOutput({
|
|
47
|
-
status: "
|
|
48
|
+
status: "payment_handoff_required",
|
|
48
49
|
selection,
|
|
49
50
|
cart,
|
|
50
51
|
checkout,
|
|
51
52
|
payment_intent: intent,
|
|
52
53
|
payment_handoff: paymentHandoff(intent),
|
|
53
|
-
agent_next_actions:
|
|
54
|
-
next:
|
|
55
|
-
|
|
56
|
-
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
|
-
}
|
|
54
|
+
agent_next_actions: paymentHandoffAgentNextActions(),
|
|
55
|
+
next: paymentHandoffNext(),
|
|
56
|
+
after_human_visible_markdown: paymentHandoffAfter(intent)
|
|
59
57
|
}));
|
|
60
58
|
return;
|
|
61
59
|
}
|
|
@@ -81,6 +79,7 @@ async function buyerBuy(flags) {
|
|
|
81
79
|
}
|
|
82
80
|
|
|
83
81
|
async function buyer(command, rest, flags) {
|
|
82
|
+
rejectBuyerSandboxFlag(flags);
|
|
84
83
|
const subcommand = rest[0] && !String(rest[0]).startsWith("--") ? rest[0] : "";
|
|
85
84
|
if (command === "catalog") {
|
|
86
85
|
if (subcommand === "search") {
|
|
@@ -315,19 +314,16 @@ async function buyer(command, rest, flags) {
|
|
|
315
314
|
const intent = await getBuyerPaymentIntent(checkout.payment_intent_id, flags);
|
|
316
315
|
await renderItPayPaymentAction(intent, flags);
|
|
317
316
|
output(buyerRunOutput({
|
|
318
|
-
status: intent.status === "verified" ? "payment_verified" : "
|
|
317
|
+
status: intent.status === "verified" ? "payment_verified" : "payment_handoff_required",
|
|
319
318
|
checkout,
|
|
320
319
|
payment_intent: intent,
|
|
321
320
|
payment_handoff: intent.status === "verified" ? undefined : paymentHandoff(intent),
|
|
322
321
|
buyer_session: buyerSessionClaimStatus(claimedSession),
|
|
323
|
-
agent_next_actions: intent.agent_next_actions || checkout.agent_next_actions || ["
|
|
322
|
+
agent_next_actions: intent.status === "verified" ? (intent.agent_next_actions || checkout.agent_next_actions || ["poll_checkout"]) : paymentHandoffAgentNextActions(),
|
|
324
323
|
next: intent.status === "verified"
|
|
325
324
|
? { command: cliCommand("buyer", "checkout", "status", checkout.checkout_id, "--json"), safe_for_agent: true }
|
|
326
|
-
:
|
|
327
|
-
|
|
328
|
-
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."
|
|
330
|
-
}
|
|
325
|
+
: paymentHandoffNext(),
|
|
326
|
+
after_human_visible_markdown: intent.status === "verified" ? undefined : paymentHandoffAfter(intent)
|
|
331
327
|
}));
|
|
332
328
|
return;
|
|
333
329
|
}
|
|
@@ -341,16 +337,13 @@ async function buyer(command, rest, flags) {
|
|
|
341
337
|
const intent = await createBuyerPaymentIntent(checkout.checkout_id, flags);
|
|
342
338
|
await renderItPayPaymentAction(intent, flags);
|
|
343
339
|
output(buyerRunOutput({
|
|
344
|
-
status: "
|
|
340
|
+
status: "payment_handoff_required",
|
|
345
341
|
checkout,
|
|
346
342
|
payment_intent: intent,
|
|
347
343
|
payment_handoff: paymentHandoff(intent),
|
|
348
|
-
agent_next_actions:
|
|
349
|
-
next:
|
|
350
|
-
|
|
351
|
-
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."
|
|
353
|
-
}
|
|
344
|
+
agent_next_actions: paymentHandoffAgentNextActions(),
|
|
345
|
+
next: paymentHandoffNext(),
|
|
346
|
+
after_human_visible_markdown: paymentHandoffAfter(intent)
|
|
354
347
|
}));
|
|
355
348
|
return;
|
|
356
349
|
}
|
|
@@ -369,6 +362,19 @@ async function buyer(command, rest, flags) {
|
|
|
369
362
|
const paymentIntentID = flags.payment_intent || flags.payment_intent_id || positional(rest, 1) || readState().last_core_payment_intent_id;
|
|
370
363
|
if (!paymentIntentID) throw new Error("payment_intent_id is required");
|
|
371
364
|
const intent = await getBuyerPaymentIntent(paymentIntentID, flags);
|
|
365
|
+
if (intent.status !== "verified" && !paymentQRShown(flags)) {
|
|
366
|
+
await renderItPayPaymentAction(intent, flags);
|
|
367
|
+
output(buyerRunOutput({
|
|
368
|
+
status: "payment_handoff_required",
|
|
369
|
+
payment_intent: intent,
|
|
370
|
+
payment_handoff: paymentHandoff(intent),
|
|
371
|
+
payment_guidance: paymentRecoveryGuidance(intent, { event_type: "payment_display_required" }),
|
|
372
|
+
agent_next_actions: paymentHandoffAgentNextActions(),
|
|
373
|
+
next: paymentHandoffNext(),
|
|
374
|
+
after_human_visible_markdown: paymentHandoffAfter(intent)
|
|
375
|
+
}));
|
|
376
|
+
return;
|
|
377
|
+
}
|
|
372
378
|
const event = intent.status === "verified"
|
|
373
379
|
? { event_type: "payment_intent.verified", payment_intent_id: paymentIntentID, agent_next_actions: intent.agent_next_actions || ["poll_checkout"] }
|
|
374
380
|
: await waitBuyerPayment(intent, flags);
|
|
@@ -380,7 +386,7 @@ async function buyer(command, rest, flags) {
|
|
|
380
386
|
agent_next_actions: event.agent_next_actions || intent.agent_next_actions || paymentAgentNextActions(intent, event),
|
|
381
387
|
next: event.event_type === "payment_intent.verified"
|
|
382
388
|
? { command: intent.checkout_id ? cliCommand("buyer", "checkout", "status", intent.checkout_id, "--json") : undefined, safe_for_agent: true }
|
|
383
|
-
: { command:
|
|
389
|
+
: { command: paymentWaitCommand(paymentIntentID, { qrShown: true }), safe_for_agent: true }
|
|
384
390
|
}));
|
|
385
391
|
return;
|
|
386
392
|
}
|
|
@@ -390,18 +396,15 @@ async function buyer(command, rest, flags) {
|
|
|
390
396
|
const refreshed = await refreshBuyerPaymentQR(paymentIntentID, flags);
|
|
391
397
|
await renderItPayPaymentAction(refreshed, flags);
|
|
392
398
|
output(buyerRunOutput({
|
|
393
|
-
status: refreshed.status === "verified" ? "payment_verified" : "
|
|
399
|
+
status: refreshed.status === "verified" ? "payment_verified" : "payment_handoff_required",
|
|
394
400
|
payment_intent: refreshed,
|
|
395
401
|
payment_handoff: refreshed.status === "verified" ? undefined : paymentHandoff(refreshed),
|
|
396
402
|
payment_guidance: paymentRecoveryGuidance(refreshed, { event_type: refreshed.status === "verified" ? "payment_intent.verified" : "qr_refreshed" }),
|
|
397
|
-
agent_next_actions: refreshed.agent_next_actions || paymentAgentNextActions(refreshed, { event_type:
|
|
403
|
+
agent_next_actions: refreshed.status === "verified" ? (refreshed.agent_next_actions || paymentAgentNextActions(refreshed, { event_type: "payment_intent.verified" })) : paymentHandoffAgentNextActions(),
|
|
398
404
|
next: refreshed.status === "verified"
|
|
399
405
|
? { command: cliCommand("buyer", "checkout", "status", refreshed.checkout_id, "--json"), safe_for_agent: true }
|
|
400
|
-
:
|
|
401
|
-
|
|
402
|
-
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."
|
|
404
|
-
}
|
|
406
|
+
: paymentHandoffNext(),
|
|
407
|
+
after_human_visible_markdown: refreshed.status === "verified" ? undefined : paymentHandoffAfter(refreshed)
|
|
405
408
|
}));
|
|
406
409
|
return;
|
|
407
410
|
}
|
|
@@ -822,7 +825,7 @@ async function createBuyerCheckoutFromCart(cart, selection = null, flags = {}) {
|
|
|
822
825
|
const cartID = typeof cart === "string" ? cart : (cart?.cart_id || cart?.id);
|
|
823
826
|
if (!cartID) throw new Error("cart_id is required");
|
|
824
827
|
const deliveryContact = {};
|
|
825
|
-
if (flags.email) deliveryContact.email = flags.email;
|
|
828
|
+
if (flags.email) deliveryContact.email = realBuyerEmail(flags.email);
|
|
826
829
|
if (flags.phone) deliveryContact.phone = flags.phone;
|
|
827
830
|
const missing = requiredDeliveryContactFields(selection).filter((field) => !deliveryContact[field]);
|
|
828
831
|
if (missing.length) {
|
|
@@ -861,6 +864,21 @@ function requiredDeliveryContactFields(selection) {
|
|
|
861
864
|
return Array.isArray(fields) ? fields.map((field) => String(field).trim()).filter(Boolean) : [];
|
|
862
865
|
}
|
|
863
866
|
|
|
867
|
+
function rejectBuyerSandboxFlag(flags = {}) {
|
|
868
|
+
if (!flags?.sandbox) return;
|
|
869
|
+
throw new Error("--sandbox is not used by buyer commands; environment is selected by the ItPay API base. Run the command without --sandbox.");
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function realBuyerEmail(value) {
|
|
873
|
+
const email = String(value || "").trim();
|
|
874
|
+
if (!email) return "";
|
|
875
|
+
const domain = email.split("@").pop().toLowerCase();
|
|
876
|
+
if (["example.com", "example.net", "example.org"].includes(domain)) {
|
|
877
|
+
throw new Error("placeholder email rejected; ask the human for their real delivery email before checkout.");
|
|
878
|
+
}
|
|
879
|
+
return email;
|
|
880
|
+
}
|
|
881
|
+
|
|
864
882
|
async function createBuyerPaymentIntent(checkoutID, flags = {}) {
|
|
865
883
|
if (!checkoutID) throw new Error("checkout_id is required");
|
|
866
884
|
const method = String(flags.method || flags.payment_method || "alipay").toLowerCase();
|
|
@@ -1035,7 +1053,7 @@ function paymentHandoff(intent = {}) {
|
|
|
1035
1053
|
const preferredQRURL = action.preferred_qr_url || qrPNGURL || action.qr_image_url || intent.qr_image_url || intent.qr?.image_url || "";
|
|
1036
1054
|
const localQRPath = action.local_qr_path || intent.local_qr_path || "";
|
|
1037
1055
|
const mobileWalletURL = action.mobile_wallet_url || intent.mobile_wallet_url || "";
|
|
1038
|
-
const waitCommand = paymentWaitCommand(paymentIntentID, { short: true });
|
|
1056
|
+
const waitCommand = paymentWaitCommand(paymentIntentID, { short: true, qrShown: true });
|
|
1039
1057
|
return {
|
|
1040
1058
|
type: "payment_qr_handoff",
|
|
1041
1059
|
payment_intent_id: paymentIntentID || null,
|
|
@@ -1049,7 +1067,8 @@ function paymentHandoff(intent = {}) {
|
|
|
1049
1067
|
markdown: paymentHandoffMarkdown({ localQRPath, qrPNGURL, preferredQRURL, entryURL, mobileWalletURL }),
|
|
1050
1068
|
wait_command: waitCommand || null,
|
|
1051
1069
|
safe_for_agent: true,
|
|
1052
|
-
instruction: "Show markdown to the human first. Then run wait_command and wait for payment_intent.verified unless the human asked you to pause."
|
|
1070
|
+
instruction: "Show markdown to the human first. Then run wait_command and wait for payment_intent.verified unless the human asked you to pause.",
|
|
1071
|
+
display_targets: paymentHandoffDisplayTargets()
|
|
1053
1072
|
};
|
|
1054
1073
|
}
|
|
1055
1074
|
|
|
@@ -1063,11 +1082,46 @@ function paymentHandoffMarkdown({ localQRPath = "", qrPNGURL = "", preferredQRUR
|
|
|
1063
1082
|
return lines.join("\n\n");
|
|
1064
1083
|
}
|
|
1065
1084
|
|
|
1066
|
-
function paymentWaitCommand(paymentIntentID, { short = false } = {}) {
|
|
1085
|
+
function paymentWaitCommand(paymentIntentID, { short = false, qrShown = false } = {}) {
|
|
1067
1086
|
if (!paymentIntentID) return "";
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1087
|
+
const args = ["buyer", "payment", "wait", paymentIntentID];
|
|
1088
|
+
if (short) args.push("--timeout", "60");
|
|
1089
|
+
if (qrShown) args.push("--qr-shown");
|
|
1090
|
+
args.push("--json");
|
|
1091
|
+
return cliCommand(...args);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
function paymentQRShown(flags = {}) {
|
|
1095
|
+
if (flags.qr_shown === undefined) return false;
|
|
1096
|
+
return booleanFlag(flags.qr_shown);
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
function paymentHandoffAgentNextActions() {
|
|
1100
|
+
return ["show_human_visible_markdown"];
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
function paymentHandoffNext() {
|
|
1104
|
+
return {
|
|
1105
|
+
type: "reply_to_human",
|
|
1106
|
+
safe_for_agent: false,
|
|
1107
|
+
instruction: "Send human_visible_markdown to the human now. Do not run payment wait until this message is visible to the human."
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
function paymentHandoffAfter(intent = {}) {
|
|
1112
|
+
return {
|
|
1113
|
+
command: paymentWaitCommand(intent.payment_intent_id, { short: true, qrShown: true }),
|
|
1114
|
+
safe_for_agent: true,
|
|
1115
|
+
instruction: "Run this only after human_visible_markdown has been sent to the human."
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
|
|
1119
|
+
function paymentHandoffDisplayTargets() {
|
|
1120
|
+
return {
|
|
1121
|
+
desktop_app: ["local_qr_path", "qr_png_url", "payment_entry_url"],
|
|
1122
|
+
terminal: ["qr_png_url", "payment_entry_url", "mobile_wallet_url"],
|
|
1123
|
+
chat_cloud: ["qr_png_url", "payment_entry_url", "mobile_wallet_url"]
|
|
1124
|
+
};
|
|
1071
1125
|
}
|
|
1072
1126
|
|
|
1073
1127
|
function paymentRecoveryGuidance(intent = {}, event = {}) {
|
|
@@ -1367,7 +1421,7 @@ async function refreshBuyerPaymentQR(paymentIntentID, flags = {}) {
|
|
|
1367
1421
|
if (intent.status === "verified") return intent;
|
|
1368
1422
|
const refreshURL = intent.qr_refresh_url;
|
|
1369
1423
|
if (!refreshURL) {
|
|
1370
|
-
throw new Error("payment intent does not expose qr_refresh_url; refresh is supported only for refreshable
|
|
1424
|
+
throw new Error("payment intent does not expose qr_refresh_url; refresh is supported only for refreshable provider QR intents");
|
|
1371
1425
|
}
|
|
1372
1426
|
return await coreApi(refreshURL, {
|
|
1373
1427
|
method: "POST",
|
|
@@ -1409,7 +1463,7 @@ async function waitBuyerPayment(intent, flags = {}) {
|
|
|
1409
1463
|
action: intent?.human_action || null,
|
|
1410
1464
|
lastHeartbeatAt,
|
|
1411
1465
|
flags,
|
|
1412
|
-
command:
|
|
1466
|
+
command: paymentWaitCommand(paymentIntentID, { qrShown: true })
|
|
1413
1467
|
});
|
|
1414
1468
|
}
|
|
1415
1469
|
return lastEvent || { event_type: "wait.timeout", payment_intent_id: paymentIntentID, cursor, agent_next_actions: ["wait_payment"] };
|
|
@@ -1436,10 +1490,11 @@ function isBuyerDeliveryComplete(result) {
|
|
|
1436
1490
|
}
|
|
1437
1491
|
|
|
1438
1492
|
function buyerRunOutput(value = {}) {
|
|
1493
|
+
const body = compactBuyerOutput(value);
|
|
1439
1494
|
return normalizeBuyerMoneyFields(stripInternalBuyerFields({
|
|
1440
1495
|
schema_version: "itp.buyer.v1",
|
|
1496
|
+
...body,
|
|
1441
1497
|
docs: value.docs || buyerDocsFor(value),
|
|
1442
|
-
...value,
|
|
1443
1498
|
secrets: {
|
|
1444
1499
|
raw_content_included: false,
|
|
1445
1500
|
claim_token_included: false,
|
|
@@ -1448,6 +1503,158 @@ function buyerRunOutput(value = {}) {
|
|
|
1448
1503
|
}));
|
|
1449
1504
|
}
|
|
1450
1505
|
|
|
1506
|
+
function compactBuyerOutput(value = {}) {
|
|
1507
|
+
const result = {};
|
|
1508
|
+
if (value.status !== undefined) result.status = value.status;
|
|
1509
|
+
if (value.payment_handoff?.markdown) {
|
|
1510
|
+
result.must_reply_to_human_before_next_command = true;
|
|
1511
|
+
result.human_visible_markdown = value.payment_handoff.markdown;
|
|
1512
|
+
}
|
|
1513
|
+
for (const [key, item] of Object.entries(value)) {
|
|
1514
|
+
if (["docs", "status"].includes(key)) continue;
|
|
1515
|
+
result[key] = compactBuyerField(key, item);
|
|
1516
|
+
}
|
|
1517
|
+
return result;
|
|
1518
|
+
}
|
|
1519
|
+
|
|
1520
|
+
function compactBuyerField(key, value) {
|
|
1521
|
+
if (key === "selection") return compactSelection(value);
|
|
1522
|
+
if (key === "selections") return Array.isArray(value) ? value.map(compactSelection) : value;
|
|
1523
|
+
if (key === "product") return compactProduct(value);
|
|
1524
|
+
if (key === "products") return Array.isArray(value) ? value.map(compactProduct) : value;
|
|
1525
|
+
if (key === "cart") return compactCart(value);
|
|
1526
|
+
if (key === "checkout") return compactCheckout(value);
|
|
1527
|
+
if (key === "payment_intent") return compactPaymentIntent(value);
|
|
1528
|
+
return value;
|
|
1529
|
+
}
|
|
1530
|
+
|
|
1531
|
+
function compactSelection(selection = {}) {
|
|
1532
|
+
if (!selection || typeof selection !== "object") return selection;
|
|
1533
|
+
return compactObject({
|
|
1534
|
+
catalog_item_id: selection.catalog_item_id,
|
|
1535
|
+
catalog_variant_id: selection.catalog_variant_id,
|
|
1536
|
+
ucp_variant_id: selection.ucp_variant_id,
|
|
1537
|
+
offer_id: selection.offer_id,
|
|
1538
|
+
title: selection.title,
|
|
1539
|
+
variant_title: selection.variant_title,
|
|
1540
|
+
amount: selection.expected_amount,
|
|
1541
|
+
currency: selection.currency,
|
|
1542
|
+
required_contact_fields: selection.required_contact_fields,
|
|
1543
|
+
purchasable: selection.purchasable
|
|
1544
|
+
});
|
|
1545
|
+
}
|
|
1546
|
+
|
|
1547
|
+
function compactProduct(product = {}) {
|
|
1548
|
+
if (!product || typeof product !== "object") return product;
|
|
1549
|
+
return compactObject({
|
|
1550
|
+
id: product.id,
|
|
1551
|
+
title: product.title,
|
|
1552
|
+
description: product.description,
|
|
1553
|
+
selected: product.selected,
|
|
1554
|
+
variants: Array.isArray(product.variants) ? product.variants.map(compactVariant) : undefined
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
function compactVariant(variant = {}) {
|
|
1559
|
+
const metadata = variant.metadata || {};
|
|
1560
|
+
return compactObject({
|
|
1561
|
+
id: variant.id,
|
|
1562
|
+
title: variant.title,
|
|
1563
|
+
description: variant.description,
|
|
1564
|
+
price: variant.price,
|
|
1565
|
+
availability: variant.availability,
|
|
1566
|
+
required_profile_fields: metadata["ai.itpay.required_profile_fields"],
|
|
1567
|
+
input_schema_json: metadata["ai.itpay.api.input_schema_json"],
|
|
1568
|
+
agent_explanation_zh: metadata["ai.itpay.agent_explanation_zh"]
|
|
1569
|
+
});
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
function compactCart(cart = {}) {
|
|
1573
|
+
if (!cart || typeof cart !== "object") return cart;
|
|
1574
|
+
return compactObject({
|
|
1575
|
+
cart_id: cart.cart_id || cart.id,
|
|
1576
|
+
status: cart.status,
|
|
1577
|
+
amount: cart.amount,
|
|
1578
|
+
currency: cart.currency,
|
|
1579
|
+
line_items: Array.isArray(cart.line_items) ? cart.line_items.map(compactCartLine) : undefined
|
|
1580
|
+
});
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
function compactCartLine(line = {}) {
|
|
1584
|
+
return compactObject({
|
|
1585
|
+
id: line.id,
|
|
1586
|
+
quantity: line.quantity,
|
|
1587
|
+
amount: line.amount,
|
|
1588
|
+
currency: line.currency,
|
|
1589
|
+
input: line.input,
|
|
1590
|
+
item: line.item ? compactObject({
|
|
1591
|
+
id: line.item.id,
|
|
1592
|
+
title: line.item.title,
|
|
1593
|
+
catalog_item_id: line.item.catalog_item_id,
|
|
1594
|
+
catalog_variant_id: line.item.catalog_variant_id,
|
|
1595
|
+
offer_id: line.item.offer_id
|
|
1596
|
+
}) : undefined
|
|
1597
|
+
});
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
function compactCheckout(checkout = {}) {
|
|
1601
|
+
if (!checkout || typeof checkout !== "object") return checkout;
|
|
1602
|
+
return compactObject({
|
|
1603
|
+
checkout_id: checkout.checkout_id,
|
|
1604
|
+
cart_id: checkout.cart_id,
|
|
1605
|
+
order_id: checkout.order_id,
|
|
1606
|
+
status: checkout.status,
|
|
1607
|
+
delivery_status: checkout.delivery_status,
|
|
1608
|
+
identity_status: checkout.identity_status,
|
|
1609
|
+
next_required_action: checkout.next_required_action,
|
|
1610
|
+
payment_intent_id: checkout.payment_intent_id,
|
|
1611
|
+
amount: checkout.amount,
|
|
1612
|
+
currency: checkout.currency,
|
|
1613
|
+
delivery: checkout.delivery,
|
|
1614
|
+
human_action: checkout.human_action,
|
|
1615
|
+
agent_next_actions: checkout.agent_next_actions
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
function compactPaymentIntent(intent = {}) {
|
|
1620
|
+
if (!intent || typeof intent !== "object") return intent;
|
|
1621
|
+
return compactObject({
|
|
1622
|
+
payment_intent_id: intent.payment_intent_id,
|
|
1623
|
+
payment_attempt_id: intent.payment_attempt_id,
|
|
1624
|
+
checkout_id: intent.checkout_id,
|
|
1625
|
+
order_id: intent.order_id,
|
|
1626
|
+
status: intent.status,
|
|
1627
|
+
amount: intent.amount,
|
|
1628
|
+
currency: intent.currency,
|
|
1629
|
+
human_action: compactHumanAction(intent.human_action),
|
|
1630
|
+
qr_png_url: intent.qr_png_url,
|
|
1631
|
+
qr_image_url: intent.qr_image_url,
|
|
1632
|
+
mobile_wallet_url: intent.mobile_wallet_url,
|
|
1633
|
+
agent_wait: intent.agent_wait,
|
|
1634
|
+
agent_next_actions: intent.agent_next_actions
|
|
1635
|
+
});
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
function compactHumanAction(action = null) {
|
|
1639
|
+
if (!action || typeof action !== "object") return action;
|
|
1640
|
+
return compactObject({
|
|
1641
|
+
kind: action.kind,
|
|
1642
|
+
url: action.url,
|
|
1643
|
+
local_qr_path: action.local_qr_path,
|
|
1644
|
+
local_qr_mime: action.local_qr_mime,
|
|
1645
|
+
qr_png_url: action.qr_png_url,
|
|
1646
|
+
qr_image_url: action.qr_image_url,
|
|
1647
|
+
preferred_qr_url: action.preferred_qr_url,
|
|
1648
|
+
mobile_wallet_url: action.mobile_wallet_url,
|
|
1649
|
+
agent_display_hint: action.agent_display_hint,
|
|
1650
|
+
expires_at: action.expires_at
|
|
1651
|
+
});
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
function compactObject(value = {}) {
|
|
1655
|
+
return Object.fromEntries(Object.entries(value).filter(([, item]) => item !== undefined && item !== null));
|
|
1656
|
+
}
|
|
1657
|
+
|
|
1451
1658
|
function normalizeBuyerMoneyFields(value) {
|
|
1452
1659
|
if (Array.isArray(value)) return value.map((item) => normalizeBuyerMoneyFields(item));
|
|
1453
1660
|
if (!value || typeof value !== "object") return value;
|
|
@@ -1481,7 +1688,7 @@ function buyerDocsFor(value = {}) {
|
|
|
1481
1688
|
topics.add("cart-checkout");
|
|
1482
1689
|
topics.add("payment-qr");
|
|
1483
1690
|
}
|
|
1484
|
-
if (status.includes("waiting_user_payment") || actions.includes("wait_payment") || value.payment_intent?.human_action || value.payment_intent?.qr_image_url) {
|
|
1691
|
+
if (status.includes("payment_handoff") || status.includes("waiting_user_payment") || actions.includes("show_human_visible_markdown") || actions.includes("wait_payment") || value.payment_intent?.human_action || value.payment_intent?.qr_image_url) {
|
|
1485
1692
|
topics.add("payment-qr");
|
|
1486
1693
|
topics.add("payment-wait");
|
|
1487
1694
|
}
|
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
|
|
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
|
|
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
|
|
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
|
@@ -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> --
|
|
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 --
|
|
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,23 +206,28 @@ 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> --
|
|
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
|
-
`
|
|
216
|
-
`
|
|
215
|
+
`human_visible_markdown` to the human first, then run
|
|
216
|
+
`after_human_visible_markdown.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
|
|
|
220
|
+
If a response has `status=payment_handoff_required`, `next` is the user-visible
|
|
221
|
+
reply step, not payment wait. Do not run `buyer payment wait` until the QR/link
|
|
222
|
+
has been sent to the human; then use the returned
|
|
223
|
+
`after_human_visible_markdown.command` with `--qr-shown`.
|
|
224
|
+
|
|
220
225
|
For first-purchase auth, treat the returned ItPay authorization entry as a
|
|
221
226
|
single human orchestration entry. It may open Alipay login/registration first
|
|
222
227
|
and then payment after ItPay receives the OAuth callback. Do not call
|
|
223
228
|
`oauth_start_url` directly unless the ItPay auth page asks for fallback.
|
|
224
|
-
If the payment page says
|
|
225
|
-
or if
|
|
229
|
+
If the payment page says provider entry is stabilizing/preparing,
|
|
230
|
+
or if the payment provider says "order not found", tell the human to wait 30-60
|
|
226
231
|
seconds and use the same page/QR again. Do not ask them to refresh repeatedly,
|
|
227
232
|
and do not create another checkout or payment intent. Use
|
|
228
233
|
`itp buyer payment refresh-qr ... --reason order-not-found` only after the
|
|
@@ -238,7 +243,7 @@ I found the service and selected the matching variant.
|
|
|
238
243
|
I created the cart and checkout.
|
|
239
244
|
Please open the returned ItPay authorization link and approve Alipay login.
|
|
240
245
|
I am waiting for ItPay account authorization.
|
|
241
|
-
Please scan the returned ItPay-hosted QR image with
|
|
246
|
+
Please scan the returned ItPay-hosted QR image with the payment provider.
|
|
242
247
|
I am waiting for ItPay payment verification.
|
|
243
248
|
Payment is verified.
|
|
244
249
|
Delivery is claimable by the human buyer. Please check your email.
|