payment-kit 1.27.2 → 1.28.0

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 (184) hide show
  1. package/__blocklet__.js +37 -0
  2. package/api/ocap-1.30-subpath-shims.d.ts +35 -0
  3. package/api/src/crons/index.ts +10 -0
  4. package/api/src/crons/metering-subscription-detection.ts +12 -14
  5. package/api/src/crons/overdue-detection.ts +51 -74
  6. package/api/src/integrations/arcblock/nft.ts +6 -2
  7. package/api/src/integrations/arcblock/stake.ts +3 -2
  8. package/api/src/integrations/arcblock/token.ts +4 -4
  9. package/api/src/integrations/blocklet/notification.ts +1 -1
  10. package/api/src/integrations/ethereum/tx.ts +29 -0
  11. package/api/src/integrations/stripe/handlers/invoice.ts +70 -53
  12. package/api/src/integrations/stripe/handlers/payment-intent.ts +8 -1
  13. package/api/src/integrations/stripe/resource.ts +8 -0
  14. package/api/src/libs/audit.ts +32 -16
  15. package/api/src/libs/auth.ts +49 -2
  16. package/api/src/libs/chain-error.ts +31 -0
  17. package/api/src/libs/error.ts +15 -0
  18. package/api/src/libs/event.ts +42 -1
  19. package/api/src/libs/invoice.ts +69 -34
  20. package/api/src/libs/notification/template/customer-auto-recharge-daily-limit-exceeded.ts +1 -3
  21. package/api/src/libs/notification/template/customer-auto-recharge-failed.ts +1 -3
  22. package/api/src/libs/notification/template/customer-credit-grant-granted.ts +1 -3
  23. package/api/src/libs/notification/template/customer-credit-insufficient.ts +1 -3
  24. package/api/src/libs/notification/template/customer-credit-low-balance.ts +1 -3
  25. package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -3
  26. package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -3
  27. package/api/src/libs/notification/template/one-time-payment-refund-succeeded.ts +1 -3
  28. package/api/src/libs/notification/template/one-time-payment-succeeded.ts +1 -3
  29. package/api/src/libs/notification/template/subscription-renew-failed.ts +1 -3
  30. package/api/src/libs/notification/template/subscription-slippage-exceeded.ts +1 -3
  31. package/api/src/libs/notification/template/subscription-slippage-warning.ts +1 -3
  32. package/api/src/libs/notification/template/subscription-succeeded.ts +1 -1
  33. package/api/src/libs/pagination.ts +14 -9
  34. package/api/src/libs/payment.ts +25 -10
  35. package/api/src/libs/session.ts +1 -1
  36. package/api/src/libs/timing.ts +35 -0
  37. package/api/src/libs/util.ts +16 -15
  38. package/api/src/libs/wallet-migration.ts +72 -53
  39. package/api/src/queues/auto-recharge.ts +1 -1
  40. package/api/src/queues/credit-consume.ts +94 -12
  41. package/api/src/queues/credit-grant.ts +4 -0
  42. package/api/src/queues/event.ts +14 -2
  43. package/api/src/queues/invoice.ts +1 -0
  44. package/api/src/queues/payment.ts +83 -15
  45. package/api/src/queues/refund.ts +84 -71
  46. package/api/src/queues/subscription.ts +1 -0
  47. package/api/src/routes/checkout-sessions.ts +82 -43
  48. package/api/src/routes/connect/change-payment.ts +2 -0
  49. package/api/src/routes/connect/change-plan.ts +2 -0
  50. package/api/src/routes/connect/pay.ts +12 -3
  51. package/api/src/routes/connect/setup.ts +3 -1
  52. package/api/src/routes/connect/shared.ts +52 -39
  53. package/api/src/routes/connect/subscribe.ts +4 -1
  54. package/api/src/routes/credit-grants.ts +25 -17
  55. package/api/src/routes/donations.ts +2 -2
  56. package/api/src/routes/meter-events.ts +16 -6
  57. package/api/src/routes/payment-links.ts +1 -1
  58. package/api/src/routes/payment-methods.ts +1 -1
  59. package/api/src/routes/settings.ts +1 -1
  60. package/api/src/routes/tax-rates.ts +1 -1
  61. package/api/src/store/models/customer.ts +23 -1
  62. package/api/src/store/models/payment-method.ts +4 -0
  63. package/api/src/store/models/price.ts +23 -14
  64. package/api/tests/libs/wallet-migration.spec.ts +4 -4
  65. package/api/tests/queues/credit-consume-batch.spec.ts +5 -2
  66. package/api/tests/queues/credit-consume.spec.ts +8 -4
  67. package/api/tests/routes/credit-grants.spec.ts +1 -0
  68. package/blocklet.yml +1 -1
  69. package/cloudflare/MIGRATION-CHALLENGES.md +676 -0
  70. package/cloudflare/MIGRATION-RUNBOOK.md +777 -0
  71. package/cloudflare/README.md +499 -0
  72. package/cloudflare/STAGING-MIGRATION-GUIDE.md +602 -0
  73. package/cloudflare/build.ts +151 -0
  74. package/cloudflare/did-connect-auth.ts +527 -0
  75. package/cloudflare/docs/2026-04-22-sdk-1.30.9-upgrade-retro.md +324 -0
  76. package/cloudflare/docs/2026-04-24-queue-ops-followup.md +218 -0
  77. package/cloudflare/docs/cf-queues-ops-alert-analysis.md +663 -0
  78. package/cloudflare/docs/cf-workers-local-dev-and-fixes.md +284 -0
  79. package/cloudflare/docs/cleanup-tasks-2026-05.md +62 -0
  80. package/cloudflare/docs/payment-kit-platform-analysis-2026-04-20.md +354 -0
  81. package/cloudflare/frontend-shims/buffer-polyfill.ts +9 -0
  82. package/cloudflare/frontend-shims/js-sdk.ts +43 -0
  83. package/cloudflare/frontend-shims/mime-types.ts +46 -0
  84. package/cloudflare/frontend-shims/session.ts +24 -0
  85. package/cloudflare/frontend-shims/vite-plugin-noop.ts +6 -0
  86. package/cloudflare/index.html +40 -0
  87. package/cloudflare/migrate-to-d1.js +252 -0
  88. package/cloudflare/migrations/0001_initial_schema.sql +82 -0
  89. package/cloudflare/migrations/0002_indexes.sql +75 -0
  90. package/cloudflare/migrations/0003_locks_and_constraints.sql +18 -0
  91. package/cloudflare/run-build.js +390 -0
  92. package/cloudflare/scripts/test-decrypt.js +102 -0
  93. package/cloudflare/shims/arcblock-ws.ts +20 -0
  94. package/cloudflare/shims/axios-http-adapter.ts +4 -0
  95. package/cloudflare/shims/axios-lite.ts +117 -0
  96. package/cloudflare/shims/blocklet-sdk/auth-service.ts +33 -0
  97. package/cloudflare/shims/blocklet-sdk/cdn.ts +3 -0
  98. package/cloudflare/shims/blocklet-sdk/component-api.ts +35 -0
  99. package/cloudflare/shims/blocklet-sdk/component.ts +18 -0
  100. package/cloudflare/shims/blocklet-sdk/config.ts +8 -0
  101. package/cloudflare/shims/blocklet-sdk/did.ts +14 -0
  102. package/cloudflare/shims/blocklet-sdk/env.ts +12 -0
  103. package/cloudflare/shims/blocklet-sdk/eventbus.ts +3 -0
  104. package/cloudflare/shims/blocklet-sdk/fallback.ts +3 -0
  105. package/cloudflare/shims/blocklet-sdk/index.ts +11 -0
  106. package/cloudflare/shims/blocklet-sdk/logger.ts +11 -0
  107. package/cloudflare/shims/blocklet-sdk/middlewares.ts +15 -0
  108. package/cloudflare/shims/blocklet-sdk/notification.ts +11 -0
  109. package/cloudflare/shims/blocklet-sdk/security.ts +53 -0
  110. package/cloudflare/shims/blocklet-sdk/session.ts +8 -0
  111. package/cloudflare/shims/blocklet-sdk/verify-sign.ts +38 -0
  112. package/cloudflare/shims/blocklet-sdk/wallet-authenticator.ts +3 -0
  113. package/cloudflare/shims/blocklet-sdk/wallet-handler.ts +6 -0
  114. package/cloudflare/shims/blocklet-sdk/wallet.ts +103 -0
  115. package/cloudflare/shims/cookie-parser.ts +3 -0
  116. package/cloudflare/shims/cors.ts +21 -0
  117. package/cloudflare/shims/cron.ts +189 -0
  118. package/cloudflare/shims/crypto-js-warn.ts +7 -0
  119. package/cloudflare/shims/did-space-js.ts +17 -0
  120. package/cloudflare/shims/did-space.ts +11 -0
  121. package/cloudflare/shims/error.ts +18 -0
  122. package/cloudflare/shims/express-compat/index.ts +80 -0
  123. package/cloudflare/shims/express-compat/types.ts +41 -0
  124. package/cloudflare/shims/fastq.ts +105 -0
  125. package/cloudflare/shims/lock.ts +115 -0
  126. package/cloudflare/shims/mime-types.ts +56 -0
  127. package/cloudflare/shims/nedb-storage.ts +9 -0
  128. package/cloudflare/shims/node-child-process.ts +9 -0
  129. package/cloudflare/shims/node-fs.ts +20 -0
  130. package/cloudflare/shims/node-http.ts +13 -0
  131. package/cloudflare/shims/node-https.ts +4 -0
  132. package/cloudflare/shims/node-misc.ts +15 -0
  133. package/cloudflare/shims/node-net.ts +8 -0
  134. package/cloudflare/shims/node-os.ts +14 -0
  135. package/cloudflare/shims/node-tty.ts +8 -0
  136. package/cloudflare/shims/node-zlib.ts +17 -0
  137. package/cloudflare/shims/noop.ts +26 -0
  138. package/cloudflare/shims/payment-vendor.ts +14 -0
  139. package/cloudflare/shims/querystring.ts +12 -0
  140. package/cloudflare/shims/queue.ts +585 -0
  141. package/cloudflare/shims/rolldown-runtime.ts +43 -0
  142. package/cloudflare/shims/sequelize-d1/datatypes.ts +24 -0
  143. package/cloudflare/shims/sequelize-d1/helpers.ts +46 -0
  144. package/cloudflare/shims/sequelize-d1/index.ts +34 -0
  145. package/cloudflare/shims/sequelize-d1/model.ts +1157 -0
  146. package/cloudflare/shims/sequelize-d1/operators.ts +293 -0
  147. package/cloudflare/shims/sequelize-d1/retry.ts +85 -0
  148. package/cloudflare/shims/sequelize-d1/sequelize-class.ts +119 -0
  149. package/cloudflare/shims/sequelize-d1/timing.ts +81 -0
  150. package/cloudflare/shims/sequelize-d1/types.ts +35 -0
  151. package/cloudflare/shims/stripe-cf.ts +29 -0
  152. package/cloudflare/shims/ws-lite.ts +103 -0
  153. package/cloudflare/shims/xss.ts +3 -0
  154. package/cloudflare/tests/shims/cron.spec.ts +210 -0
  155. package/cloudflare/tests/shims/queue-scheduled.spec.ts +186 -0
  156. package/cloudflare/vite.config.ts +162 -0
  157. package/cloudflare/worker.ts +1553 -0
  158. package/cloudflare/wrangler.json +63 -0
  159. package/cloudflare/wrangler.jsonc +69 -0
  160. package/cloudflare/wrangler.staging.json +66 -0
  161. package/cloudflare/wrangler.toml +28 -0
  162. package/jest.config.js +4 -12
  163. package/package.json +26 -22
  164. package/src/app.tsx +62 -4
  165. package/src/components/customer/link.tsx +9 -13
  166. package/src/components/customer/notification-preference.tsx +3 -2
  167. package/src/components/filter-toolbar.tsx +4 -0
  168. package/src/components/invoice/list.tsx +9 -1
  169. package/src/components/invoice-pdf/utils.ts +2 -1
  170. package/src/components/layout/admin.tsx +39 -5
  171. package/src/components/layout/user-cf.tsx +77 -0
  172. package/src/components/payment-intent/actions.tsx +23 -3
  173. package/src/components/safe-did-address.tsx +75 -0
  174. package/src/libs/patch-user-card.ts +25 -0
  175. package/src/libs/util.ts +5 -7
  176. package/src/pages/admin/billing/meter-events/index.tsx +4 -0
  177. package/src/pages/admin/customers/customers/detail.tsx +2 -2
  178. package/src/pages/admin/customers/customers/index.tsx +2 -2
  179. package/src/pages/admin/overview.tsx +3 -1
  180. package/src/pages/customer/subscription/detail.tsx +4 -4
  181. package/tsconfig.api.json +1 -6
  182. package/tsconfig.json +3 -4
  183. package/tsconfig.types.json +2 -1
  184. package/vite.config.ts +6 -1
@@ -0,0 +1,324 @@
1
+ # SDK 1.26.3 → 1.30.9 升级完整回顾
2
+
3
+ **时间**:2026-04-20 ~ 2026-04-22
4
+ **涉及仓库**:`payment-kit` + `abt-wallet`
5
+ **主要目标**:升级 `@arcblock/*` / `@ocap/*` 到 1.30.9,并在可行时切到 CBOR 编码
6
+
7
+ ---
8
+
9
+ ## 目录
10
+
11
+ 1. [升级目标与产出](#一升级目标与产出)
12
+ 2. [遇到的坑(按层级分类)](#二遇到的坑按层级分类)
13
+ 3. [每个问题的成因与解决](#三每个问题的成因与解决)
14
+ 4. [如果重新做一次:完整 Checklist](#四如果重新做一次完整-checklist)
15
+ 5. [尚未解决的事项](#五尚未解决的事项)
16
+
17
+ ---
18
+
19
+ ## 一、升级目标与产出
20
+
21
+ ### 起点
22
+ - `@ocap/*`、`@arcblock/*` 在 1.26.3 系列(protobuf 原生,无 CBOR)
23
+ - payment-kit 已迁移到 Cloudflare Workers 但 SDK 未升
24
+ - abt-wallet 仍使用 1.26.3 SDK
25
+
26
+ ### 1.30.9 带来的主要变化
27
+ | 变化 | 意义 | 对消费者影响 |
28
+ |---|---|---|
29
+ | `@ocap/client` 默认 → CBOR | wire 格式切换 | 必须改用 `/legacy` subpath 或接受 CBOR |
30
+ | `@arcblock/did-util` 拆 `/cbor` `/protobuf` 两个 subpath | 按需引入,减小 bundle | 要手动选对子路径才省体积 |
31
+ | `@ocap/asset/mint/client` 加入 CBOR-only 版本 | 同上 | 同上 |
32
+ | `@scure/bip32` 替换 `@wangshijun/hdkey` | 移除 elliptic 依赖链 | **seed 严格 16-64 字节**(break change)|
33
+ | `@noble/curves` v2 替换 elliptic | 安全审计 + bundle -45% | API 可能不同 |
34
+ | Joi → Valibot schema 校验 | 更快、更小 | 某些 key-order 敏感路径要改 |
35
+ | `@ocap/asset/mint/server`、`@arcblock/did-util` 顶层入口 | 需要显式 `encoding` 参数 | 旧代码会 break |
36
+ | itx address derivation 跟 `client.txEncoding` 对齐 | 修复编码不一致 bug | 纯 UPSIDE |
37
+
38
+ ### 最终产出
39
+ - `payment-kit/feat/cf-workers-migration`(legacy 分支):1.30.9 + `/legacy` wire 格式 + Node 后端 pnpm override pin 到 1.29.27
40
+ - `payment-kit/feat/cbor-migration-pk`(CBOR 分支):1.30.9 + CBOR wire 格式,bundle 2.76 MB / gzip 819 KiB(比 legacy -21% raw / -11% gzip,google-protobuf 完全清除)
41
+ - `abt-wallet/upgrade-sdk-to-1.30.9-dual-decode`:4.19.x + dual-decode 能力(WIP)
42
+ - 两个 smoke test 脚本沉淀:`tools/sdk-v3-smoke.ts`、`tools/l2-wallet-chain-smoke.ts`
43
+
44
+ ---
45
+
46
+ ## 二、遇到的坑(按层级分类)
47
+
48
+ ### A. 依赖管理层(6 个)
49
+
50
+ | # | 问题 | 难度 | 首次触发时间 |
51
+ |---|---|---|---|
52
+ | A1 | pnpm 不认 `resolutions` 字段(yarn-only),顶层 1.30.9 强制无效 | 高 | Phase A 后期 |
53
+ | A2 | `@blocklet/sdk@1.17.11` 内嵌 `@arcblock/did-connect-js@1.29.8`,pnpm 扁平化后它 `require('@ocap/client')` 命中顶层 1.30.9,**Node 后端悄无声息切到 CBOR** | **极高** | 用户主动追问发现 |
54
+ | A3 | `@arcblock/did-connect-js` 发布版本最高仅到 1.30.1,无法对齐 1.30.9 | 中 | Phase A 规划 |
55
+ | A4 | `@wangshijun/hdkey` → `@scure/bip32` 严格 seed 长度,旧钱包 `fromAppDid('')` 炸 `seed length must be between 128 and 512 bits; got 152` | 高 | abt-wallet 编译后首次登录 |
56
+ | A5 | 同一 npm 树下两个 webpack 副本(react-scripts 5.76.2 vs top-level 5.106.2),plugin 实例跨版本 `.tap()` 失败 | 极高 | 钱包 build |
57
+ | A6 | nested packages 的 @ocap/client 无法通过 resolution 强制 pin,需 `pnpm.overrides` 的 `>` 语法显式隔离 | 高 | A2 的解决过程中 |
58
+
59
+ ### B. webpack / bundler 打包层(5 个)
60
+
61
+ | # | 问题 | 难度 |
62
+ |---|---|---|
63
+ | B1 | `@ocap/message/esm/patch.mjs` 的 Rolldown `__require(createRequire(import.meta.url))` 在浏览器得到 noop,炸 "Cannot read properties of undefined (reading 'Map')" | 极高 |
64
+ | B2 | CRA 5 `ModuleScopePlugin` 阻止所有 alias 指向 `src/` 之外的文件 | 高 |
65
+ | B3 | CRA webpack 规则里 `oneOf` 的 file-loader fallback 把 `.cjs` 当静态资源,`require('@ocap/mcrypto')` 返回 URL 字符串而不是模块 → `mcrypto.Hasher === undefined` | **极高** |
66
+ | B4 | webpack ESM↔CJS interop:CJS consumer `require('@ocap/mcrypto')` 经 ESM 路径加载时,命名导出 `.Hasher` 在某些场景下丢失 | 高 |
67
+ | B5 | CRA 默认不编译 `.cjs` 文件(babel-loader test 仅 `/\.(m?js|tsx?)$/`),需显式加 rule | 中 |
68
+
69
+ ### C. SDK 运行时层(4 个)
70
+
71
+ | # | 问题 | 难度 |
72
+ |---|---|---|
73
+ | C1 | `@ocap/client` 默认 entry 即使在 CBOR 模式下,仍静态引入 `@ocap/proto/gen/*_pb.js`(1.2 MB protobuf schema),bundle 未按 commit message 宣传那样瘦身 | 高 |
74
+ | C2 | `@ocap/client/encode` 的 JSDoc 写 "protobuf 二进制" 但实际代码 `require('@ocap/message/cbor')` 输出 CBOR,文档与实现不符 | 中 |
75
+ | C3 | `@arcblock/did-util` 顶层入口会无条件拉进 `protobuf.mjs` → `@ocap/proto/runtime`,即使消费者只用到 `toStakeAddress` 等通用工具 | 高 |
76
+ | C4 | `@ocap/asset` 顶层入口同理会拉 `mint/server.mjs`(dual-encoding)→ 再拉 `@arcblock/did-util` 顶层 | 高 |
77
+
78
+ ### D. 应用代码层(4 个)
79
+
80
+ | # | 问题 | 难度 |
81
+ |---|---|---|
82
+ | D1 | `@ocap/client/legacy` wire 格式下 tx bytes 是 protobuf,CBOR 钱包无法解;反之 CBOR bytes 也被 legacy 钱包拒 → `INVALID_CHECK: Unmatched end-group tag` | 中(理解后)|
83
+ | D2 | 1.30.9 `toTokenAddress(payload)` 等函数签名加 `encoding` 参数为必填,不传报 `Expected 2 arguments, but got 1` | 中 |
84
+ | D3 | 1.30.9 `preMintFromFactory({...})` 要求 `encoding` 字段,原来的调用处不传会 type check 失败 | 中 |
85
+ | D4 | payment-kit 多个业务文件用顶层 `@arcblock/did-util` / `@ocap/asset`,变相带进整条 protobuf 链 | 中(追踪到之后)|
86
+
87
+ ### E. 体验层(非 bug 但影响调试)(3 个)
88
+
89
+ | # | 问题 |
90
+ |---|---|
91
+ | E1 | payment-kit 服务端的 `INVALID_CHECK` / `INSUFFICIENT_FUND` 错误未结构化透传给钱包,钱包仅看到 "jwt response empty or invalid",完全丢失诊断信息 |
92
+ | E2 | `new Error('INVALID_CHECK', 'actual reason')` —— 第二参数被静默丢弃,钱包日志只剩错误码 |
93
+ | E3 | wrangler dev 热重载有时对 ESM `import path` 大改动不敏感,需重启才生效 |
94
+
95
+ ---
96
+
97
+ ## 三、典型问题深度分析
98
+
99
+ > 二节共记录 22 个问题;本节挑其中**耗时最长、最具代表性的 7 个**做深度解析,其余在"解决"栏里用一行概述。
100
+
101
+ ### A1: pnpm 不认 `resolutions`
102
+
103
+ **成因**:pnpm 只识别 `pnpm.overrides`,顶层 package.json 的 `resolutions` 是 yarn 字段。
104
+ **症状**:所有"强制 1.30.9"的意图全部无效,nested 依赖照样装旧版本。
105
+ **解决**:把需要的 override 迁到 `pnpm.overrides`,支持 `A>B` 语法定点覆盖。
106
+ **代码位置**:`payment-kit/package.json`
107
+
108
+ ### A2: @blocklet/sdk 嵌套 did-connect-js 偷偷用 CBOR
109
+
110
+ **成因**:
111
+ ```
112
+ @blocklet/sdk@1.17.11
113
+ └── node_modules/@arcblock/did-connect-js@1.29.8 (pinned, 不是 ^)
114
+ └── require('@ocap/client') # pnpm 扁平化后命中顶层 1.30.9 (CBOR!)
115
+ ```
116
+ **症状**:Node 后端 `blocklet dev` 悄悄发 CBOR 字节,与 master 钱包的 protobuf 解码器不兼容。**此 bug 在我们以为"Node 侧跟 CF 不一样"时隐藏了数小时**。
117
+ **解决**:
118
+ ```json
119
+ "pnpm": {
120
+ "overrides": {
121
+ "@arcblock/did-connect-js>@ocap/client": "1.29.27"
122
+ }
123
+ }
124
+ ```
125
+ pnpm 装了一份 1.29.27 在 `@blocklet/sdk/node_modules/@ocap/client`,Node resolution 向上查找时命中。顶层和 CF Workers 的 1.30.9 完全不受影响。
126
+
127
+ ### A4: @scure/bip32 seed 长度
128
+
129
+ **成因**:旧 `@wangshijun/hdkey` 宽容地接受任意长度 seed;`@scure/bip32` 严格 16-64 字节。wallet 的 `fromAppDid('', root.base64Seed, ...)` 里 `base64Seed` 是 base64 编码的字符串,直接当 bytes 传会得到 152 字节。
130
+ **症状**:`HDKey: seed length must be between 128 and 512 bits; got 152`
131
+ **解决**:
132
+ ```js
133
+ const seedLike = (root?.base64Seed || '').slice(0, 64);
134
+ const newDID = fromAppDid('', seedLike, DID_TYPE_ARCBLOCK, 0);
135
+ ```
136
+ **副作用 ⚠️**:现有用户的 wallet DID **会变**(seed 换算路径不同)。升级前必须评估用户数据迁移。
137
+
138
+ ### A5: Dual webpack
139
+
140
+ **成因**:
141
+ - `react-scripts` 内 `node_modules/webpack@5.76.2`
142
+ - `node_modules/webpack@5.106.2`(被某个 dev dep 引入)
143
+
144
+ `config-overrides.js` 的 `require('webpack')` 命中 5.106,但 compiler 创建用 5.76。`IgnorePlugin` 实例在 apply 时调用 `compiler.hooks.normalModuleFactory.beforeResolve.tap(...)` 这个 hook 在两个版本间 shape 不同。
145
+ **症状**:`Cannot read properties of undefined (reading 'tap')`
146
+ **解决**:
147
+ ```js
148
+ const webpack = require('react-scripts/node_modules/webpack');
149
+ ```
150
+ 显式锁到 react-scripts 自带的 webpack。
151
+
152
+ ### B1: Rolldown __require shim
153
+
154
+ **成因**:1.30.x 部分 `@ocap/*` 包用 Rolldown 打包,其 ESM runtime 里 `__require = createRequire(import.meta.url)` 在 Node 里 OK,在 webpack 浏览器 bundle 里 createRequire 被 stub 成 noop。
155
+ **症状**:`@ocap/message/esm/patch.mjs` 模块 init 时 `__require('google-protobuf').Map` 炸。
156
+ **解决**:`mocks/rolldown-runtime-shim.js` + `webpack.NormalModuleReplacementPlugin` 按正则替换 `_virtual/rolldown_runtime.mjs`:
157
+ ```js
158
+ const __require = (id) => {
159
+ const loader = __requireModuleMap[id];
160
+ if (!loader) throw new Error(...);
161
+ return loader();
162
+ };
163
+ ```
164
+ 静态 switch 因为 webpack 只能 bundle 它能静态分析的模块。
165
+
166
+ ### B3 + B5: `.cjs` 当静态资源
167
+
168
+ **成因**:
169
+ 1. CRA 的 `oneOf` 最后一条是 file-loader fallback,match 所有未识别扩展名(包括 `.cjs`)。
170
+ 2. babel-loader 的 test 是 `/\.(m?js|tsx?)$/`,**不含 `.cjs`**。
171
+ 3. 结果 `.cjs` 文件被当作静态资源,`require('.../index.cjs')` 返回 URL 字符串。
172
+
173
+ **症状**:`mcrypto_1.Hasher === undefined`(因为 mcrypto_1 其实是一个路径字符串)。
174
+
175
+ **解决**:webpack rule 注入到 `oneOf` 顶部:
176
+ ```js
177
+ for (const rule of config.module.rules) {
178
+ if (Array.isArray(rule.oneOf)) {
179
+ rule.oneOf.unshift({
180
+ test: /[\\/]node_modules[\\/]@ocap[\\/].*\.cjs$/,
181
+ type: 'javascript/auto',
182
+ });
183
+ break;
184
+ }
185
+ }
186
+ ```
187
+ **关键点**:scope **只限 `@ocap/*`**,否则会把 axon 等 Node-only CJS 也拉进来,触发 `Can't resolve 'net'`。
188
+
189
+ ### C1 + D4: Bundle 没瘦身的真相
190
+
191
+ **Phase 2 commit message 宣称 "drops ~300KB google-protobuf"**,实测 `cfa2fb51` 只改了两个点,protobuf 还在 bundle 里,**实际只省了 5 KiB gzip**。
192
+
193
+ **根因追踪(耗时最长的 bug)**:payment-kit 业务代码 6 处从顶层入口 import:
194
+ ```
195
+ api/src/libs/util.ts → import { toStakeAddress } from '@arcblock/did-util'
196
+ api/src/libs/wallet-migration.ts → import { toDelegateAddress, toStakeAddress }
197
+ api/src/integrations/arcblock/stake.ts
198
+ api/src/integrations/arcblock/token.ts
199
+ api/src/routes/connect/shared.ts
200
+ api/src/integrations/arcblock/nft.ts → '@ocap/asset'(非 mint/client)
201
+ ```
202
+
203
+ `@arcblock/did-util` 顶层无条件 `import './protobuf.mjs'` → `@ocap/proto/lib/runtime.js` → 拖入 1.2 MB `*_pb.js` + 246 KB google-protobuf。
204
+
205
+ **解决**:改 6 个 import 到 `/cbor` / `/mint/client` 子路径。
206
+ **收益**:raw -735 KB (-21%)、gzip -103 KiB (-11%)。
207
+
208
+ **本质教训**:**CBOR 不自动瘦身 bundle**。切 wire 格式只是第一步,业务代码必须主动用 CBOR-only 子路径才能真省。
209
+
210
+ ---
211
+
212
+ ## 四、如果重新做一次:完整 Checklist
213
+
214
+ ### Phase 0:前置调研(1-2 天)
215
+
216
+ - [ ] **读一遍 upstream CHANGELOG**,特别关注:
217
+ - 签名 / 接口变化(`encoding` 参数是否必填?)
218
+ - 依赖链路变化(`@wangshijun/hdkey` → `@scure/bip32` 这类替换)
219
+ - 新 subpath 是否已经存在(`/cbor`、`/mint/client`)
220
+ - [ ] **画出完整依赖树**:`pnpm why @ocap/client`,找所有嵌套版本。标记谁 pin 了哪个。
221
+ - [ ] **确认包管理器**:pnpm 还是 yarn?这决定 override 语法。
222
+ - [ ] **列举自己业务代码对顶层 import 的使用**:`grep -rn "from '@arcblock/did-util'" src/`、`from '@ocap/asset'`、`from '@ocap/client'`。这些都是潜在的 "顶层 aggregate entry → 肥胖" 点。
223
+ - [ ] **准备"legacy 中间态"作为回退点**:切 wire 格式前,先让 SDK 升级在 legacy/protobuf 下稳定。
224
+
225
+ ### Phase 1:SDK 升级(1-2 天)
226
+
227
+ - [ ] bump `package.json`,`pnpm install`
228
+ - [ ] **如果有 pnpm + 嵌套依赖**:先跑 `pnpm why @ocap/client | @ocap/message` 看扁平化后的版本,可能需要 `pnpm.overrides` 定点隔离
229
+ - [ ] **写 L1 smoke 脚本**(Node 不经 bundler 直接测 SDK),验证:
230
+ - 新 SDK 的 `encode*Tx` / `sign*Tx` / `multiSign*Tx` 全部注册
231
+ - 常见 tx 类型(V2、V3、rollup)编码/解码 roundtrip 成功
232
+ - [ ] **写 L2 smoke 脚本**(mirror 业务代码调用模式),避免先 build bundle 才发现 SDK 用法变了
233
+ - [ ] 对付 build-time 问题:
234
+ - [ ] Rolldown shim(如果包用 Rolldown ESM)
235
+ - [ ] `.cjs` 加入 JS 解析 rule
236
+ - [ ] webpack 版本对齐(dual webpack 时锁到 react-scripts 自带版本)
237
+ - [ ] CRA ModuleScopePlugin 视情况 remove
238
+ - [ ] 对付 breaking changes:
239
+ - [ ] 搜所有 `fromAppDid` / `fromMasterSeed` 用法,检查 seed 长度
240
+ - [ ] 搜所有 `toXxxAddress(payload)` / `preMintFromFactory({...})`,加 `encoding` 参数
241
+ - [ ] 搜所有 Joi schema,确认 Valibot key-order 敏感点
242
+
243
+ ### Phase 2:legacy 中间态(半天)
244
+
245
+ - [ ] 把所有 `@ocap/client` 改 `@ocap/client/legacy`,继续 protobuf wire
246
+ - [ ] **部署 legacy 1.30.9 到 staging**,先让它跑通跟 master 钱包的 E2E
247
+ - [ ] 这个中间态作为"SDK 升级独立变量"的验证点:如果这一步不通,回退容易
248
+
249
+ ### Phase 3:CBOR 迁移(1-2 天,独立分支)
250
+
251
+ - [ ] 新分支
252
+ - [ ] `@ocap/client/legacy` → `@ocap/client` 默认
253
+ - [ ] did-connect-auth 的 txEncoder 换 `@ocap/client/encode` 的 `createTxEncoder`
254
+ - [ ] **pre-warm `fetchContext(chainHost)`** 在 `initAuth` 时调一次,避免首次请求 8s timeout
255
+ - [ ] **搜索所有 `@arcblock/did-util` 顶层 import,改 `/cbor`**(这一步是 bundle 瘦身的真正来源)
256
+ - [ ] **同理 `@ocap/asset` → `/mint/client`**
257
+ - [ ] 对应的 tsconfig `paths` 可能要加 `.d.mts` 映射(看 moduleResolution)
258
+ - [ ] Wallet 端必须有 dual-decode 能力(检查 CBOR tag `0xd9d9f7`,按需选 CBOR/protobuf 解码器)
259
+
260
+ ### Phase 4:验证
261
+
262
+ - [ ] **bundle 体积实测**:用 esbuild meta.json 看 top 包,**不要信 commit message**
263
+ - [ ] **Node 后端单独测**:`blocklet dev` 能起来、pre-start tsc 过、能跟 master 钱包通
264
+ - [ ] **CF Workers 单独测**:`wrangler dev` 起来,扫 QR 能走通支付流
265
+ - [ ] **扫 QR 测实际支付**:要一笔真的上链 tx hash 作为"迁移成功"证据
266
+ - [ ] **错误路径也测**:用 balance 不足的钱包扫,看错误是否合理透传(当前 payment-kit 有 error-surface bug)
267
+
268
+ ### Phase 5:收尾
269
+
270
+ - [ ] 两个分支都 push 到 origin,作为"legacy 回退线"和"CBOR 前进线"并存
271
+ - [ ] 旧的诊断脚本(sdk-v3-smoke、l2-wallet-chain-smoke)归档到 `tools/`,下次升级复用
272
+ - [ ] 写这份回顾文档(or update 本文档),记录坑和学到的东西
273
+
274
+ ---
275
+
276
+ ## 五、尚未解决的事项
277
+
278
+ | 项目 | 状态 | 影响 |
279
+ |---|---|---|
280
+ | **abt-wallet V3 multi-sign "o is not a function"** | 未定位 | 浏览器扩展里 V3 多签走不通,原因可能是 webpack tree-shake 或 Rolldown shim 未覆盖。L1 smoke 证明不是 SDK bug |
281
+ | **payment-kit 错误透传** | CBOR 分支已部分修复(commit `715ff9ae`:structured chain error parsing);legacy 分支待合入 | 链拒 tx 时服务端返回破损 JWT,钱包只看到 "jwt empty" |
282
+ | **`new Error('CODE', 'reason')` 参数丢失** | 已知 bug | 钱包十余处 throw 点丢 reason,调试困难 |
283
+ | **钱包跨账户智能调度** | 产品 roadmap | 余额不够时应提示内部转账,而非 hard error |
284
+ | **`.mts` type 解析(CBOR 分支)** | tsconfig 未更新 | `blocklet dev` 的 pre-start tsc 报 6 处 "Cannot find module",不影响 esbuild |
285
+ | **wallet DID 因 seed slice(0,64) 改变** | 未测 | 1.26.3 → 1.30.9 升级后,老用户的 wallet DID 可能变。**生产升级前必须做数据迁移评估** |
286
+ | **官方 @blocklet/sdk 升级到 1.30.x** | 等上游 | 现在用 pnpm override 定点 pin 1.29.27,等 SDK 官方发布 1.18.x 对齐 1.30.9 再彻底解决 |
287
+
288
+ ---
289
+
290
+ ## 附录:关键 commit 索引
291
+
292
+ ### payment-kit
293
+ ```
294
+ feat/cf-workers-migration (legacy 主线)
295
+ e5cb9fdb test(tools): SDK + wallet-chain smoke scripts
296
+ e09e404b fix(deps): pin did-connect-js's @ocap/client to 1.29.27
297
+ e89b73a9 fix(api): close TS type gaps after 1.30.9
298
+ f3aef135 fix(cloudflare): enable CORS on /__blocklet__.js
299
+ 54c98f71 fix(cloudflare): adapt payment to 1.30.9 wallet compat
300
+ b6d8837a chore(deps): bump @arcblock/@ocap SDK to ^1.30.9
301
+
302
+ feat/cbor-migration-pk (CBOR 线)
303
+ 715ff9ae feat(api): structured chain error parsing for payment flow ← 部分修 E1
304
+ 93028445 fix(api): annotate implicit-any params in payment.ts
305
+ 6727a62a perf(cloudflare): drop google-protobuf from bundle (-720KB)
306
+ 2c6d2ddd fix(cloudflare): pre-warm chain context cache at initAuth
307
+ cfa2fb51 feat(cloudflare): Phase 2 — CBOR default
308
+ ```
309
+
310
+ ### abt-wallet
311
+ ```
312
+ feat/cbor-migration-design (早期激进尝试,含审计文档)
313
+ 11953f9f docs(audit): honest Phase 1 change review
314
+ 9ceaf3a8 chore(release): 4.20.0 — dual-mode CBOR migration
315
+ 57668a5a fix(wallet-did): slice encrypted seed to 64 bytes ← 关键修复
316
+
317
+ feat/sdk-upgrade-1.30-legacy (我的 Phase A 尝试)
318
+ 73eb0a2e fix(build): force @ocap/mcrypto to CJS entry
319
+ c33ef404 fix(build): pin webpack in config-overrides
320
+ 36c9401b fix(chain): pin @ocap/client imports to /legacy
321
+
322
+ upgrade-sdk-to-1.30.9-dual-decode (用户自建的 WIP 分支)
323
+ 667d6d49 WIP: Stage 1 upgrade SDK 1.26.3 -> 1.30.9 (legacy only)
324
+ ```
@@ -0,0 +1,218 @@
1
+ # CF Queues 免费额度连续触顶 — 2026-04-24 现状更新与部署记录
2
+
3
+ | 字段 | 值 |
4
+ |---|---|
5
+ | 状态 | 🔴 每日 10k 免费额度连续 14 天被打穿;Plan 8 + Plan 13 已部署,效果待验证 |
6
+ | 触发事件 | 2026-04-23 23:58 UTC CF 邮件:"You have reached the daily Cloudflare Queues free tier limit of 10000 operations. Attempts to send a message to a queue will fail until the limit resets at 2026-04-25 at 00:00:00 UTC." |
7
+ | 受影响环境 | ArcBlock 主账号 `983482dc52a0aee5dbd7f986840a6190` / `staging-aigne-hub-payment-kit` |
8
+ | 本文定位 | **更正并替代** `cf-queues-ops-alert-analysis.md` 与 `payment-kit-platform-analysis-2026-04-20.md` 中与现状冲突的预期值和承诺阈值。原文档保留以备历史溯源,勘误对照详见 §5。|
9
+
10
+ ---
11
+
12
+ ## 1. 一句话结论
13
+
14
+ **账号是免费套餐(不是 $5/月付费),Queue 免费配额是"每天 10,000 ops"(不是"每月 1,000,000 ops");自 2026-04-11 起连续 14 天超限,2026-04-24 06:00 UTC 首次实测触发 send() 拒绝,约 18 小时队列完全停写。** Plan 8(`runAllScheduledJobs` D1 合并)+ Plan 13(`depositVault` 合并 + `creditQueue` 恢复批处理)于 2026-04-24 12:33 UTC 部署,效果需 04-25 UTC 完整一日数据才能评估。
15
+
16
+ ---
17
+
18
+ ## 2. 事实更正(与旧文档冲突的关键点)
19
+
20
+ ### 2.1 账号是 **免费套餐**,不是付费 $5
21
+
22
+ | 证据 | 内容 |
23
+ |---|---|
24
+ | CF 邮件原文(2026-04-23 23:58 UTC)| `"daily Cloudflare Queues free tier limit of 10000 operations"` + `"upgrade your (ArcBlock) account to the Paid plan"` |
25
+ | API `/accounts/:id/subscriptions` | 返回空数组(无任何付费订阅) |
26
+ | Workers account settings | `default_usage_model: "standard"`(仅表示计费模型类别,不表示是否付费) |
27
+
28
+ **影响的旧文档陈述**:
29
+ - `cf-queues-ops-alert-analysis.md` §1.1 / §8.2 / §17.2 / §18.3 多处假设"1M/月免费额度"
30
+ - `payment-kit-platform-analysis-2026-04-20.md` §3.1 成本表整表基于"$5/月付费"基线
31
+
32
+ ### 2.2 Queue 免费配额是 **10k ops/day**,不是 1M ops/month
33
+
34
+ - 每日 UTC 00:00 重置,达到 10k 后 `queue.send()` 返回错误直到次日
35
+ - 1 条消息 = 1 Write + 1 Read + 1 Delete = **3 ops**,所以 ~3,333 条消息/天就打满
36
+ - 付费 Workers Paid($5/月)才有月度 1M ops 含额度 + $0.40/M 超额
37
+
38
+ ### 2.3 `cf-queues-ops-alert-analysis.md §18` 运维承诺已失效
39
+
40
+ | 原文 | 现状 |
41
+ |---|---|
42
+ | O-1 `CF Queue 日 ops(稳态)≤ 5,000 / day` | 实际 10,000–13,000 / day |
43
+ | O-2 `月度 ops 占 1M 免费额度 ≤ 20%` | 前提错(免费是日度 10k) |
44
+ | O-3 `修复后 30 天内告警触发频次 = 0` | 04-23 再次收到每日限额告警,04-24 触发 send() 拒绝 |
45
+
46
+ 原文承诺的"Plan 1 + PR #1341 部署后 ops 降到 2-4k/day"**未达成**,原因:
47
+ 1. 业务量(AI 调用产生的 MeterEvent)是 ops 总量的主导因素(~85%)
48
+ 2. PR #1341 只消除了 shim 层 5× 放大(~15% 贡献)
49
+ 3. 消除 5× 放大后,底量 ~10-13k/day 仍高于 10k 免费线
50
+
51
+ ### 2.4 `exceededResources` 比率恶化,未按预期降到零
52
+
53
+ | 日期 | 每小时 exceeded | 过载率 |
54
+ |---|---|---|
55
+ | 2026-04-18 (Plan 1 刚上线) | 12 | 13% |
56
+ | 2026-04-20 14:00 起(突变) | 60 | 上升 |
57
+ | **2026-04-23 稳态** | **60** | **42.2%**(success 1,991 / exceeded 1,451) |
58
+ | 2026-04-24 部署前 | 60 | ~37% |
59
+
60
+ `payment-kit-platform-analysis-2026-04-20.md` §1.1 预期 "Phase 1 后 <5%" 未达成。现实是 `runAllScheduledJobs` 每分钟对 16 队列做 32 次串行 D1 RTT,总计约 2s wall-time,稳定踩 CF scheduled handler 时长上限。Plan 8(见 §3)是针对此问题的修复。
61
+
62
+ ---
63
+
64
+ ## 3. 部署记录(2026-04-24)
65
+
66
+ ### 3.1 Plan 8 + Plan 13 组合部署
67
+
68
+ | 项目 | 值 |
69
+ |---|---|
70
+ | 部署时间 | **2026-04-24 12:33:43 UTC** |
71
+ | Worker Version ID | `4effff8a-1cd0-4006-a2ec-ca88d5be8f46` |
72
+ | Deployment ID | `94059fb5-9b18-49f1-912c-bc7311f4183a` |
73
+ | 分支 | `fix/plan13-reduce-queue-ops` |
74
+ | 包含 commits | `94a7fc33`(Plan 8)+ `d57651e1`(Plan 13)+ `9f1f0ef5`(D1 ctx.waitUntil fix) |
75
+ | URL | https://staging-aigne-hub-payment-kit.arcblock.workers.dev |
76
+
77
+ ### 3.2 Plan 8 — `runAllScheduledJobs` D1 查询合并
78
+
79
+ **文件**:`blocklets/core/cloudflare/shims/queue.ts`
80
+
81
+ 原实现:对 16 个已注册队列串行调用 `getScheduledJobs()` + `getJobs()` = **32 次 D1 round-trip**,每次 50–80ms,总计 1.5–2.1s wall-time。
82
+
83
+ 新实现:单次 `Job.findAll` 跨所有队列(`Op.in` 过滤 queueName,`Op.or` 覆盖"延迟到期 + 立即卡滞"两类),本地按 queueName 分组路由到现有 `dispatchJob`。
84
+
85
+ **预期影响**:scheduled handler wall-time 2s → <150ms,`exceededResources` 应从 60/h → ~0。
86
+
87
+ **测试**:`blocklets/core/cloudflare/tests/shims/queue-scheduled.spec.ts` 新增 9 个单测(无队列早退、单次 findAll、按队列分组、异常隔离、WHERE 子句结构校验等)。
88
+
89
+ ### 3.3 Plan 13 — 队列 ops 源头减量
90
+
91
+ **文件**:`blocklets/core/api/src/queues/payment.ts` + `blocklets/core/api/src/queues/credit-consume.ts`
92
+
93
+ **改动 A: `startDepositVaultQueue` 合并 N 币种为单条消息**
94
+ - 原:N 个启用 vault 的币种 × 288 次 cron/day = **288N writes/day**
95
+ - 新:1 条 `{ currencyIds: [...] }` 消息,consumer 内部循环处理,per-currency try/catch 隔离
96
+ - 保留 `currencyId?` 与 `currencyIds?` 双兼容字段
97
+ - 使用稳定 jobId `deposit-vault-batch`(D1 unique constraint 提供背压)
98
+
99
+ **改动 B: `startCreditConsumeQueue` 恢复路径按 customer 分组**
100
+ - 原:pending MeterEvent 每条 → 1 次 `addCreditConsumptionJob` → 1 条 queue 消息
101
+ - 新:按 `(customerId, eventName, subscriptionId)` 分组,多事件组走 `handleBatchCreditConsumption` 批路径
102
+ - 单例组保留原逐条路径(失败隔离不变)
103
+ - 使用稳定 jobId `batch-credit-recovery-${key}`(与实时 `addToBatch` 共享 batchKey 语义)
104
+
105
+ **预期影响**(保守/乐观):
106
+ - depositVault writes 节省:假设 N=3 → 1,728 ops/day;N=5 → 3,456 ops/day
107
+ - credit 恢复爆发压缩:单次恢复事件节省 270–1,350 ops(取决于 pending 数量)
108
+ - 合计:日度 ops 从 10.5k 降到 **6.5–8.8k(保守)/ 4.5k(乐观)**
109
+
110
+ ---
111
+
112
+ ## 4. 部署后 2.5 小时观察
113
+
114
+ ### 4.1 Queue 无法测量(仍在熔断中)
115
+ - 今日 06:00 UTC 打满 10,448 ops 后,send() 被 CF 拒绝至今
116
+ - 04-25 00:00 UTC 解封后才能看到 Plan 13 效果
117
+ - 预计首个完整评估窗口:**2026-04-25 UTC 全日**
118
+
119
+ ### 4.2 `exceededResources` 尚无明显改善
120
+ | UTC 时段 | exceeded/h | 相对部署 |
121
+ |---|---|---|
122
+ | 11:00 | 59 | 部署前 |
123
+ | 12:00 | 61 | 部署窗口 |
124
+ | 13:00 | 54 | T+0.5h |
125
+ | 14:00 | 59 | T+1.5h |
126
+ | 15:00(部分) | 10 → 投射 60 | T+2.5h |
127
+
128
+ Plan 8 预期把 scheduled handler 超时清零,目前看未兑现。可能原因:
129
+ - Scheduled 路径还有其他 I/O 瓶颈(如 `flushPendingJobs` 或 `ctx.waitUntil` 内的下游调用)
130
+ - 需 wrangler tail 实测日志进一步定位(本地 tail auth 暂时无法用,留待后续)
131
+
132
+ ### 4.3 HTTP 流量自 12:00 UTC 起静默
133
+ success 调用:09:00 **2,219** → 10:00 882 → 11:00 214 → 12:00–15:00 **0**
134
+
135
+ 无法判断是正常业务低潮还是 queue 熔断的二级影响(上游 aigne-hub 因 payment-kit 队列失败而停止调用)。
136
+
137
+ ---
138
+
139
+ ## 5. 旧文档勘误对照表
140
+
141
+ ### 5.1 `cf-queues-ops-alert-analysis.md` 勘误
142
+
143
+ | 位置 | 原文 | 更正 |
144
+ |---|---|---|
145
+ | §1.1 | "触达免费额度(1,000,000 ops / month)的 95%" | 免费是 10k/day 日度硬顶;95% 告警触发的是"日度阈值"而非"月度" |
146
+ | §1.4 预期表 | "PR #1341 完整部署后 2-4k/day" | 实测 11-13k/day;降幅仅 ~15% 而非 80% |
147
+ | §1.4 预期表 | "`exceededResources` 趋近 0" | 04-20 14:00 后升至 60/h 稳态;需 Plan 8 另行修复 |
148
+ | §4 | "占 1M/month 免费额度 10-15%" | 前提错;实际每日超限 |
149
+ | §8.2 | "17 天累计 90k ops → 对 1M/month 免费额度占用 9%" | 前提错;免费是日度 10k |
150
+ | §13.2 | "目标 ≤ 3,000/day" | 从未达成;目标下调为 "≤ 10,000/day" 才符合现实约束 |
151
+ | §17.2 | "升级付费计划... 根因修完后 ops 在免费额度 10× 以内,不必升级" | 现实每日超 10k,需考虑升级或继续减量 |
152
+ | §18.3 O-1 | "CF Queue 日 ops ≤ 5,000/day" | **承诺失效**;当前基线 10-13k |
153
+ | §18.3 O-2 | "月度 ops 占 1M 免费额度 ≤ 20%" | **承诺失效**;前提错 |
154
+ | §18.3 O-3 | "修复后 30 天内告警 = 0" | **承诺失效**;04-23 再次告警 |
155
+ | §21 | Plan 5/6/7/8/9 状态为 pending | Plan 5 已完成;Plan 8 已实施并部署;Plan 13(本文档新增)已部署 |
156
+
157
+ ### 5.2 `payment-kit-platform-analysis-2026-04-20.md` 勘误
158
+
159
+ | 位置 | 原文 | 更正 |
160
+ |---|---|---|
161
+ | 总述 | "2-3 周 1 人可完成,账单仍 $5-10/月" | 基线错 —— 账号**未付费**;"$5/月"是升级后的底价,不是当前实际 |
162
+ | §1.1 | "`exceededResources` 比率 13%" | 当前 36–42%;Phase 1 完成率 1/8 未整改 |
163
+ | §1.4 预期 | "服务端时间 15-18 秒 / `exceededResources` < 5%" | 未兑现(Phase 1 未做完)|
164
+ | §3.1 成本表 | 各列基于 $5 付费套餐假设 | 当前未付费;付费后真实月账单详见 §6 |
165
+ | §4 Phase 1 清单 | 8 项改动 | 实际完成度:1 (item 7) / 8;item 4 索引 2/3;item 5 代码就绪但配置默认关闭 |
166
+ | §4 item 6 | "空数组短路" 标记为"未做" | 复查代码后发现:所有现有 `Op.in + update/destroy` 路径已有 length 守卫;Sequelize 6.37.7 下空数组自带保护。不过防御式编程规则仍建议加 lint |
167
+
168
+ ### 5.3 在两份旧文档顶部各加一行交叉引用(不改原文)
169
+
170
+ 两份文档顶部会加一行 `> [!NOTE] 2026-04-24 更新:本文档部分预期/承诺值已被现实证伪,请先阅读 2026-04-24-queue-ops-followup.md`。
171
+
172
+ ---
173
+
174
+ ## 6. 付费模式下各规模预期月账单(基于真实用量推算)
175
+
176
+ Workers Paid $5/月含 10M 请求 + 30M CPU-ms + 1M Queue ops + 5B D1 rows read + 50M D1 rows written。超额单价:Queue $0.40/M,Workers $0.30/M,D1 reads $0.001/M(即 $1/B),D1 writes $1/M。
177
+
178
+ | 用户规模 | Queue ops/月 | D1 rows read/月 | 月账单 |
179
+ |---|---|---|---|
180
+ | 当前(~100 用户)| 335k | 1.45B | **$5** |
181
+ | 1,000 用户 | 3.3M | 14.5B(超 9.5B × $1/B + Queue 超 2.3M × $0.40)| **~$15** |
182
+ | 10,000 用户 | 33M | 145B | **~$155**(D1 读为主)|
183
+ | 100,000 用户 | 335M | 1.45T | **~$1,500** |
184
+
185
+ **要点**:D1 rows read 是最大成本随用户规模放大器。Dashboard 预聚合(旧文档 Phase 1 item 8,未做)是长期关键优化。Queue 层优化(Plan 8 + Plan 13)对未来付费账单节省有限,但对当前免费套餐保命必要。
186
+
187
+ ---
188
+
189
+ ## 7. 后续观察计划
190
+
191
+ | 时间点 | 动作 | 判据 |
192
+ |---|---|---|
193
+ | **2026-04-25 00:00 UTC** | Queue 配额解封,业务消息恢复写入 | 期望:Plan 13 效果显现,日度 ops <10k |
194
+ | 2026-04-25 12:00 UTC(T+24h)| 查 04-25 UTC 全日 ops | 期望:~6.5-8.8k/day |
195
+ | 2026-04-26 UTC(T+48h)| 稳态判断 | 期望:稳定在 <10k/day,不再收到 CF 告警 |
196
+ | 持续 | 监测 `exceededResources` | 若 48h 后仍 60/h,Plan 8 未解决根因,需独立 PR 深入定位 |
197
+
198
+ 若 T+48h 后仍每天超 10k,需在下列三路径中选一:
199
+ - **A**:升级 CF Workers Paid $5/月 —— 立即解决
200
+ - **B**:继续压 queue writes(Phase 1 item 8 Dashboard 预聚合 + 订阅 cron 精准化),还能再降 3–5k/day
201
+ - **C**:完全去 CF Queue 化,改为纯 D1 + cron 自驱动 —— 1-2 周工程量,性价比差
202
+
203
+ ---
204
+
205
+ ## 8. 相关文档
206
+
207
+ - `cf-queues-ops-alert-analysis.md` — 2026-04-17 首次告警根因分析(原始 RCA,保留供历史溯源;§18 承诺阈值已部分失效,以本文 §5.1 勘误为准)
208
+ - `payment-kit-platform-analysis-2026-04-20.md` — 2026-04-20 平台级评估(Phase 1 建议仍有效,但成本表基于付费套餐假设,以本文 §5.2 勘误为准)
209
+
210
+ ---
211
+
212
+ ## 9. 变更记录
213
+
214
+ | 日期 | 内容 | 状态 |
215
+ |---|---|---|
216
+ | 2026-04-24 | 本文档产出;汇总 Plan 8 + Plan 13 部署记录、事实勘误、旧文档对照 | ✅ |
217
+ | 2026-04-25 00:00+ UTC | 预计 Queue 配额解封,开始效果验证 | ⏳ |
218
+ | 2026-04-26 12:00 UTC | T+48h 稳态评估 | ⏳ |