@alipay/alipay-aipay 1.0.2 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/commands/install.d.ts.map +1 -1
  2. package/dist/commands/install.js +71 -22
  3. package/dist/commands/install.js.map +1 -1
  4. package/dist/commands/prepare-tools.d.ts.map +1 -1
  5. package/dist/commands/prepare-tools.js +16 -4
  6. package/dist/commands/prepare-tools.js.map +1 -1
  7. package/dist/commands/update.d.ts.map +1 -1
  8. package/dist/commands/update.js +9 -9
  9. package/dist/commands/update.js.map +1 -1
  10. package/dist/index.js +1 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/utils/alipay-cli.d.ts +6 -0
  13. package/dist/utils/alipay-cli.d.ts.map +1 -1
  14. package/dist/utils/alipay-cli.js +38 -22
  15. package/dist/utils/alipay-cli.js.map +1 -1
  16. package/dist/utils/file.d.ts +9 -2
  17. package/dist/utils/file.d.ts.map +1 -1
  18. package/dist/utils/file.js +33 -3
  19. package/dist/utils/file.js.map +1 -1
  20. package/dist/utils/gpg.d.ts.map +1 -1
  21. package/dist/utils/gpg.js +97 -53
  22. package/dist/utils/gpg.js.map +1 -1
  23. package/dist/utils/jq.d.ts.map +1 -1
  24. package/dist/utils/jq.js +6 -9
  25. package/dist/utils/jq.js.map +1 -1
  26. package/dist/utils/npm.d.ts.map +1 -1
  27. package/dist/utils/npm.js +2 -6
  28. package/dist/utils/npm.js.map +1 -1
  29. package/dist/utils/shell.d.ts +15 -2
  30. package/dist/utils/shell.d.ts.map +1 -1
  31. package/dist/utils/shell.js +216 -12
  32. package/dist/utils/shell.js.map +1 -1
  33. package/package.json +3 -4
  34. package/skill/SKILL.md +77 -11
  35. package/skill/VERSION +1 -0
  36. package/skill/references/integration/flow.md +154 -136
  37. package/skill/references/integration/modules/alipay-sdk-reminder.md +29 -13
  38. package/skill/references/integration/modules/checklist.md +49 -19
  39. package/skill/references/integration/modules/code-examples/csharp/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +3 -3
  40. package/skill/references/integration/modules/code-examples/csharp/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.cs +27 -13
  41. package/skill/references/integration/modules/code-examples/java/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +3 -3
  42. package/skill/references/integration/modules/code-examples/java/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemoController.java +31 -12
  43. package/skill/references/integration/modules/code-examples/nodejs/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +5 -4
  44. package/skill/references/integration/modules/code-examples/nodejs/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.js +69 -37
  45. package/skill/references/integration/modules/code-examples/php/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +3 -3
  46. package/skill/references/integration/modules/code-examples/php/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.php +38 -17
  47. package/skill/references/integration/modules/code-examples/python/2-/347/275/221/347/253/231/346/224/257/344/273/230//347/273/237/344/270/200/346/224/266/345/215/225/344/270/213/345/215/225/345/271/266/346/224/257/344/273/230/351/241/265/351/235/242/346/216/245/345/217/243/344/273/243/347/240/201/347/244/272/344/276/213.md +3 -3
  48. package/skill/references/integration/modules/code-examples/python/4-/346/214/211/351/207/217/344/273/230/350/264/271/A2MPaymentDemo.py +31 -18
  49. package/skill/references/integration/modules/interface-guide.md +7 -3
  50. package/skill/references/integration/modules/sandbox/a2m-sandbox-test.md +77 -48
  51. package/skill/references/integration/modules/sandbox/alipay-sandbox-tool.md +8 -5
  52. package/skill/references/integration/modules/sandbox/sandbox-setup-guide.md +157 -117
  53. package/skill/references/integration/modules/scripts/local_402_sandbox_pay.py +35 -4
  54. package/skill/references/normal/alipay-cli-env.md +13 -11
  55. package/skill/references/normal/self-update.md +59 -0
  56. package/skill/references/onboarding/flow.md +202 -282
  57. package/skill/references/onboarding/modules/app-release.md +91 -35
  58. package/skill/references/onboarding/modules/authorization.md +49 -22
  59. package/skill/references/onboarding/modules/error-handling.md +19 -14
  60. package/skill/references/onboarding/modules/product-sign.md +23 -20
  61. package/skill/references/onboarding/modules/scripts/app.sh +139 -40
  62. package/skill/references/onboarding/modules/scripts/auth.sh +183 -101
  63. package/skill/references/onboarding/modules/scripts/error_handler.sh +148 -18
  64. package/skill/references/onboarding/modules/scripts/query_sign_status.sh +17 -11
  65. package/skill/references/onboarding/modules/scripts/service.sh +208 -37
  66. package/skill/references/onboarding/modules/scripts/upload_screenshots.sh +1 -1
  67. package/skill/references/onboarding/modules/service-registration.md +72 -29
@@ -1,7 +1,7 @@
1
1
  # 产品签约模块
2
2
 
3
3
  > 本文档定义产品签约的完整流程、参数规范和状态判断逻辑。
4
- > 被引用文档:`SKILL.md` → Step 5 入驻推进 - 签约部分
4
+ > 被引用文档:`onboarding/flow.md` → Step 3.1 签约查询 + Step 5.1 签约提交
5
5
  > 本文档位于 `references/onboarding/modules/`,文中的 `scripts/...` 路径相对本目录;从技能包根目录执行时对应 `references/onboarding/modules/scripts/...`。
6
6
 
7
7
  ---
@@ -12,7 +12,7 @@
12
12
 
13
13
  本模块采用 `ar-sign.apply` **外部直调 / 静态参数模式**:由 `scripts/ar_sign_apply.sh` 基于当前已确认的产品类型、MCC 和资料字段直接构造 `businessProperty` 后提交申请。它不走 `previewFormView` 动态 schema 收集循环,因此只覆盖本文档明确列出的字段和产品。
14
14
 
15
- **触发条件:** Step 3.1 授权后处理中,签约状态为 NOT_SIGNED 时触发。
15
+ **使用条件:** Step 3.1 始终使用本模块的签约状态查询规则;只有查询结果为 `NOT_SIGNED` 时,才使用本模块的签约申请提交规则。`SIGNED_EFFECTIVE`、`SIGN_SUBMITTED`、查询失败或其他状态均禁止提交签约申请。
16
16
 
17
17
  ---
18
18
 
@@ -42,8 +42,8 @@
42
42
  | 返回结果 | 状态判定 | 说明 |
43
43
  |----------|----------|------|
44
44
  | `resultObj.arInfoList` 为空数组 `[]` | `NOT_SIGNED` | 未签约 |
45
- | `resultObj.arInfoList` 不为空,且存在 `arStatus` 为 `"02"` 的记录 | `SIGNED_EFFECTIVE` | 签约已生效,跳过资料采集 |
46
- | `resultObj.arInfoList` 不为空,且存在 `arStatus` 为 `"01"` 的记录 | `SIGN_SUBMITTED` | 已提交签约(待生效),跳过资料采集与重复签约提交 |
45
+ | `resultObj.arInfoList` 不为空,且存在 `arStatus` 为 `"02"` 的记录 | `SIGNED_EFFECTIVE` | 签约已生效,跳过签约材料采集,仍在 Step 4 处理应用/服务决策 |
46
+ | `resultObj.arInfoList` 不为空,且存在 `arStatus` 为 `"01"` 的记录 | `SIGN_SUBMITTED` | 已提交签约(待生效),跳过签约材料和重复签约提交,仍在 Step 4 处理应用/服务决策 |
47
47
 
48
48
  ### 判断逻辑
49
49
 
@@ -53,22 +53,22 @@
53
53
 
54
54
  | 签约状态 | 产品类型 | FLOW 信号 | 后续流程 |
55
55
  |----------|----------|-----------|----------|
56
- | `NOT_SIGNED` | 网站支付 | `FLOW:PC_WEB_NOT_SIGNED` | 进入 Step 4 资料采集(3张网站截图)→ Step 5 签约 + 应用发布 |
57
- | `NOT_SIGNED` | APP支付 | `FLOW:APP_NOT_SIGNED` | 进入 Step 4 资料采集(APP名称 + 3张APP界面截图)→ Step 5 签约 + 应用发布 |
58
- | `NOT_SIGNED` | 按量付费 | `FLOW:AI_PAY_NOT_SIGNED` | 跳过 Step 4,进入 Step 5.1 签约,再继续服务注册 + 应用发布 |
59
- | `SIGNED_EFFECTIVE` | 网站支付 | `FLOW:PC_WEB_SIGNED` | 跳过签约提交,直接执行应用发布 |
60
- | `SIGNED_EFFECTIVE` | APP支付 | `FLOW:APP_SIGNED` | 跳过签约提交,直接执行应用发布 |
61
- | `SIGNED_EFFECTIVE` | 按量付费 | `FLOW:AI_PAY_SIGNED` | 跳过签约提交,直接执行服务注册 + 应用发布 |
62
- | `SIGN_SUBMITTED` | 网站支付 | `FLOW:PC_WEB_SIGNED` | 跳过重复签约提交,直接执行应用发布 |
63
- | `SIGN_SUBMITTED` | APP支付 | `FLOW:APP_SIGNED` | 跳过重复签约提交,直接执行应用发布 |
64
- | `SIGN_SUBMITTED` | 按量付费 | `FLOW:AI_PAY_SIGNED` | 跳过重复签约提交,直接执行服务注册 + 应用发布 |
65
-
66
- **⚠️ 按量付费 无需前置资料采集:**
56
+ | `NOT_SIGNED` | 网站支付 | `FLOW:PC_WEB_NOT_SIGNED` | 进入 Step 4 一次性资料与资源决策(3张网站截图 + 应用决策/条件资料)→ Step 5 签约 + 应用发布 |
57
+ | `NOT_SIGNED` | APP支付 | `FLOW:APP_NOT_SIGNED` | 进入 Step 4 一次性资料与资源决策(APP名称 + 3张APP界面截图 + 应用决策/条件资料)→ Step 5 签约 + 应用发布 |
58
+ | `NOT_SIGNED` | 按量付费 | `FLOW:AI_PAY_NOT_SIGNED` | Step 4 无需收集页面图片,但一次完成服务/应用决策和条件资料;再进入 Step 5.1 签约 |
59
+ | `SIGNED_EFFECTIVE` | 网站支付 | `FLOW:PC_WEB_SIGNED` | Step 4 完成应用决策后,跳过签约提交并执行应用发布 |
60
+ | `SIGNED_EFFECTIVE` | APP支付 | `FLOW:APP_SIGNED` | Step 4 完成应用决策后,跳过签约提交并执行应用发布 |
61
+ | `SIGNED_EFFECTIVE` | 按量付费 | `FLOW:AI_PAY_SIGNED` | Step 4 完成服务/应用决策后,跳过签约提交并执行服务注册 + 应用发布 |
62
+ | `SIGN_SUBMITTED` | 网站支付 | `FLOW:PC_WEB_SIGNED` | Step 4 完成应用决策后,跳过重复签约提交并执行应用发布 |
63
+ | `SIGN_SUBMITTED` | APP支付 | `FLOW:APP_SIGNED` | Step 4 完成应用决策后,跳过重复签约提交并执行应用发布 |
64
+ | `SIGN_SUBMITTED` | 按量付费 | `FLOW:AI_PAY_SIGNED` | Step 4 完成服务/应用决策后,跳过重复签约提交并执行服务注册 + 应用发布 |
65
+
66
+ **⚠️ 按量付费无需签约材料:**
67
67
  ```
68
68
  ✅ 网站支付未签约:需先采集 3 张网站截图,再进入 Step 5 签约
69
69
  ✅ APP支付未签约:需先采集 APP 名称和 3 张APP界面截图,再进入 Step 5 签约
70
- ✅ 按量付费 未签约:跳过 Step 4,进入 Step 5.1 签约,再继续服务注册 + 应用发布
71
- ❌ 禁止:按量付费 在 Step 4 收集服务注册信息(服务信息在 Step 5 服务注册时交互式收集)
70
+ ✅ 按量付费 未签约:Step 4 无需签约页面图片,但前置完成服务/应用决策和条件资料;Step 5 仍先提交签约
71
+ ✅ 按量付费在 Step 4 基于已查询的服务候选,一次收集新建/修改分支的完整五项服务资料;Step 5.2 才执行写操作
72
72
  ```
73
73
 
74
74
  **按量付费签约后的必要后续动作:**
@@ -188,8 +188,8 @@ bash scripts/ar_sign_apply.sh --product apppay --sales-code "I108030000100004131
188
188
  | `mccCode` | Step 2 方案规划 | 运行时变量,格式 `Axxxx_Bxxxx`,不持久化到状态文件 |
189
189
  | `channelCode` | 固定值 | `"B_SK_SH_RPC"` |
190
190
  | `orderType` | 固定值 | `"NEW_SIGN"` |
191
- | `screenshot` | Step 4 资料采集 | **仅网站支付/APP支付需要**(3个上传后的图片引用值),按量付费 无需此项 |
192
- | `appName` | Step 4 资料采集 | **仅APP支付需要**;脚本参数仍为 `--app-name`,提交到 MCP 时映射为 `nativeAppDTO.name` |
191
+ | `screenshot` | Step 4 一次性资料与资源决策 | **仅未签约的网站支付/APP支付需要**(3个上传后的图片引用值),按量付费无需此项 |
192
+ | `appName` | Step 4 一次性资料与资源决策 | **仅未签约的 APP支付需要**;脚本参数仍为 `--app-name`,提交到 MCP 时映射为 `nativeAppDTO.name` |
193
193
  | `appStatus` | 固定值 | **仅APP支付需要**,本次固定为 `OFFLINE` |
194
194
 
195
195
  ### 按量付费接入产物映射
@@ -209,8 +209,11 @@ bash scripts/ar_sign_apply.sh --product apppay --sales-code "I108030000100004131
209
209
  ✅ 网站支付未签约 → 需要 3 张网站截图(首页、商品页、支付页)
210
210
  ✅ APP支付未签约 → 需要 APP 名称和 3 张APP界面截图(首页、商品页、支付页),签约状态固定按 OFFLINE 提交
211
211
  ✅ 按量付费 未签约 → 无需截图,直接签约
212
+ ✅ 网站支付/APP支付缺少支付页或支付能力 → 不提交签约;先进入/回到集成流程完成实现,再回来上传图片
213
+ ✅ 网站支付/APP支付页面和支付能力已存在、仅缺三张页面图片 → 保留在 onboarding Step 4 收集和上传,不进入集成流程
212
214
  ❌ 禁止:按量付费 签约时传入 screenshot 字段
213
215
  ❌ 禁止:网站支付/APP支付 签约时省略 screenshot 字段
216
+ ❌ 禁止:将“支付页”自行改口为“支付成功页”或要求用户提供文档未定义的页面
214
217
  ❌ 禁止:APP支付签约 MCP payload 中使用 webAppDTO、appDTO、appName、appType、placeType=APP、placeType=ONLINE_WEBAPP、placeType=ONLINE_NATIVEAPP 或 appType=PC_WEB
215
218
  ```
216
219
 
@@ -226,7 +229,7 @@ bash scripts/ar_sign_apply.sh --product apppay --sales-code "I108030000100004131
226
229
 
227
230
  > ⚠️ **签约提交成功后,无需等待签约审核通过,应立即继续推进后续步骤。**
228
231
 
229
- 签约申请提交后,后端会异步审核,`arStatus` 为 `"01"`(已提交待生效)。应用创建和服务注册不依赖签约审核通过,可以并行推进。
232
+ 签约申请提交后,后端会异步审核,`arStatus` 为 `"01"`(已提交待生效)。应用创建和服务注册不依赖签约审核通过,可以继续推进。为减少用户交互,应用/服务列表查询及分支资料采集可在签约提交前完成;当前流程仍先提交签约,成功后才执行服务或应用写操作。
230
233
 
231
234
  ### FLOW 信号
232
235
 
@@ -2,7 +2,7 @@
2
2
  #=============================================================================
3
3
  # 脚本名称: app.sh
4
4
  # 功能描述: 应用发布全流程 - 查询/复用 + 创建 + 公钥设置 + 提交审核
5
- # 调用位置: Step 5.3 应用发布
5
+ # 调用位置: list 用于 Step 3.1 前置资源查询;其他命令用于 Step 5.3 应用发布
6
6
  # 调用前置: 脚本通过 error_handler.sh 间接初始化 DEV_TOOL_NAME
7
7
  # list/create 可传 --application-type,未传时按 --product-type/--sales-code 或兼容环境变量推断
8
8
  # 用法:
@@ -13,8 +13,8 @@
13
13
  # app audit <appId> - 提交应用审核
14
14
  # app reuse <appId> - 复用已有应用(查询安全密钥)
15
15
  # 返回值:
16
- # list: FLOW:CREATE_NEW | FLOW:SELECT | FLOW:ERROR
17
- # create: 输出 APP_ID=xxx
16
+ # list: FLOW:CREATE_NEW | FLOW:SELECT | FLOW:PENDING_APPLICATIONS | FLOW:ERROR
17
+ # create: 成功输出 APP_ID=xxx;失败 exit 1,返回结构异常时同时输出 FLOW:ERROR
18
18
  # key: 输出 confirmPageUrl
19
19
  # audit: 输出审核结果
20
20
  # reuse: FLOW:REUSE_SUCCESS | FLOW:REUSE_NO_KEY | FLOW:ERROR
@@ -31,7 +31,6 @@ else
31
31
  fi
32
32
 
33
33
  require_command jq || exit 1
34
- require_command alipay-cli || exit 1
35
34
 
36
35
  parse_app_context_args() {
37
36
  while [[ $# -gt 0 ]]; do
@@ -261,36 +260,54 @@ extract_application_status() {
261
260
  '
262
261
  }
263
262
 
264
- extract_rsa2_key_info() {
263
+ extract_rsa2_application_key_info() {
265
264
  echo "$SECURITY_BUSINESS" | jq -c '
266
265
  (
267
- (.resultObj.alipayKeyList // []) +
266
+ (.resultObj.securityConfigList // []) +
268
267
  (.resultObj.securityKeys // []) +
269
- (.data.alipayKeyList // []) +
270
- (.data.securityKeys // [])
271
- ) | map(select(.signType == "RSA2")) | .[0] // {}
268
+ (.data.securityConfigList // []) +
269
+ (.data.securityKeys // []) +
270
+ (.resultObj.alipayKeyList // []) +
271
+ (.data.alipayKeyList // [])
272
+ ) |
273
+ map(select(
274
+ .signType == "RSA2" and
275
+ (
276
+ ((.partnerPublicKey // "") != "") or
277
+ ((.certInfoDTO | type) == "object" and ((.certInfoDTO.publicKey // "") != ""))
278
+ )
279
+ )) |
280
+ .[0] // {}
272
281
  '
273
282
  }
274
283
 
275
- extract_rsa2_security_config() {
284
+ extract_rsa2_alipay_key_info() {
276
285
  echo "$SECURITY_BUSINESS" | jq -c '
277
286
  (
278
- (.resultObj.securityConfigList // []) +
279
- (.data.securityConfigList // [])
280
- ) | map(select(.signType == "RSA2")) | .[0] // {}
287
+ (.resultObj.alipayKeyList // []) +
288
+ (.data.alipayKeyList // []) +
289
+ (.resultObj.securityKeys // []) +
290
+ (.data.securityKeys // [])
291
+ ) |
292
+ map(select(.signType == "RSA2" and ((.alipayPublicKey // "") != ""))) |
293
+ .[0] // {}
281
294
  '
282
295
  }
283
296
 
297
+ extract_alipay_public_key() {
298
+ local RSA2_KEY_INFO="$1"
299
+ echo "$RSA2_KEY_INFO" | jq -r '.alipayPublicKey // empty'
300
+ }
301
+
284
302
  is_rsa2_application_key_ready() {
285
303
  local RSA2_KEY_INFO="$1"
286
- local RSA2_SECURITY_CONFIG="$2"
287
304
 
288
305
  echo "$RSA2_KEY_INFO" | jq -e '
289
- ((.certInfoDTO.publicKey // .publicKey // .alipayPublicKey // "") != "")
290
- ' >/dev/null 2>&1 && return 0
291
-
292
- echo "$RSA2_SECURITY_CONFIG" | jq -e '
293
- ((.partnerPublicKey // "") != "") and (.invalid != true)
306
+ ((.partnerPublicKey // "") != "") or
307
+ (
308
+ (.certInfoDTO | type) == "object" and
309
+ ((.certInfoDTO.publicKey // "") != "")
310
+ )
294
311
  ' >/dev/null 2>&1
295
312
  }
296
313
 
@@ -342,10 +359,9 @@ ensure_rsa2_application_key_ready() {
342
359
  while [ "$ATTEMPT" -le "$MAX_ATTEMPTS" ]; do
343
360
  query_security_key "$APP_ID" "$PUBLIC_KEY" || return 1
344
361
 
345
- RSA2_KEY_INFO=$(extract_rsa2_key_info)
346
- RSA2_SECURITY_CONFIG=$(extract_rsa2_security_config)
362
+ RSA2_KEY_INFO=$(extract_rsa2_application_key_info)
347
363
 
348
- if is_rsa2_application_key_ready "$RSA2_KEY_INFO" "$RSA2_SECURITY_CONFIG"; then
364
+ if is_rsa2_application_key_ready "$RSA2_KEY_INFO"; then
349
365
  return 0
350
366
  fi
351
367
 
@@ -361,30 +377,35 @@ ensure_rsa2_application_key_ready() {
361
377
  return 1
362
378
  }
363
379
 
364
- ensure_alipay_public_key_exported() {
380
+ verify_key_and_export_alipay_public_key() {
365
381
  local APP_ID="$1"
366
382
 
367
383
  query_security_key "$APP_ID" || return 1
368
384
 
369
- RSA2_KEY_INFO=$(extract_rsa2_key_info)
370
- RSA2_SECURITY_CONFIG=$(extract_rsa2_security_config)
371
- ALIPAY_PUBLIC_KEY=$(echo "$RSA2_KEY_INFO" | jq -r '.alipayPublicKey // empty')
385
+ RSA2_KEY_INFO=$(extract_rsa2_application_key_info)
386
+ RSA2_ALIPAY_KEY_INFO=$(extract_rsa2_alipay_key_info)
387
+ ALIPAY_PUBLIC_KEY=$(extract_alipay_public_key "$RSA2_ALIPAY_KEY_INFO")
372
388
 
373
- if ! is_rsa2_application_key_ready "$RSA2_KEY_INFO" "$RSA2_SECURITY_CONFIG"; then
389
+ if ! is_rsa2_application_key_ready "$RSA2_KEY_INFO"; then
374
390
  echo "❌ 应用公钥尚未确认,禁止提交应用审核"
375
391
  return 1
376
392
  fi
377
393
 
378
394
  if [ -z "$ALIPAY_PUBLIC_KEY" ]; then
379
- echo "❌ 未获取到 RSA2 支付宝公钥,禁止提交应用审核"
395
+ echo "❌ 已确认应用公钥生效,但未获取到 RSA2 支付宝公钥,禁止提交应用审核"
380
396
  return 1
381
397
  fi
382
398
 
383
399
  if write_alipay_public_key "$APP_ID" "$ALIPAY_PUBLIC_KEY"; then
384
400
  echo "✅ 已确认应用公钥并导出支付宝公钥"
385
401
  echo "支付宝公钥保存路径: ~/.config/${APP_ID}-alipayPublicKey.keytext"
402
+ echo "ALIPAY_PUBLIC_KEY_EXPORT_STATUS=EXPORTED"
386
403
  echo "ALIPAY_PUBLIC_KEY_FILE=~/.config/${APP_ID}-alipayPublicKey.keytext"
404
+ else
405
+ echo "ALIPAY_PUBLIC_KEY_EXPORT_STATUS=MANUAL_CONFIGURATION_REQUIRED"
387
406
  fi
407
+
408
+ return 0
388
409
  }
389
410
 
390
411
  # ─── app list: 查询已有应用 ──────────────────────────────────────────────────
@@ -422,7 +443,7 @@ app_list() {
422
443
  elif (.resultObj | type) == "array" then
423
444
  .resultObj
424
445
  else
425
- []
446
+ null
426
447
  end
427
448
  ' 2>/dev/null)
428
449
  if [ -z "$APP_LIST" ] || ! echo "$APP_LIST" | jq -e 'type == "array"' >/dev/null 2>&1; then
@@ -433,17 +454,32 @@ app_list() {
433
454
  APP_COUNT=$(echo "$APP_LIST" | jq 'length')
434
455
 
435
456
  if [ "$APP_COUNT" -eq 0 ]; then
436
- echo "📋 暂无 ${APP_APPLICATION_TYPE} 应用,进入创建应用流程"
457
+ echo "📋 暂无 ${APP_APPLICATION_TYPE} 应用;记录新建候选分支,等待 Step 4 统一决策"
437
458
  echo "FLOW:CREATE_NEW"
438
459
  else
439
460
  # 实际返回的状态字段值是 "ON_LINE"(带下划线)
440
461
  ONLINE_APPS=$(echo "$APP_LIST" | jq -r '[.[] | select(.status == "ON_LINE")]')
462
+ NON_ONLINE_APPS=$(echo "$APP_LIST" | jq -r '[.[] | select(.status != "ON_LINE")]')
441
463
  ONLINE_COUNT=$(echo "$ONLINE_APPS" | jq 'length')
464
+ NON_ONLINE_COUNT=$(echo "$NON_ONLINE_APPS" | jq 'length')
442
465
  ONLINE_COUNT=${ONLINE_COUNT:-0}
466
+ NON_ONLINE_COUNT=${NON_ONLINE_COUNT:-0}
443
467
 
444
468
  if [ "$ONLINE_COUNT" -eq 0 ]; then
445
- echo "📋 暂无上线 ${APP_APPLICATION_TYPE} 应用,进入创建应用流程"
446
- echo "FLOW:CREATE_NEW"
469
+ echo "📋 当前已有以下尚未上线的 ${APP_APPLICATION_TYPE} 应用:"
470
+ echo ""
471
+ echo "| 应用ID | 应用名称 | 实际状态 |"
472
+ echo "|--------|----------|----------|"
473
+ for i in $(seq 0 $((NON_ONLINE_COUNT-1))); do
474
+ APP=$(echo "$NON_ONLINE_APPS" | jq ".[$i]")
475
+ APP_ID=$(echo "$APP" | jq -r '.appId // "未知"')
476
+ APP_NAME=$(echo "$APP" | jq -r '.appName // "未知"')
477
+ APP_STATUS=$(echo "$APP" | jq -r '.status // "未知"')
478
+ echo "| $APP_ID | $APP_NAME | $APP_STATUS |"
479
+ done
480
+ echo ""
481
+ echo "以上应用当前不可复用。请等待应用上线,或在了解现有应用状态后明确选择新建。"
482
+ echo "FLOW:PENDING_APPLICATIONS"
447
483
  else
448
484
  echo "📋 发现您已有以下上线 ${APP_APPLICATION_TYPE} 应用:"
449
485
  echo ""
@@ -457,6 +493,23 @@ app_list() {
457
493
  echo "| $((i+1)) | $APP_ID | $APP_NAME | 已上线 |"
458
494
  done
459
495
 
496
+ if [ "$NON_ONLINE_COUNT" -gt 0 ]; then
497
+ echo ""
498
+ echo "另有以下尚未上线的同类型应用,当前不可复用:"
499
+ echo ""
500
+ echo "| 应用ID | 应用名称 | 实际状态 |"
501
+ echo "|--------|----------|----------|"
502
+ for i in $(seq 0 $((NON_ONLINE_COUNT-1))); do
503
+ APP=$(echo "$NON_ONLINE_APPS" | jq ".[$i]")
504
+ APP_ID=$(echo "$APP" | jq -r '.appId // "未知"')
505
+ APP_NAME=$(echo "$APP" | jq -r '.appName // "未知"')
506
+ APP_STATUS=$(echo "$APP" | jq -r '.status // "未知"')
507
+ echo "| $APP_ID | $APP_NAME | $APP_STATUS |"
508
+ done
509
+ echo ""
510
+ echo "新建前请结合以上状态避免重复创建。"
511
+ fi
512
+
460
513
  echo ""
461
514
  echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
462
515
  echo " 请选择操作:"
@@ -499,6 +552,11 @@ app_create() {
499
552
 
500
553
  if [ "$SUCCESS" = "true" ]; then
501
554
  APP_ID=$(echo "$BUSINESS" | jq -r '.resultObj.appId // .data.appId // empty')
555
+ if [ -z "$APP_ID" ]; then
556
+ echo "❌ 应用创建返回成功,但未解析到 appId,禁止进入公钥设置流程"
557
+ echo "FLOW:ERROR"
558
+ exit 1
559
+ fi
502
560
  echo "✅ 应用创建成功"
503
561
  echo "APPLICATION_TYPE=$APP_APPLICATION_TYPE"
504
562
  echo "APP_ID=$APP_ID"
@@ -511,6 +569,12 @@ app_create() {
511
569
 
512
570
  # ─── app key: 设置应用公钥 ───────────────────────────────────────────────────
513
571
  app_key() {
572
+ if [ "$#" -ne 2 ] || [[ "${1:-}" == --* ]]; then
573
+ echo "❌ 参数格式错误:key 使用位置参数 <appId> <publicKey>,不支持 --app-id 等未定义选项"
574
+ echo "用法: bash app.sh key <appId> <publicKey>"
575
+ exit 1
576
+ fi
577
+
514
578
  APP_ID="$1"
515
579
  PUBLIC_KEY="$2"
516
580
 
@@ -546,7 +610,8 @@ app_key() {
546
610
  echo ""
547
611
  echo "请点击以下链接确认公钥设置:"
548
612
  echo ""
549
- echo "[点击确认公钥设置]($CONFIRM_PAGE_URL)(无法跳链时,请复制链接到网页浏览器打开)"
613
+ echo "提示:无法跳链时,请复制下方链接到网页浏览器打开。"
614
+ echo "[点击确认公钥设置]($CONFIRM_PAGE_URL)"
550
615
  echo ""
551
616
  echo "链接有效期为10分钟。"
552
617
  echo ""
@@ -564,6 +629,12 @@ app_key() {
564
629
 
565
630
  # ─── app audit: 提交应用审核 ─────────────────────────────────────────────────
566
631
  app_audit() {
632
+ if [ "$#" -ne 1 ] || [[ "${1:-}" == --* ]]; then
633
+ echo "❌ 参数格式错误:audit 使用位置参数 <appId>,不支持 --app-id 等未定义选项"
634
+ echo "用法: bash app.sh audit <appId>"
635
+ exit 1
636
+ fi
637
+
567
638
  APP_ID="$1"
568
639
 
569
640
  if [ -z "$APP_ID" ]; then
@@ -572,7 +643,7 @@ app_audit() {
572
643
  exit 1
573
644
  fi
574
645
 
575
- ensure_alipay_public_key_exported "$APP_ID" || exit 1
646
+ verify_key_and_export_alipay_public_key "$APP_ID" || exit 1
576
647
 
577
648
  REQUEST_JSON=$(jq -n --arg appId "$APP_ID" '{request:{appId:$appId}}')
578
649
 
@@ -602,6 +673,12 @@ app_audit() {
602
673
 
603
674
  # ─── app verify-key: 确认应用公钥已设置 ─────────────────────────────────────
604
675
  app_verify_key() {
676
+ if [ "$#" -lt 1 ] || [ "$#" -gt 2 ] || [[ "${1:-}" == --* ]]; then
677
+ echo "❌ 参数格式错误:verify-key 使用位置参数 <appId> [publicKey],不支持 --app-id 等未定义选项"
678
+ echo "用法: bash app.sh verify-key <appId> [publicKey]"
679
+ exit 1
680
+ fi
681
+
605
682
  APP_ID="$1"
606
683
  PUBLIC_KEY="${2:-}"
607
684
 
@@ -623,6 +700,12 @@ app_verify_key() {
623
700
 
624
701
  # ─── app reuse: 复用已有应用(查询安全密钥) ──────────────────────────────────
625
702
  app_reuse() {
703
+ if [ "$#" -ne 1 ] || [[ "${1:-}" == --* ]]; then
704
+ echo "❌ 参数格式错误:reuse 使用位置参数 <appId>,不支持 --app-id 等未定义选项"
705
+ echo "用法: bash app.sh reuse <appId>"
706
+ exit 1
707
+ fi
708
+
626
709
  APP_ID="$1"
627
710
 
628
711
  if [ -z "$APP_ID" ]; then
@@ -644,20 +727,29 @@ app_reuse() {
644
727
  fi
645
728
 
646
729
  if query_security_key "$APP_ID"; then
647
- RSA2_KEY_INFO=$(extract_rsa2_key_info)
648
- ALIPAY_PUBLIC_KEY=$(echo "$RSA2_KEY_INFO" | jq -r '.alipayPublicKey // empty')
730
+ RSA2_KEY_INFO=$(extract_rsa2_application_key_info)
731
+ RSA2_ALIPAY_KEY_INFO=$(extract_rsa2_alipay_key_info)
732
+ ALIPAY_PUBLIC_KEY=$(extract_alipay_public_key "$RSA2_ALIPAY_KEY_INFO")
649
733
 
650
- if [ -n "$ALIPAY_PUBLIC_KEY" ]; then
734
+ if ! is_rsa2_application_key_ready "$RSA2_KEY_INFO"; then
735
+ echo "⚠️ 未确认到已生效的 RSA2 应用公钥"
736
+ echo "FLOW:REUSE_NO_KEY"
737
+ exit 1
738
+ elif [ -n "$ALIPAY_PUBLIC_KEY" ]; then
651
739
  if write_alipay_public_key "$APP_ID" "$ALIPAY_PUBLIC_KEY"; then
652
740
  echo "✅ 已获取支付宝公钥"
653
741
  echo "支付宝公钥保存路径: ~/.config/${APP_ID}-alipayPublicKey.keytext"
742
+ echo "ALIPAY_PUBLIC_KEY_EXPORT_STATUS=EXPORTED"
654
743
  echo "ALIPAY_PUBLIC_KEY_FILE=~/.config/${APP_ID}-alipayPublicKey.keytext"
744
+ else
745
+ echo "ALIPAY_PUBLIC_KEY_EXPORT_STATUS=MANUAL_CONFIGURATION_REQUIRED"
655
746
  fi
656
747
  echo "APP_ID=$APP_ID"
657
748
  echo "FLOW:REUSE_SUCCESS"
658
749
  else
659
- echo "⚠️ 未找到 RSA2 公钥,请先设置应用公钥"
660
- echo "FLOW:REUSE_NO_KEY"
750
+ echo "❌ 已确认应用公钥生效,但未获取到 RSA2 支付宝公钥"
751
+ echo "FLOW:ERROR"
752
+ exit 1
661
753
  fi
662
754
  else
663
755
  echo "FLOW:ERROR"
@@ -666,7 +758,14 @@ app_reuse() {
666
758
  }
667
759
 
668
760
  # ─── 入口分发 ────────────────────────────────────────────────────────────────
669
- case "${1:-}" in
761
+ APP_COMMAND="${1:-}"
762
+ case "$APP_COMMAND" in
763
+ list|create|key|verify-key|audit|reuse)
764
+ require_command alipay-cli || exit 1
765
+ ;;
766
+ esac
767
+
768
+ case "$APP_COMMAND" in
670
769
  list)
671
770
  shift
672
771
  app_list "$@"