@alipay/alipay-aipay 1.5.2 → 1.5.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 +1 -1
- package/package.json +1 -1
- package/skill/VERSION +1 -1
- package/skill/references/integration/flow.md +2 -2
- package/skill/references/normal/json/customer-messages.json +2 -2
- package/skill/references/normal/json/policy-rules.json +6 -0
- package/skill/references/normal/scripts/onboarding_discovery_runner.mjs +137 -20
- package/skill/references/normal/scripts/onboarding_message_runner.mjs +144 -52
- package/skill/references/normal/scripts/render_customer_message.mjs +36 -3
- package/skill/references/normal/scripts/runtime.mjs +69 -0
- package/skill/references/normal/scripts/telemetry.mjs +11 -0
- package/skill/references/onboarding/flow.md +36 -22
- package/skill/references/onboarding/modules/app-release.md +3 -1
- package/skill/references/onboarding/modules/authorization.md +3 -2
- package/skill/references/onboarding/modules/error-handling.md +10 -4
- package/skill/references/onboarding/modules/product-sign.md +5 -1
- package/skill/references/onboarding/modules/scripts/app.sh +27 -10
- package/skill/references/onboarding/modules/scripts/ar_sign_apply.sh +6 -0
- package/skill/references/onboarding/modules/scripts/auth.sh +118 -61
- package/skill/references/onboarding/modules/scripts/error_handler.sh +147 -22
- package/skill/references/onboarding/modules/scripts/service.sh +9 -0
- package/skill/references/onboarding/modules/scripts/upload_screenshots.sh +10 -1
- package/skill/references/onboarding/modules/service-registration.md +1 -1
- package/skill/references/runtime-entry.md +5 -5
|
@@ -221,6 +221,7 @@ if [ "$RETRY_RC" -eq 75 ]; then
|
|
|
221
221
|
RECONCILE_OUTPUT=$(bash "${SCRIPT_DIR}/query_sign_status.sh" --sales-code "$SALES_CODE" --product-type "$PRODUCT_TYPE" 2>&1 || true)
|
|
222
222
|
RECONCILED_STATUS=$(printf '%s\n' "$RECONCILE_OUTPUT" | sed -n 's/^ALIPAY_AIPAY_INTERNAL:SIGN_STATUS=//p' | tail -1)
|
|
223
223
|
if [ "$RECONCILED_STATUS" = "SIGN_SUBMITTED" ] || [ "$RECONCILED_STATUS" = "SIGNED_EFFECTIVE" ]; then
|
|
224
|
+
emit_control "SIGN_APPLY_RESULT=SUCCESS"
|
|
224
225
|
case "$PRODUCT_TYPE" in
|
|
225
226
|
aipay) emit_control "FLOW:AI_PAY_SIGN_CONTINUE" ;;
|
|
226
227
|
webpay) emit_control "FLOW:PC_WEB_SIGN_CONTINUE" ;;
|
|
@@ -229,14 +230,17 @@ if [ "$RETRY_RC" -eq 75 ]; then
|
|
|
229
230
|
exit 0
|
|
230
231
|
fi
|
|
231
232
|
echo "❌ 签约提交响应不明且现有查询无法排除传播延迟,结果标记为 UNKNOWN,禁止自动重复提交"
|
|
233
|
+
emit_control "SIGN_APPLY_RESULT=UNKNOWN"
|
|
232
234
|
exit 1
|
|
233
235
|
elif [ "$RETRY_RC" -ne 0 ]; then
|
|
234
236
|
echo "❌ 签约提交因明确未发送的网络异常在自动重试两次后仍未恢复"
|
|
237
|
+
emit_control "SIGN_APPLY_RESULT=FAILED"
|
|
235
238
|
exit 1
|
|
236
239
|
fi
|
|
237
240
|
|
|
238
241
|
# 错误检测(先于业务解析,handle_error 内部会自动解包信封)
|
|
239
242
|
if ! handle_error "$RESULT"; then
|
|
243
|
+
emit_control "SIGN_APPLY_RESULT=FAILED"
|
|
240
244
|
exit 1
|
|
241
245
|
fi
|
|
242
246
|
|
|
@@ -247,6 +251,7 @@ SUCCESS=$(echo "$BUSINESS" | jq -r '.success // false')
|
|
|
247
251
|
if [ "$SUCCESS" = "true" ]; then
|
|
248
252
|
emit_control "✅ 签约提交成功"
|
|
249
253
|
emit_control "SIGN_APPLY_STATUS=SUBMITTED"
|
|
254
|
+
emit_control "SIGN_APPLY_RESULT=SUCCESS"
|
|
250
255
|
# 签约提交成功后无需等待审核,立即推进后续步骤
|
|
251
256
|
# AI 按量付费: 继续 5.2 服务注册 → 5.3 应用发布
|
|
252
257
|
# AI 网页应用收款/AI 移动应用收款: 继续 5.3 应用发布
|
|
@@ -263,5 +268,6 @@ else
|
|
|
263
268
|
ERROR_MSG=$(echo "$BUSINESS" | jq -r '.error.message // .errorMessage // "未知错误"')
|
|
264
269
|
ERROR_MSG=$(sanitize_customer_error_text "$ERROR_MSG")
|
|
265
270
|
echo "❌ 签约提交失败: $ERROR_MSG"
|
|
271
|
+
emit_control "SIGN_APPLY_RESULT=FAILED"
|
|
266
272
|
exit 1
|
|
267
273
|
fi
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
# init: AUTH_FLOW:SKIP | AUTH_FLOW:READY_TO_OPEN | AUTH_FLOW:SCOPE_MISMATCH | AUTH_FLOW:MCC_MISMATCH | AUTH_FLOW:RETRY_WITH_NETWORK | AUTH_FLOW:FAILED
|
|
15
15
|
# wait: AUTH_FLOW:AUTH_SUCCESS | AUTH_FLOW:PENDING | AUTH_FLOW:EXPIRED | AUTH_FLOW:SCOPE_MISMATCH | AUTH_FLOW:MCC_MISMATCH | AUTH_FLOW:CONTEXT_MISMATCH | AUTH_FLOW:RETRY_WITH_NETWORK | AUTH_FLOW:FAILED
|
|
16
16
|
# confirm: AUTH_FLOW:AUTH_SUCCESS | AUTH_FLOW:PENDING | AUTH_FLOW:EXPIRED | AUTH_FLOW:SCOPE_MISMATCH | AUTH_FLOW:MCC_MISMATCH | AUTH_FLOW:CONTEXT_MISMATCH | AUTH_FLOW:RETRY_WITH_NETWORK | AUTH_FLOW:FAILED
|
|
17
|
-
# mismatch: logout
|
|
17
|
+
# mismatch: logout 后置条件可确认后调用 auth init 重新生成授权链接;上下文冲突时输出 AUTH_FLOW:CONTEXT_MISMATCH 且不 logout;无法确认 CLI 结果时输出 AUTH_FLOW:RETRY_WITH_NETWORK;仍处于登录态时输出 AUTH_FLOW:LOGOUT_STILL_LOGGED_IN
|
|
18
18
|
# stdout: 仅登记对客正文;AUTH_FLOW/状态/查询复用标记写入 stderr
|
|
19
19
|
#=============================================================================
|
|
20
20
|
|
|
@@ -37,6 +37,28 @@ require_command node || exit 1
|
|
|
37
37
|
require_command mktemp || exit 1
|
|
38
38
|
umask 077
|
|
39
39
|
|
|
40
|
+
auth_product_type_from_sales_code() {
|
|
41
|
+
case "$1" in
|
|
42
|
+
"I1080300001000160457") printf 'aipay' ;;
|
|
43
|
+
"I1080300001000041203") printf 'webpay' ;;
|
|
44
|
+
"I1080300001000041313") printf 'apppay' ;;
|
|
45
|
+
*) return 1 ;;
|
|
46
|
+
esac
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
create_auth_sign_reuse_receipt() {
|
|
50
|
+
local sign_status="$1" sign_flow="$2" product_type payload encoded
|
|
51
|
+
product_type=$(auth_product_type_from_sales_code "$SALES_CODE") || return 1
|
|
52
|
+
payload=$(jq -cn \
|
|
53
|
+
--arg productType "$product_type" \
|
|
54
|
+
--arg salesCode "$SALES_CODE" \
|
|
55
|
+
--arg signStatus "$sign_status" \
|
|
56
|
+
--arg flow "$sign_flow" \
|
|
57
|
+
'{schema_ver:1,product_type:$productType,sales_code:$salesCode,sign_status:$signStatus,flow:$flow}') || return 1
|
|
58
|
+
encoded=$(printf '%s' "$payload" | jq -rR '@base64' | tr '+/' '-_' | tr -d '=\n') || return 1
|
|
59
|
+
printf 'AIPAY_SIGN_REUSE_V1.%s' "$encoded"
|
|
60
|
+
}
|
|
61
|
+
|
|
40
62
|
# ─── 授权状态文件(用于独立执行 confirm/mismatch 时兜底恢复非敏感参数)────
|
|
41
63
|
AUTH_STATE_OWNER="$(id -u 2>/dev/null || printf '%s' "${USER:-unknown}" | tr -c 'A-Za-z0-9_-' '_')"
|
|
42
64
|
AUTH_STATE_BASE="${TMPDIR:-/tmp}"
|
|
@@ -159,6 +181,14 @@ save_auth_state() {
|
|
|
159
181
|
}
|
|
160
182
|
}
|
|
161
183
|
|
|
184
|
+
save_auth_state_after_page_delivery() {
|
|
185
|
+
if save_auth_state; then
|
|
186
|
+
return 0
|
|
187
|
+
fi
|
|
188
|
+
emit_control "AUTH_STATE:SAVE_FAILED"
|
|
189
|
+
return 0
|
|
190
|
+
}
|
|
191
|
+
|
|
162
192
|
load_auth_state() {
|
|
163
193
|
if ! validate_auth_state_file; then
|
|
164
194
|
echo "❌ 缺少授权上下文(请传入 --sales-code/--mcc-code,或先执行 auth.sh init)"
|
|
@@ -315,6 +345,26 @@ auth_cli_environment_failure() {
|
|
|
315
345
|
emit_control "AUTH_FLOW:RETRY_WITH_NETWORK"
|
|
316
346
|
}
|
|
317
347
|
|
|
348
|
+
handle_auth_cli_result() {
|
|
349
|
+
local cli_result="$1" error_type
|
|
350
|
+
error_type=$(detect_error "$cli_result")
|
|
351
|
+
if handle_error "$cli_result"; then
|
|
352
|
+
return 0
|
|
353
|
+
fi
|
|
354
|
+
case "$error_type" in
|
|
355
|
+
MCP_AUTH_ERROR|AUTH_MISMATCH)
|
|
356
|
+
# error_handler.sh already emitted the unique recovery marker.
|
|
357
|
+
;;
|
|
358
|
+
MCP_SERVICE_ERROR|SERVICE_UNSTABLE)
|
|
359
|
+
emit_control "AUTH_FLOW:RETRY_WITH_NETWORK"
|
|
360
|
+
;;
|
|
361
|
+
*)
|
|
362
|
+
emit_control "AUTH_FLOW:FAILED"
|
|
363
|
+
;;
|
|
364
|
+
esac
|
|
365
|
+
return 1
|
|
366
|
+
}
|
|
367
|
+
|
|
318
368
|
wait_for_effective_whoami() {
|
|
319
369
|
local action="$1"
|
|
320
370
|
local max_attempts=3
|
|
@@ -332,10 +382,7 @@ wait_for_effective_whoami() {
|
|
|
332
382
|
auth_cli_environment_failure "$action"
|
|
333
383
|
return 1
|
|
334
384
|
}
|
|
335
|
-
|
|
336
|
-
emit_control "AUTH_FLOW:FAILED"
|
|
337
|
-
return 1
|
|
338
|
-
fi
|
|
385
|
+
handle_auth_cli_result "$AUTH_WHOAMI_RESULT" || return 1
|
|
339
386
|
|
|
340
387
|
logged_in=$(echo "$AUTH_WHOAMI_RESULT" | jq -r '.data.logged_in // false')
|
|
341
388
|
is_expired=$(echo "$AUTH_WHOAMI_RESULT" | jq -r '.data.is_expired // false')
|
|
@@ -364,18 +411,6 @@ run_json_command() {
|
|
|
364
411
|
exit_code=$?
|
|
365
412
|
stderr_output=$(cat "$stderr_file")
|
|
366
413
|
|
|
367
|
-
if json_payload=$(extract_json_payload "$stdout"); then
|
|
368
|
-
rm -f "$stderr_file"
|
|
369
|
-
printf '%s' "$json_payload"
|
|
370
|
-
return 0
|
|
371
|
-
fi
|
|
372
|
-
|
|
373
|
-
if json_payload=$(extract_json_payload "$stderr_output"); then
|
|
374
|
-
rm -f "$stderr_file"
|
|
375
|
-
printf '%s' "$json_payload"
|
|
376
|
-
return 0
|
|
377
|
-
fi
|
|
378
|
-
|
|
379
414
|
combined_output=$(printf '%s\n%s' "$stdout" "$stderr_output")
|
|
380
415
|
if json_payload=$(extract_json_payload "$combined_output"); then
|
|
381
416
|
rm -f "$stderr_file"
|
|
@@ -395,7 +430,7 @@ validate_current_authorization() {
|
|
|
395
430
|
local logged_in is_expired granted_scope required_scope
|
|
396
431
|
local scope_match mcc_verify_result mcc_verify_unwrapped mcc_match mcc_raw retry_rc
|
|
397
432
|
local mcc_error_type mcc_error_code mcc_success
|
|
398
|
-
local mismatch_type sign_status_output
|
|
433
|
+
local mismatch_type sign_status_output sign_status_marker sign_flow_marker sign_reuse_receipt
|
|
399
434
|
local granted_scope_item required_scope_item required_scope_found
|
|
400
435
|
local -a granted_scope_items required_scope_items
|
|
401
436
|
|
|
@@ -412,10 +447,7 @@ validate_current_authorization() {
|
|
|
412
447
|
}
|
|
413
448
|
fi
|
|
414
449
|
|
|
415
|
-
|
|
416
|
-
emit_control "AUTH_FLOW:FAILED"
|
|
417
|
-
return 1
|
|
418
|
-
fi
|
|
450
|
+
handle_auth_cli_result "$whoami_result" || return 1
|
|
419
451
|
|
|
420
452
|
logged_in=$(echo "$whoami_result" | jq -r '.data.logged_in // false')
|
|
421
453
|
is_expired=$(echo "$whoami_result" | jq -r '.data.is_expired // false')
|
|
@@ -461,12 +493,12 @@ validate_current_authorization() {
|
|
|
461
493
|
retry_rc=$?
|
|
462
494
|
if [ "$retry_rc" -ne 0 ]; then
|
|
463
495
|
echo "❌ 授权 MCC 校验因网络或服务异常在自动重试两次后仍未恢复"
|
|
464
|
-
emit_control "AUTH_FLOW:
|
|
496
|
+
emit_control "AUTH_FLOW:RETRY_WITH_NETWORK"
|
|
465
497
|
return 1
|
|
466
498
|
fi
|
|
467
499
|
mcc_verify_result=$(extract_json_payload "$mcc_raw") || {
|
|
468
500
|
echo "❌ alipay-cli mcp call ar-query.queryArInfosBySalesProd 未返回合法 JSON"
|
|
469
|
-
emit_control "AUTH_FLOW:
|
|
501
|
+
emit_control "AUTH_FLOW:RETRY_WITH_NETWORK"
|
|
470
502
|
return 1
|
|
471
503
|
}
|
|
472
504
|
|
|
@@ -494,6 +526,11 @@ validate_current_authorization() {
|
|
|
494
526
|
if [ "$mcc_match" = "true" ] && sign_status_output=$(emit_sign_status_from_business "$mcc_verify_unwrapped" "$SALES_CODE" 2>/dev/null); then
|
|
495
527
|
printf '%s\n' "$sign_status_output" \
|
|
496
528
|
| while IFS= read -r marker; do emit_control "$marker"; done
|
|
529
|
+
sign_status_marker=$(printf '%s\n' "$sign_status_output" | awk -F= '/^SIGN_STATUS=/{print $2; exit}')
|
|
530
|
+
sign_flow_marker=$(printf '%s\n' "$sign_status_output" | awk '/^FLOW:/{print; exit}')
|
|
531
|
+
if [ -n "$sign_status_marker" ] && [ -n "$sign_flow_marker" ] && sign_reuse_receipt=$(create_auth_sign_reuse_receipt "$sign_status_marker" "$sign_flow_marker"); then
|
|
532
|
+
emit_control "AUTH_SIGN_REUSE_RECEIPT=${sign_reuse_receipt}"
|
|
533
|
+
fi
|
|
497
534
|
emit_control "AUTH_SIGN_QUERY_REUSABLE=true"
|
|
498
535
|
fi
|
|
499
536
|
fi
|
|
@@ -564,10 +601,7 @@ auth_init() {
|
|
|
564
601
|
auth_cli_environment_failure "登录状态检查"
|
|
565
602
|
return 1
|
|
566
603
|
}
|
|
567
|
-
|
|
568
|
-
emit_control "AUTH_FLOW:FAILED"
|
|
569
|
-
return 1
|
|
570
|
-
fi
|
|
604
|
+
handle_auth_cli_result "$CHECK_RESULT" || return 1
|
|
571
605
|
LOGGED_IN=$(echo "$CHECK_RESULT" | jq -r '.data.logged_in // false')
|
|
572
606
|
IS_EXPIRED=$(echo "$CHECK_RESULT" | jq -r '.data.is_expired // false')
|
|
573
607
|
|
|
@@ -576,13 +610,17 @@ auth_init() {
|
|
|
576
610
|
# 后端查询没有权威 MCC 字段,旧状态变化时不能把现有登录误判为当前上下文已授权。
|
|
577
611
|
DEVICE_CODE=""
|
|
578
612
|
BROWSER_URL=""
|
|
579
|
-
|
|
613
|
+
if ! save_auth_state; then
|
|
614
|
+
emit_control "AUTH_STATE:SAVE_FAILED"
|
|
615
|
+
fi
|
|
580
616
|
emit_control "AUTH_FLOW:${AUTH_INIT_CONTEXT_MISMATCH_TYPE}"
|
|
581
617
|
return 1
|
|
582
618
|
fi
|
|
583
619
|
validate_current_authorization "$CHECK_RESULT" || return 1
|
|
584
620
|
DEVICE_CODE=""
|
|
585
|
-
|
|
621
|
+
if ! save_auth_state; then
|
|
622
|
+
emit_control "AUTH_STATE:SAVE_FAILED"
|
|
623
|
+
fi
|
|
586
624
|
emit_control "✅ 当前登录及授权范围有效,可继续后续流程"
|
|
587
625
|
emit_control "AUTH_FLOW:SKIP"
|
|
588
626
|
return 0
|
|
@@ -593,16 +631,15 @@ auth_init() {
|
|
|
593
631
|
auth_cli_environment_failure "授权登录发起"
|
|
594
632
|
return 1
|
|
595
633
|
}
|
|
596
|
-
|
|
597
|
-
emit_control "AUTH_FLOW:FAILED"
|
|
598
|
-
return 1
|
|
599
|
-
fi
|
|
634
|
+
handle_auth_cli_result "$LOGIN_RESULT" || return 1
|
|
600
635
|
LOGIN_STATUS=$(echo "$LOGIN_RESULT" | jq -r '.data.status // ""')
|
|
601
636
|
|
|
602
637
|
if [ "$LOGIN_STATUS" = "already_logged_in" ]; then
|
|
603
638
|
validate_current_authorization || return 1
|
|
604
639
|
DEVICE_CODE=""
|
|
605
|
-
|
|
640
|
+
if ! save_auth_state; then
|
|
641
|
+
emit_control "AUTH_STATE:SAVE_FAILED"
|
|
642
|
+
fi
|
|
606
643
|
emit_control "✅ 当前登录及授权范围有效,可继续后续流程"
|
|
607
644
|
emit_control "AUTH_FLOW:SKIP"
|
|
608
645
|
return 0
|
|
@@ -630,10 +667,7 @@ auth_init() {
|
|
|
630
667
|
return 1
|
|
631
668
|
}
|
|
632
669
|
|
|
633
|
-
# Step 6:
|
|
634
|
-
save_auth_state || return 1
|
|
635
|
-
|
|
636
|
-
# Step 7: 有效期转换(对非数字做防护)
|
|
670
|
+
# Step 6: 有效期转换(对非数字做防护)
|
|
637
671
|
if [[ "$EXPIRES_IN" =~ ^[0-9]+$ ]] && [ "$EXPIRES_IN" -ge 60 ]; then
|
|
638
672
|
EXPIRES_MIN=$((EXPIRES_IN / 60))
|
|
639
673
|
EXPIRES_DISPLAY="${EXPIRES_MIN} 分钟"
|
|
@@ -643,7 +677,7 @@ auth_init() {
|
|
|
643
677
|
EXPIRES_DISPLAY="10 分钟"
|
|
644
678
|
fi
|
|
645
679
|
|
|
646
|
-
# Step
|
|
680
|
+
# Step 7: 由统一目录渲染授权信息;独立 open 动作在消息发出后打开。
|
|
647
681
|
DISPLAY_CODE="${VERIFICATION_CODE:-(请查看支付宝授权页面)}"
|
|
648
682
|
MESSAGE_INPUT=$(printf '%s\n' "$PRODUCT_NAME" "$DEVICE_CODE" "$MCC_CODE" "$MCC_NAME" "$DISPLAY_CODE" "$EXPIRES_DISPLAY" "$BROWSER_URL" | jq -Rs '
|
|
649
683
|
split("\n") as $v | {productName:$v[0],deviceCode:$v[1],mccCode:$v[2],mccName:$v[3],verificationCode:$v[4],expiresDisplay:$v[5],officialUrl:$v[6]}
|
|
@@ -653,6 +687,8 @@ auth_init() {
|
|
|
653
687
|
emit_control "AUTH_FLOW:FAILED"
|
|
654
688
|
exit 1
|
|
655
689
|
fi
|
|
690
|
+
# Step 8: 页面 stdout 已经安全送达时,状态文件只用于 opener 便利;保存失败不得生成第二个授权页或截断 wait。
|
|
691
|
+
save_auth_state_after_page_delivery
|
|
656
692
|
echo ""
|
|
657
693
|
emit_control "AUTH_FLOW:READY_TO_OPEN"
|
|
658
694
|
}
|
|
@@ -661,12 +697,11 @@ auth_open() {
|
|
|
661
697
|
local saved_device saved_url validate_input open_status open_helper
|
|
662
698
|
|
|
663
699
|
restore_auth_context_or_state "$@" || exit 1
|
|
664
|
-
saved_device=$(jq -r '.deviceCode // ""' "$AUTH_STATE_FILE")
|
|
665
|
-
saved_url=$(jq -r '.authorizationUrl // ""' "$AUTH_STATE_FILE")
|
|
700
|
+
saved_device=$(jq -r '.deviceCode // ""' "$AUTH_STATE_FILE" 2>/dev/null || printf '')
|
|
701
|
+
saved_url=$(jq -r '.authorizationUrl // ""' "$AUTH_STATE_FILE" 2>/dev/null || printf '')
|
|
666
702
|
if [ -z "$saved_device" ] || [ -z "$saved_url" ]; then
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
return 1
|
|
703
|
+
emit_control "AUTH_OPEN:LINK_ONLY"
|
|
704
|
+
return 0
|
|
670
705
|
fi
|
|
671
706
|
|
|
672
707
|
validate_input=$(jq -n \
|
|
@@ -740,12 +775,31 @@ auth_confirm() {
|
|
|
740
775
|
elif [ "$OUTER_SUCCESS" = "true" ] || [ "$DATA_STATUS" = "logged_in" ] || [ "$DATA_STATUS" = "already_logged_in" ] || [ "$DATA_STATUS" = "completed" ]; then
|
|
741
776
|
:
|
|
742
777
|
else
|
|
743
|
-
if
|
|
744
|
-
|
|
778
|
+
if ! handle_auth_cli_result "$LOGIN_COMPLETE_RESULT"; then
|
|
779
|
+
cleanup_auth_state
|
|
780
|
+
return 1
|
|
745
781
|
fi
|
|
782
|
+
wait_for_effective_whoami "授权完成确认状态复核"
|
|
783
|
+
whoami_rc=$?
|
|
784
|
+
if [ "$whoami_rc" -eq 2 ]; then
|
|
785
|
+
AUTH_CONFIRM_RESULT="PENDING"
|
|
786
|
+
if [ "${AUTH_POLLING:-false}" != "true" ]; then
|
|
787
|
+
if ! printf '%s' '{}' | ALIPAY_AIPAY_RENDERER_MANAGED_CALLER=auth.sh node "$RENDERER" auth.pending --variant DEFAULT; then
|
|
788
|
+
emit_control "AUTH_FLOW:FAILED"
|
|
789
|
+
return 1
|
|
790
|
+
fi
|
|
791
|
+
emit_control "AUTH_FLOW:PENDING"
|
|
792
|
+
fi
|
|
793
|
+
return 0
|
|
794
|
+
elif [ "$whoami_rc" -ne 0 ]; then
|
|
795
|
+
return 1
|
|
796
|
+
fi
|
|
797
|
+
validate_current_authorization "$AUTH_WHOAMI_RESULT" || return 1
|
|
746
798
|
cleanup_auth_state
|
|
747
|
-
|
|
748
|
-
|
|
799
|
+
AUTH_CONFIRM_RESULT="AUTH_SUCCESS"
|
|
800
|
+
emit_control "✅ 授权确认成功(本机 CLI 登录态、scope 与 MCC 授权有效性均已通过)"
|
|
801
|
+
emit_control "AUTH_FLOW:AUTH_SUCCESS"
|
|
802
|
+
return 0
|
|
749
803
|
fi
|
|
750
804
|
|
|
751
805
|
# Step 3: 复用 init 的 scope + MCC 授权有效性校验
|
|
@@ -817,8 +871,6 @@ auth_wait() {
|
|
|
817
871
|
|
|
818
872
|
# ─── auth mismatch: 授权范围不满足 → logout → 重新授权 ──────────────────────
|
|
819
873
|
auth_mismatch() {
|
|
820
|
-
local logout_result logout_error_type
|
|
821
|
-
|
|
822
874
|
if [ "${1:-}" = "--defer-open" ]; then
|
|
823
875
|
shift
|
|
824
876
|
else
|
|
@@ -847,16 +899,21 @@ auth_mismatch() {
|
|
|
847
899
|
emit_control "AUTH_FLOW:FAILED"
|
|
848
900
|
return 1
|
|
849
901
|
fi
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
902
|
+
confirm_logout_postcondition
|
|
903
|
+
case "$ALIPAY_LOGOUT_POSTCONDITION" in
|
|
904
|
+
COMMAND_CONFIRMED_SUCCESS|POSTCONDITION_LOGGED_OUT)
|
|
905
|
+
;;
|
|
906
|
+
STILL_LOGGED_IN)
|
|
907
|
+
render_logout_still_logged_in || return 1
|
|
908
|
+
emit_control "AUTH_FLOW:LOGOUT_STILL_LOGGED_IN"
|
|
909
|
+
return 1
|
|
910
|
+
;;
|
|
911
|
+
*)
|
|
912
|
+
render_logout_unconfirmed || return 1
|
|
913
|
+
emit_control "AUTH_FLOW:RETRY_WITH_NETWORK"
|
|
914
|
+
return 1
|
|
915
|
+
;;
|
|
916
|
+
esac
|
|
860
917
|
|
|
861
918
|
# Step 3: 调用 auth init 执行登录 + 构建授权链接 + 输出唯一授权消息。
|
|
862
919
|
# DEV_TOOL_NAME 已 export,子进程可继承。
|
|
@@ -348,7 +348,7 @@ detect_error() {
|
|
|
348
348
|
fi
|
|
349
349
|
|
|
350
350
|
# 3. MCP 调用失败(网络/连接错误)
|
|
351
|
-
if echo "$UNWRAPPED" | grep -qiE "MCP 调用失败|connection refused|timeout|network error"; then
|
|
351
|
+
if echo "$UNWRAPPED" | grep -qiE "MCP 调用失败|connection refused|timeout|network[ _]error|网络连接失败"; then
|
|
352
352
|
echo "MCP_SERVICE_ERROR"
|
|
353
353
|
return
|
|
354
354
|
fi
|
|
@@ -383,31 +383,156 @@ detect_error() {
|
|
|
383
383
|
echo "SUCCESS"
|
|
384
384
|
}
|
|
385
385
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
local
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
386
|
+
extract_captured_json() {
|
|
387
|
+
local RAW="$1"
|
|
388
|
+
local ANALYSIS STATUS
|
|
389
|
+
ANALYSIS=$(extract_unique_json "$RAW" 2>/dev/null || echo '{"status":"none"}')
|
|
390
|
+
STATUS=$(echo "$ANALYSIS" | jq -r '.status // "none"' 2>/dev/null)
|
|
391
|
+
[ "$STATUS" = "ok" ] || return 1
|
|
392
|
+
echo "$ANALYSIS" | jq -ce '.value | select(type == "object")' 2>/dev/null
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
run_alipay_cli_json_capture() {
|
|
396
|
+
local stderr_file stdout stderr_output combined_output
|
|
397
|
+
|
|
398
|
+
ALIPAY_CLI_JSON_CAPTURE=""
|
|
399
|
+
ALIPAY_CLI_JSON_EXIT_CODE=0
|
|
400
|
+
stderr_file="$(mktemp "${TMPDIR:-/tmp}/alipay_cli_capture_stderr.XXXXXX")" || return 1
|
|
401
|
+
stdout=$(PLATFORM="${DEV_TOOL_NAME:-unknown}" PLATFORM_ID="${PLATFORM_ID:-}" PRODUCT="${PRODUCT:-}" "$@" 2>"$stderr_file")
|
|
402
|
+
ALIPAY_CLI_JSON_EXIT_CODE=$?
|
|
403
|
+
stderr_output=$(cat "$stderr_file")
|
|
404
|
+
rm -f "$stderr_file"
|
|
405
|
+
|
|
406
|
+
combined_output=$(printf '%s\n%s' "$stdout" "$stderr_output")
|
|
407
|
+
if ALIPAY_CLI_JSON_CAPTURE=$(extract_captured_json "$combined_output"); then
|
|
408
|
+
return 0
|
|
409
|
+
fi
|
|
410
|
+
|
|
411
|
+
ALIPAY_CLI_JSON_CAPTURE=""
|
|
412
|
+
return 1
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
confirm_logout_postcondition() {
|
|
416
|
+
local logout_error_type whoami_error_type whoami_state
|
|
417
|
+
local logout_result_state attempt=1
|
|
418
|
+
local max_attempts=3
|
|
419
|
+
local interval_seconds=3
|
|
420
|
+
|
|
421
|
+
ALIPAY_LOGOUT_POSTCONDITION=""
|
|
422
|
+
ALIPAY_LOGOUT_ERROR_MESSAGE=""
|
|
423
|
+
|
|
424
|
+
if [ "${ALIPAY_AIPAY_TEST_MODE:-0}" = "1" ]; then
|
|
425
|
+
interval_seconds=0
|
|
426
|
+
fi
|
|
427
|
+
|
|
428
|
+
while [ "$attempt" -le "$max_attempts" ]; do
|
|
429
|
+
logout_result_state="UNCONFIRMED"
|
|
430
|
+
if run_alipay_cli_json_capture alipay-cli logout --json; then
|
|
431
|
+
logout_error_type=$(detect_error "$ALIPAY_CLI_JSON_CAPTURE")
|
|
432
|
+
if [ "$ALIPAY_CLI_JSON_EXIT_CODE" -eq 0 ] && [ "$logout_error_type" = "SUCCESS" ]; then
|
|
433
|
+
ALIPAY_LOGOUT_POSTCONDITION="COMMAND_CONFIRMED_SUCCESS"
|
|
434
|
+
return 0
|
|
435
|
+
fi
|
|
436
|
+
ALIPAY_LOGOUT_ERROR_MESSAGE=$(sanitize_customer_error_text "$(extract_error_message "$ALIPAY_CLI_JSON_CAPTURE")")
|
|
437
|
+
case "$logout_error_type" in
|
|
438
|
+
MCP_SERVICE_ERROR|SERVICE_UNSTABLE)
|
|
439
|
+
logout_result_state="RETRYABLE"
|
|
440
|
+
;;
|
|
441
|
+
SUCCESS)
|
|
442
|
+
# A success payload paired with a non-zero exit code cannot prove the command result.
|
|
443
|
+
logout_result_state="UNCONFIRMED"
|
|
444
|
+
;;
|
|
445
|
+
*)
|
|
446
|
+
logout_result_state="EXPLICIT_FAILURE"
|
|
447
|
+
;;
|
|
448
|
+
esac
|
|
402
449
|
else
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
450
|
+
ALIPAY_LOGOUT_ERROR_MESSAGE="退出命令未返回可唯一解析结果"
|
|
451
|
+
fi
|
|
452
|
+
|
|
453
|
+
if ! run_alipay_cli_json_capture alipay-cli whoami --json; then
|
|
454
|
+
ALIPAY_LOGOUT_POSTCONDITION="UNCONFIRMED"
|
|
455
|
+
return 1
|
|
456
|
+
fi
|
|
457
|
+
|
|
458
|
+
whoami_error_type=$(detect_error "$ALIPAY_CLI_JSON_CAPTURE")
|
|
459
|
+
if [ "$ALIPAY_CLI_JSON_EXIT_CODE" -ne 0 ] || [ "$whoami_error_type" != "SUCCESS" ]; then
|
|
460
|
+
ALIPAY_LOGOUT_POSTCONDITION="UNCONFIRMED"
|
|
461
|
+
return 1
|
|
406
462
|
fi
|
|
463
|
+
|
|
464
|
+
whoami_state=$(echo "$ALIPAY_CLI_JSON_CAPTURE" | jq -r '
|
|
465
|
+
if (.data | type) != "object" then "UNCONFIRMED"
|
|
466
|
+
elif (.data.logged_in? | type) == "boolean" and .data.logged_in == false then "LOGGED_OUT"
|
|
467
|
+
elif (.data.is_expired? | type) == "boolean" and .data.is_expired == true then "LOGGED_OUT"
|
|
468
|
+
elif (.data.logged_in? | type) == "boolean" and .data.logged_in == true and
|
|
469
|
+
((.data | has("is_expired") | not) or ((.data.is_expired? | type) == "boolean" and .data.is_expired == false)) then "STILL_LOGGED_IN"
|
|
470
|
+
else "UNCONFIRMED"
|
|
471
|
+
end
|
|
472
|
+
' 2>/dev/null)
|
|
473
|
+
if [ "$whoami_state" = "LOGGED_OUT" ]; then
|
|
474
|
+
ALIPAY_LOGOUT_POSTCONDITION="POSTCONDITION_LOGGED_OUT"
|
|
475
|
+
return 0
|
|
476
|
+
fi
|
|
477
|
+
if [ "$whoami_state" != "STILL_LOGGED_IN" ] || [ "$logout_result_state" != "EXPLICIT_FAILURE" ]; then
|
|
478
|
+
ALIPAY_LOGOUT_POSTCONDITION="UNCONFIRMED"
|
|
479
|
+
return 1
|
|
480
|
+
fi
|
|
481
|
+
|
|
482
|
+
if [ "$attempt" -eq "$max_attempts" ]; then
|
|
483
|
+
ALIPAY_LOGOUT_POSTCONDITION="STILL_LOGGED_IN"
|
|
484
|
+
return 1
|
|
485
|
+
fi
|
|
486
|
+
sleep "$interval_seconds"
|
|
487
|
+
attempt=$((attempt + 1))
|
|
488
|
+
done
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
render_logout_unconfirmed() {
|
|
492
|
+
render_error_message error.mcp.auth LOGOUT_UNCONFIRMED '{}'
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
render_logout_still_logged_in() {
|
|
496
|
+
local message input
|
|
497
|
+
message="${ALIPAY_LOGOUT_ERROR_MESSAGE:-退出命令未能使当前登录态失效}"
|
|
498
|
+
input=$(jq -cn --arg errorMessage "$message" '{errorMessage:$errorMessage}')
|
|
499
|
+
render_error_message error.mcp.auth LOGOUT_FAILED "$input"
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
# ─── MCP 认证错误处理 ──────────────────────────────────────────────────────
|
|
503
|
+
handle_mcp_auth_error() {
|
|
504
|
+
render_error_message error.mcp.auth START '{}' || {
|
|
505
|
+
emit_control "AUTH_FLOW:FAILED"
|
|
506
|
+
return 1
|
|
507
|
+
}
|
|
508
|
+
if [ "${ALIPAY_AIPAY_SUPPRESS_AUTH_LOGOUT:-}" = "1" ]; then
|
|
509
|
+
emit_control "AUTH_FLOW:AUTH_REQUIRED"
|
|
407
510
|
return 1
|
|
408
511
|
fi
|
|
409
|
-
|
|
410
|
-
|
|
512
|
+
confirm_logout_postcondition
|
|
513
|
+
case "$ALIPAY_LOGOUT_POSTCONDITION" in
|
|
514
|
+
COMMAND_CONFIRMED_SUCCESS|POSTCONDITION_LOGGED_OUT)
|
|
515
|
+
render_error_message error.mcp.auth LOGOUT_SUCCESS '{}' || {
|
|
516
|
+
emit_control "AUTH_FLOW:FAILED"
|
|
517
|
+
return 1
|
|
518
|
+
}
|
|
519
|
+
emit_control "AUTH_FLOW:AUTH_REQUIRED"
|
|
520
|
+
;;
|
|
521
|
+
STILL_LOGGED_IN)
|
|
522
|
+
render_logout_still_logged_in || {
|
|
523
|
+
emit_control "AUTH_FLOW:FAILED"
|
|
524
|
+
return 1
|
|
525
|
+
}
|
|
526
|
+
emit_control "AUTH_FLOW:LOGOUT_STILL_LOGGED_IN"
|
|
527
|
+
;;
|
|
528
|
+
*)
|
|
529
|
+
render_logout_unconfirmed || {
|
|
530
|
+
emit_control "AUTH_FLOW:FAILED"
|
|
531
|
+
return 1
|
|
532
|
+
}
|
|
533
|
+
emit_control "AUTH_FLOW:RETRY_WITH_NETWORK"
|
|
534
|
+
;;
|
|
535
|
+
esac
|
|
411
536
|
}
|
|
412
537
|
|
|
413
538
|
# ─── MCP 服务不可用处理 ────────────────────────────────────────────────────
|
|
@@ -351,6 +351,7 @@ service_save() {
|
|
|
351
351
|
if [ "$RETRY_RC" -eq 75 ]; then
|
|
352
352
|
if [ -z "$SERVICE_ID" ]; then
|
|
353
353
|
echo "❌ 服务创建响应不明;现有查询无法把同名候选唯一归属于本次创建,结果标记为 UNKNOWN,禁止自动重复创建"
|
|
354
|
+
emit_control "SERVICE_SAVE_RESULT=UNKNOWN"
|
|
354
355
|
exit 1
|
|
355
356
|
fi
|
|
356
357
|
if fetch_service_list; then
|
|
@@ -365,11 +366,13 @@ service_save() {
|
|
|
365
366
|
if [ "$MATCH_COUNT" -eq 1 ]; then
|
|
366
367
|
emit_control "✅ 服务修改已由列表查询核验成功"
|
|
367
368
|
emit_control "📋 服务ID: $SERVICE_ID"
|
|
369
|
+
emit_control "SERVICE_SAVE_RESULT=SUCCESS"
|
|
368
370
|
emit_control_field "SERVICE_ID" "$SERVICE_ID" || return 1
|
|
369
371
|
return 0
|
|
370
372
|
fi
|
|
371
373
|
fi
|
|
372
374
|
echo "❌ 服务修改响应不明且现有列表无法排除传播延迟,结果标记为 UNKNOWN,禁止自动重复修改"
|
|
375
|
+
emit_control "SERVICE_SAVE_RESULT=UNKNOWN"
|
|
373
376
|
exit 1
|
|
374
377
|
elif [ "$RETRY_RC" -ne 0 ]; then
|
|
375
378
|
if [ -z "$SERVICE_ID" ]; then
|
|
@@ -377,11 +380,13 @@ service_save() {
|
|
|
377
380
|
else
|
|
378
381
|
echo "❌ 服务修改因明确未发送的网络异常在自动重试两次后仍未恢复"
|
|
379
382
|
fi
|
|
383
|
+
emit_control "SERVICE_SAVE_RESULT=FAILED"
|
|
380
384
|
exit 1
|
|
381
385
|
fi
|
|
382
386
|
|
|
383
387
|
# 错误检测
|
|
384
388
|
if ! handle_error "$RESULT"; then
|
|
389
|
+
emit_control "SERVICE_SAVE_RESULT=FAILED"
|
|
385
390
|
exit 1
|
|
386
391
|
fi
|
|
387
392
|
|
|
@@ -418,10 +423,12 @@ service_save() {
|
|
|
418
423
|
|
|
419
424
|
if ! validate_control_value "$NEW_SERVICE_ID"; then
|
|
420
425
|
echo "❌ 服务创建返回成功,但未解析到 serviceId,禁止继续后续流程"
|
|
426
|
+
emit_control "SERVICE_SAVE_RESULT=UNKNOWN"
|
|
421
427
|
exit 1
|
|
422
428
|
fi
|
|
423
429
|
emit_control "✅ 服务${OPERATION}成功"
|
|
424
430
|
emit_control "📋 服务ID: $NEW_SERVICE_ID"
|
|
431
|
+
emit_control "SERVICE_SAVE_RESULT=SUCCESS"
|
|
425
432
|
emit_control_field "SERVICE_ID" "$NEW_SERVICE_ID" || exit 1
|
|
426
433
|
else
|
|
427
434
|
OPERATION="创建"
|
|
@@ -437,8 +444,10 @@ service_save() {
|
|
|
437
444
|
' 2>/dev/null)
|
|
438
445
|
ERROR_MSG=$(sanitize_customer_error_text "$ERROR_MSG")
|
|
439
446
|
echo "❌ 服务${OPERATION}失败: ${BUSINESS_CODE} / ${ERROR_MSG}"
|
|
447
|
+
emit_control "SERVICE_SAVE_RESULT=FAILED"
|
|
440
448
|
else
|
|
441
449
|
echo "❌ 服务${OPERATION}返回结构异常,无法确认操作结果"
|
|
450
|
+
emit_control "SERVICE_SAVE_RESULT=UNKNOWN"
|
|
442
451
|
fi
|
|
443
452
|
exit 1
|
|
444
453
|
fi
|
|
@@ -33,6 +33,7 @@ case "$PRODUCT_TYPE" in
|
|
|
33
33
|
*) echo "❌ 截图上传仅支持 webpay|apppay"; exit 1 ;;
|
|
34
34
|
esac
|
|
35
35
|
init_alipay_cli_product "$PRODUCT_TYPE" || exit 1
|
|
36
|
+
export ALIPAY_AIPAY_SUPPRESS_AUTH_LOGOUT=1
|
|
36
37
|
|
|
37
38
|
HOME_IMG="${1:-}"
|
|
38
39
|
SHOP_IMG="${2:-}"
|
|
@@ -83,7 +84,7 @@ wait
|
|
|
83
84
|
# 解析图片引用值(兼容 file upload 返回 fileKey 或 URL 字段)
|
|
84
85
|
parse_image_ref() {
|
|
85
86
|
local RAW=$(cat "$1")
|
|
86
|
-
if
|
|
87
|
+
if [ "$(detect_error "$RAW")" != "SUCCESS" ]; then
|
|
87
88
|
return 1
|
|
88
89
|
fi
|
|
89
90
|
# alipay-cli file upload 可能返回 MCP 信封,先解包
|
|
@@ -120,7 +121,15 @@ PAY_REF=$(parse_image_ref "$TMP_PAY")
|
|
|
120
121
|
# 校验上传结果(含认证错误检测)
|
|
121
122
|
check_upload_error() {
|
|
122
123
|
local RAW=$(cat "$1")
|
|
124
|
+
local ERROR_TYPE
|
|
125
|
+
ERROR_TYPE=$(detect_error "$RAW")
|
|
126
|
+
if [ "$ERROR_TYPE" = "MCP_AUTH_ERROR" ] && [ "${UPLOAD_AUTH_ERROR_SEEN:-false}" = "true" ]; then
|
|
127
|
+
return 1
|
|
128
|
+
fi
|
|
123
129
|
if ! handle_error "$RAW"; then
|
|
130
|
+
if [ "$ERROR_TYPE" = "MCP_AUTH_ERROR" ]; then
|
|
131
|
+
UPLOAD_AUTH_ERROR_SEEN=true
|
|
132
|
+
fi
|
|
124
133
|
return 1
|
|
125
134
|
fi
|
|
126
135
|
return 0
|
|
@@ -29,7 +29,7 @@ node "<SKILL_DIR>/references/normal/scripts/runtime.mjs" service validate --name
|
|
|
29
29
|
- 修改:必须传当前候选完整 `serviceId` 和全部五项资料;保存前使用 `onboarding.write.confirm/SERVICE_UPDATE_ONLY` 绑定当前登录会话、脱敏主体、产品、动作、目标与参数,并等待当前摘要后的明确肯定回复;`1` 只是兼容快捷输入。
|
|
30
30
|
- 任何目标、参数、主体或会话变化都使旧修改确认失效。创建不得进入修改确认。
|
|
31
31
|
|
|
32
|
-
保存写操作调用 `a2a-pay-service.saveBazaarServiceForMcp`。明确 `NOT_SENT` 才能自动重试;`MAYBE_SENT` 时用列表按 `serviceId` 和完整五项资料核验,不能靠同名服务猜测。创建结果未解析到实际 `serviceId`
|
|
32
|
+
保存写操作调用 `a2a-pay-service.saveBazaarServiceForMcp`。明确 `NOT_SENT` 才能自动重试;`MAYBE_SENT` 时用列表按 `serviceId` 和完整五项资料核验,不能靠同名服务猜测。创建结果未解析到实际 `serviceId` 时禁止宣称完成。脚本必须输出 `SERVICE_SAVE_RESULT=SUCCESS|FAILED|UNKNOWN`;`UNKNOWN` 只表示禁止自动重复保存并进入服务分支待办,不清除其他分支事实。
|
|
33
33
|
|
|
34
34
|
成功结果只用登记 renderer:
|
|
35
35
|
|