payment-kit 1.29.1 → 1.29.3

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 (343) hide show
  1. package/api/dev.ts +41 -2
  2. package/api/hono.d.ts +42 -0
  3. package/api/node-sqlite.d.ts +12 -0
  4. package/api/src/bootstrap.ts +47 -0
  5. package/api/src/crons/base.ts +3 -3
  6. package/api/src/crons/currency.ts +1 -1
  7. package/api/src/crons/index.ts +41 -37
  8. package/api/src/crons/metering-subscription-detection.ts +1 -1
  9. package/api/src/crons/overdue-detection.ts +2 -2
  10. package/api/src/crons/retry-pending-events.ts +6 -0
  11. package/api/src/crons/tenant-fanout.ts +82 -0
  12. package/api/src/host-node/did-connect-runtime-node.ts +33 -0
  13. package/api/src/host-node/serve-static-arc.ts +68 -0
  14. package/api/src/host-node/serve-static.ts +41 -0
  15. package/api/src/index.ts +22 -161
  16. package/api/src/integrations/app-store/client.ts +3 -4
  17. package/api/src/integrations/app-store/handlers/subscription.ts +7 -7
  18. package/api/src/integrations/app-store/signed-data-verifier.ts +3 -2
  19. package/api/src/integrations/arcblock/token.ts +21 -7
  20. package/api/src/integrations/google-play/handlers/subscription.ts +6 -6
  21. package/api/src/integrations/google-play/handlers/voided.ts +2 -2
  22. package/api/src/integrations/google-play/verify.ts +3 -2
  23. package/api/src/integrations/iap-reconcile.ts +3 -5
  24. package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
  25. package/api/src/integrations/stripe/handlers/subscription.ts +3 -3
  26. package/api/src/libs/archive/query.ts +19 -0
  27. package/api/src/libs/audit.ts +61 -4
  28. package/api/src/libs/auth.ts +247 -47
  29. package/api/src/libs/context.ts +89 -1
  30. package/api/src/libs/currency.ts +2 -2
  31. package/api/src/libs/dayjs.ts +8 -2
  32. package/api/src/libs/did-connect/runtime-did-connect-js.ts +88 -0
  33. package/api/src/libs/did-connect/tenant-identity.ts +221 -0
  34. package/api/src/libs/drivers/auth-storage.ts +118 -0
  35. package/api/src/libs/drivers/cron.ts +264 -0
  36. package/api/src/libs/drivers/db.ts +170 -0
  37. package/api/src/libs/drivers/identity.ts +142 -0
  38. package/api/src/libs/drivers/index.ts +40 -0
  39. package/api/src/libs/drivers/locks.ts +226 -0
  40. package/api/src/libs/drivers/migrate-runner.ts +70 -0
  41. package/api/src/libs/drivers/queue.ts +104 -0
  42. package/api/src/libs/drivers/secrets.ts +194 -0
  43. package/api/src/libs/env.ts +170 -54
  44. package/api/src/libs/exchange-rate/service.ts +7 -6
  45. package/api/src/libs/http-fetch-adapter.ts +60 -0
  46. package/api/src/libs/invoice.ts +1 -1
  47. package/api/src/libs/lock.ts +51 -47
  48. package/api/src/libs/logger.ts +48 -8
  49. package/api/src/libs/notification/index.ts +1 -1
  50. package/api/src/libs/notification/template/customer-credit-low-balance.ts +2 -1
  51. package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -1
  52. package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -1
  53. package/api/src/libs/overdraft-protection.ts +1 -1
  54. package/api/src/libs/payout.ts +1 -1
  55. package/api/src/libs/queue/index.ts +271 -52
  56. package/api/src/libs/queue/runtime.ts +175 -0
  57. package/api/src/libs/resource.ts +3 -3
  58. package/api/src/libs/secrets.ts +38 -0
  59. package/api/src/libs/session.ts +3 -2
  60. package/api/src/libs/subscription.ts +5 -5
  61. package/api/src/libs/tenant.ts +92 -0
  62. package/api/src/libs/url.ts +3 -3
  63. package/api/src/libs/util.ts +21 -13
  64. package/api/src/middlewares/hono/cdn.ts +63 -0
  65. package/api/src/middlewares/hono/context.ts +80 -0
  66. package/api/src/middlewares/hono/csrf.ts +83 -0
  67. package/api/src/middlewares/hono/fallback.ts +194 -0
  68. package/api/src/middlewares/hono/pipeline.ts +73 -0
  69. package/api/src/middlewares/hono/resource-mount.ts +42 -0
  70. package/api/src/middlewares/hono/resource.ts +63 -0
  71. package/api/src/middlewares/hono/security.ts +209 -0
  72. package/api/src/middlewares/hono/session.ts +114 -0
  73. package/api/src/middlewares/hono/xss.ts +61 -0
  74. package/api/src/queues/auto-recharge.ts +12 -10
  75. package/api/src/queues/checkout-session.ts +38 -21
  76. package/api/src/queues/credit-consume.ts +40 -36
  77. package/api/src/queues/credit-grant.ts +25 -18
  78. package/api/src/queues/credit-reconciliation.ts +7 -5
  79. package/api/src/queues/discount-status.ts +9 -6
  80. package/api/src/queues/event.ts +41 -11
  81. package/api/src/queues/exchange-rate-health.ts +49 -30
  82. package/api/src/queues/invoice.ts +18 -15
  83. package/api/src/queues/notification.ts +14 -7
  84. package/api/src/queues/payment.ts +64 -37
  85. package/api/src/queues/payout.ts +37 -21
  86. package/api/src/queues/refund.ts +36 -18
  87. package/api/src/queues/subscription.ts +83 -53
  88. package/api/src/queues/token-transfer.ts +15 -10
  89. package/api/src/queues/usage-record.ts +8 -5
  90. package/api/src/queues/vendors/commission.ts +7 -5
  91. package/api/src/queues/vendors/fulfillment-coordinator.ts +17 -13
  92. package/api/src/queues/vendors/fulfillment.ts +4 -2
  93. package/api/src/queues/vendors/return-processor.ts +5 -3
  94. package/api/src/queues/vendors/return-scanner.ts +5 -4
  95. package/api/src/queues/vendors/status-check.ts +10 -7
  96. package/api/src/queues/webhook.ts +60 -32
  97. package/api/src/routes/connect/shared.ts +1 -2
  98. package/api/src/routes/connect/subscribe.ts +3 -3
  99. package/api/src/routes/{archive.ts → hono/archive.ts} +69 -64
  100. package/api/src/routes/{auto-recharge-configs.ts → hono/auto-recharge-configs.ts} +39 -28
  101. package/api/src/routes/{checkout-sessions.ts → hono/checkout-sessions.ts} +790 -923
  102. package/api/src/routes/{coupons.ts → hono/coupons.ts} +93 -76
  103. package/api/src/routes/{credit-grants.ts → hono/credit-grants.ts} +140 -126
  104. package/api/src/routes/hono/credit-tokens.ts +43 -0
  105. package/api/src/routes/{credit-transactions.ts → hono/credit-transactions.ts} +37 -29
  106. package/api/src/routes/{customers.ts → hono/customers.ts} +199 -224
  107. package/api/src/routes/{donations.ts → hono/donations.ts} +41 -32
  108. package/api/src/routes/{entitlements.ts → hono/entitlements.ts} +28 -25
  109. package/api/src/routes/{events.ts → hono/events.ts} +107 -71
  110. package/api/src/routes/{exchange-rate-providers.ts → hono/exchange-rate-providers.ts} +138 -126
  111. package/api/src/routes/hono/exchange-rates.ts +77 -0
  112. package/api/src/routes/hono/index.ts +115 -0
  113. package/api/src/routes/{integrations → hono/integrations}/app-store.ts +68 -48
  114. package/api/src/routes/{integrations → hono/integrations}/google-play.ts +78 -58
  115. package/api/src/routes/hono/integrations/stripe.ts +74 -0
  116. package/api/src/routes/{invoices.ts → hono/invoices.ts} +253 -244
  117. package/api/src/routes/{meter-events.ts → hono/meter-events.ts} +120 -110
  118. package/api/src/routes/hono/meters.ts +288 -0
  119. package/api/src/routes/hono/passports.ts +73 -0
  120. package/api/src/routes/{payment-currencies.ts → hono/payment-currencies.ts} +219 -197
  121. package/api/src/routes/{payment-intents.ts → hono/payment-intents.ts} +136 -132
  122. package/api/src/routes/{payment-links.ts → hono/payment-links.ts} +145 -128
  123. package/api/src/routes/{payment-methods.ts → hono/payment-methods.ts} +125 -93
  124. package/api/src/routes/{payment-stats.ts → hono/payment-stats.ts} +30 -25
  125. package/api/src/routes/{payouts.ts → hono/payouts.ts} +55 -47
  126. package/api/src/routes/{prices.ts → hono/prices.ts} +265 -242
  127. package/api/src/routes/{pricing-table.ts → hono/pricing-table.ts} +94 -87
  128. package/api/src/routes/{products.ts → hono/products.ts} +172 -159
  129. package/api/src/routes/{promotion-codes.ts → hono/promotion-codes.ts} +207 -185
  130. package/api/src/routes/hono/redirect.ts +24 -0
  131. package/api/src/routes/{refunds.ts → hono/refunds.ts} +98 -83
  132. package/api/src/routes/{settings.ts → hono/settings.ts} +64 -55
  133. package/api/src/routes/{subscription-items.ts → hono/subscription-items.ts} +64 -57
  134. package/api/src/routes/{subscriptions.ts → hono/subscriptions.ts} +475 -528
  135. package/api/src/routes/{tax-rates.ts → hono/tax-rates.ts} +71 -70
  136. package/api/src/routes/hono/tool.ts +69 -0
  137. package/api/src/routes/{usage-records.ts → hono/usage-records.ts} +47 -42
  138. package/api/src/routes/{vendor.ts → hono/vendor.ts} +315 -167
  139. package/api/src/routes/{webhook-attempts.ts → hono/webhook-attempts.ts} +17 -13
  140. package/api/src/routes/hono/webhook-endpoints.ts +126 -0
  141. package/api/src/service.ts +814 -0
  142. package/api/src/store/migrations/20230911-seeding.ts +2 -1
  143. package/api/src/store/migrations/20260609-remove-did-space-jobs.ts +23 -0
  144. package/api/src/store/migrations/20260610-tenant-columns.ts +40 -0
  145. package/api/src/store/migrations/20260611-tenant-backfill.ts +33 -0
  146. package/api/src/store/models/auto-recharge-config.ts +22 -10
  147. package/api/src/store/models/checkout-session.ts +15 -14
  148. package/api/src/store/models/coupon.ts +29 -20
  149. package/api/src/store/models/credit-grant.ts +38 -29
  150. package/api/src/store/models/credit-transaction.ts +32 -21
  151. package/api/src/store/models/customer.ts +19 -17
  152. package/api/src/store/models/discount.ts +11 -2
  153. package/api/src/store/models/entitlement-grant.ts +21 -9
  154. package/api/src/store/models/entitlement-product.ts +21 -9
  155. package/api/src/store/models/entitlement.ts +19 -10
  156. package/api/src/store/models/event.ts +18 -9
  157. package/api/src/store/models/exchange-rate-provider.ts +17 -4
  158. package/api/src/store/models/invoice-item.ts +18 -9
  159. package/api/src/store/models/invoice.ts +16 -8
  160. package/api/src/store/models/meter-event.ts +27 -9
  161. package/api/src/store/models/meter.ts +31 -22
  162. package/api/src/store/models/payment-currency.ts +25 -8
  163. package/api/src/store/models/payment-intent.ts +15 -6
  164. package/api/src/store/models/payment-link.ts +15 -6
  165. package/api/src/store/models/payment-method.ts +38 -22
  166. package/api/src/store/models/payment-stat.ts +18 -9
  167. package/api/src/store/models/payout.ts +15 -6
  168. package/api/src/store/models/price-quote.ts +17 -8
  169. package/api/src/store/models/price.ts +24 -12
  170. package/api/src/store/models/pricing-table.ts +29 -20
  171. package/api/src/store/models/product-vendor.ts +20 -10
  172. package/api/src/store/models/product.ts +15 -6
  173. package/api/src/store/models/promotion-code.ts +14 -6
  174. package/api/src/store/models/refund.ts +15 -6
  175. package/api/src/store/models/revenue-snapshot.ts +21 -9
  176. package/api/src/store/models/setting.ts +18 -9
  177. package/api/src/store/models/setup-intent.ts +36 -27
  178. package/api/src/store/models/subscription-item.ts +21 -9
  179. package/api/src/store/models/subscription-schedule.ts +21 -9
  180. package/api/src/store/models/subscription.ts +21 -10
  181. package/api/src/store/models/tax-rate.ts +29 -21
  182. package/api/src/store/models/usage-record.ts +11 -2
  183. package/api/src/store/models/webhook-attempt.ts +18 -9
  184. package/api/src/store/models/webhook-endpoint.ts +18 -9
  185. package/api/src/store/scoped-core.ts +55 -0
  186. package/api/src/store/scoped.ts +247 -0
  187. package/api/src/store/sequelize.ts +82 -23
  188. package/api/src/store/sql-migrations.ts +20 -0
  189. package/api/src/store/tenant-backfill.ts +260 -0
  190. package/api/src/store/tenant-model.ts +124 -0
  191. package/api/src/store/tenant-tables.ts +50 -0
  192. package/api/tests/bootstrap/bootstrap.spec.ts +162 -0
  193. package/api/tests/crons/tenant-fanout.spec.ts +158 -0
  194. package/api/tests/embedded/embedded-multi-mode-d3.spec.ts +257 -0
  195. package/api/tests/fixtures/bare-query-violation.ts +13 -0
  196. package/api/tests/fixtures/core-env-violation.ts +10 -0
  197. package/api/tests/fixtures/host-read-violation.ts +19 -0
  198. package/api/tests/fixtures/tenants.ts +4 -0
  199. package/api/tests/integrations/iap-tenant.spec.ts +284 -0
  200. package/api/tests/libs/archive-query.spec.ts +26 -0
  201. package/api/tests/libs/audit-tenant.spec.ts +153 -0
  202. package/api/tests/libs/context.spec.ts +204 -0
  203. package/api/tests/libs/core-config.spec.ts +115 -0
  204. package/api/tests/libs/cron-driver-d2.spec.ts +237 -0
  205. package/api/tests/libs/crons-conservation-d2.spec.ts +52 -0
  206. package/api/tests/libs/did-connect-runtime-js.spec.ts +98 -0
  207. package/api/tests/libs/did-connect-tenant-identity.spec.ts +159 -0
  208. package/api/tests/libs/lock-tenant.spec.ts +66 -0
  209. package/api/tests/libs/scoped.spec.ts +222 -0
  210. package/api/tests/libs/secrets-facade.spec.ts +52 -0
  211. package/api/tests/libs/service-host.spec.ts +37 -0
  212. package/api/tests/libs/tenancy-slot-authority.spec.ts +209 -0
  213. package/api/tests/libs/tenant-middleware.spec.ts +42 -0
  214. package/api/tests/libs/tenant-scanner.spec.ts +120 -0
  215. package/api/tests/middlewares/hono/cdn.spec.ts +70 -0
  216. package/api/tests/middlewares/hono/context.spec.ts +113 -0
  217. package/api/tests/middlewares/hono/csrf.spec.ts +136 -0
  218. package/api/tests/middlewares/hono/fallback.spec.ts +67 -0
  219. package/api/tests/middlewares/hono/pipeline.spec.ts +47 -0
  220. package/api/tests/middlewares/hono/security.spec.ts +181 -0
  221. package/api/tests/middlewares/hono/session.spec.ts +42 -0
  222. package/api/tests/middlewares/hono/xss.spec.ts +81 -0
  223. package/api/tests/models/tenant-backfill.spec.ts +287 -0
  224. package/api/tests/models/tenant-columns-model.spec.ts +46 -0
  225. package/api/tests/models/tenant-columns.spec.ts +161 -0
  226. package/api/tests/queues/credit-consume-batch.spec.ts +8 -1
  227. package/api/tests/queues/credit-consume.spec.ts +8 -1
  228. package/api/tests/queues/event-tenant.spec.ts +292 -0
  229. package/api/tests/queues/exchange-rate-health-tenant-d6.spec.ts +62 -0
  230. package/api/tests/queues/queue-parity.spec.ts +249 -0
  231. package/api/tests/queues/queue-runtime-surface.spec.ts +277 -0
  232. package/api/tests/queues/queue-teardown-d2.spec.ts +127 -0
  233. package/api/tests/queues/tenant-matrix-a.spec.ts +245 -0
  234. package/api/tests/queues/tenant-matrix-b.spec.ts +168 -0
  235. package/api/tests/routes/connect/hono-attach.spec.ts +107 -0
  236. package/api/tests/service/collapse.spec.ts +96 -0
  237. package/api/tests/service/didconnect-storage-slot.spec.ts +60 -0
  238. package/api/tests/service/fail-closed-http.spec.ts +79 -0
  239. package/api/tests/service/static-arc-handler.spec.ts +101 -0
  240. package/api/tests/service/static-externalized.spec.ts +48 -0
  241. package/api/tests/store/tenant-crosscut.spec.ts +202 -0
  242. package/api/tests/store/tenant-model-spike.spec.ts +177 -0
  243. package/api/tests/store/tenant-model.spec.ts +162 -0
  244. package/api/tests/store/tenant-residual.spec.ts +196 -0
  245. package/api/third.d.ts +4 -0
  246. package/blocklet.yml +1 -1
  247. package/cloudflare/MIGRATION-RUNBOOK.md +3 -8
  248. package/cloudflare/README.md +34 -27
  249. package/cloudflare/STAGING-MIGRATION-GUIDE.md +3 -15
  250. package/cloudflare/build.ts +33 -13
  251. package/cloudflare/cf-adapter.ts +419 -0
  252. package/cloudflare/did-connect-runtime.ts +96 -0
  253. package/cloudflare/did-connect-token-storage.ts +151 -0
  254. package/cloudflare/esbuild-cf-config.cjs +407 -0
  255. package/cloudflare/migrations/0006_tenant_columns.sql +46 -0
  256. package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
  257. package/cloudflare/migrations/0008_schema_parity.sql +16 -0
  258. package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
  259. package/cloudflare/queue-runtime-mode.ts +13 -0
  260. package/cloudflare/run-build.js +33 -403
  261. package/cloudflare/scripts/cf-package-import-probe.mjs +90 -0
  262. package/cloudflare/scripts/didconnect-mock-smoke.mjs +140 -0
  263. package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
  264. package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
  265. package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
  266. package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
  267. package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
  268. package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
  269. package/cloudflare/shims/blocklet-sdk/wallet-authenticator.ts +16 -1
  270. package/cloudflare/shims/blocklet-sdk/wallet-handler.ts +18 -3
  271. package/cloudflare/shims/cron.ts +38 -158
  272. package/cloudflare/shims/events.ts +124 -0
  273. package/cloudflare/shims/fastq.ts +15 -1
  274. package/cloudflare/shims/nedb-storage.ts +16 -8
  275. package/cloudflare/shims/xss.ts +8 -0
  276. package/cloudflare/tenant-middleware.ts +36 -0
  277. package/cloudflare/tests/cf-adapter.spec.ts +244 -0
  278. package/cloudflare/tests/did-connect-token-storage.spec.ts +105 -0
  279. package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
  280. package/cloudflare/tests/worker-handler-gate.spec.ts +69 -0
  281. package/cloudflare/vite.config.ts +53 -45
  282. package/cloudflare/worker.ts +261 -448
  283. package/cloudflare/wrangler.json +0 -6
  284. package/cloudflare/wrangler.jsonc +0 -6
  285. package/cloudflare/wrangler.local-e2e.jsonc +25 -0
  286. package/cloudflare/wrangler.staging.json +0 -6
  287. package/jest.config.js +3 -1
  288. package/package.json +33 -38
  289. package/scripts/bootstrap-inject.ts +166 -0
  290. package/scripts/core-env-whitelist.json +1 -0
  291. package/scripts/e2e-12b-runtime.ts +149 -0
  292. package/scripts/e2e-core-config.ts +125 -0
  293. package/scripts/e2e-d1-tenancy.ts +116 -0
  294. package/scripts/e2e-d2-cron-queue.ts +139 -0
  295. package/scripts/e2e-d3-embedded-multi.ts +171 -0
  296. package/scripts/e2e-hono-s2.ts +125 -0
  297. package/scripts/e2e-hono-s3e.ts +135 -0
  298. package/scripts/e2e-hono-s4.ts +114 -0
  299. package/scripts/e2e-migration-contract.ts +100 -0
  300. package/scripts/e2e-s0.ts +61 -0
  301. package/scripts/e2e-s1.ts +107 -0
  302. package/scripts/e2e-s2.ts +178 -0
  303. package/scripts/e2e-s3.ts +110 -0
  304. package/scripts/e2e-s4.ts +191 -0
  305. package/scripts/e2e-s5.ts +139 -0
  306. package/scripts/e2e-s6.ts +127 -0
  307. package/scripts/e2e-tenant-model.ts +119 -0
  308. package/scripts/e2e-tenant-worker.ts +199 -0
  309. package/scripts/gen-sql-migrations.js +46 -0
  310. package/scripts/phase8-codemod.js +219 -0
  311. package/scripts/phase9a-env-getters-codemod.js +82 -0
  312. package/scripts/scan-core-env.js +109 -0
  313. package/scripts/scan-tenant-queries.js +235 -0
  314. package/scripts/schema-drift-guard.ts +210 -0
  315. package/scripts/tenant-scan-whitelist.json +1 -0
  316. package/src/app.tsx +2 -1
  317. package/src/env.d.ts +13 -1
  318. package/src/libs/service-host.ts +13 -0
  319. package/tsconfig.json +1 -1
  320. package/vite.arc.config.ts +159 -0
  321. package/api/src/libs/did-space.ts +0 -235
  322. package/api/src/libs/middleware.ts +0 -50
  323. package/api/src/libs/security.ts +0 -192
  324. package/api/src/queues/space.ts +0 -662
  325. package/api/src/routes/credit-tokens.ts +0 -38
  326. package/api/src/routes/exchange-rates.ts +0 -87
  327. package/api/src/routes/index.ts +0 -142
  328. package/api/src/routes/integrations/stripe.ts +0 -61
  329. package/api/src/routes/meters.ts +0 -274
  330. package/api/src/routes/passports.ts +0 -68
  331. package/api/src/routes/redirect.ts +0 -20
  332. package/api/src/routes/tool.ts +0 -65
  333. package/api/src/routes/webhook-endpoints.ts +0 -126
  334. package/api/tests/routes/credit-grants.spec.ts +0 -1261
  335. package/cloudflare/did-connect-auth.ts +0 -527
  336. package/cloudflare/shims/did-space-js.ts +0 -17
  337. package/cloudflare/shims/did-space.ts +0 -11
  338. package/cloudflare/shims/express-compat/index.ts +0 -80
  339. package/cloudflare/shims/express-compat/types.ts +0 -41
  340. package/cloudflare/shims/lock.ts +0 -115
  341. package/cloudflare/shims/queue.ts +0 -611
  342. package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
  343. package/cloudflare/tests/shims/queue-scheduled.spec.ts +0 -186
@@ -0,0 +1,88 @@
1
+ // S3-CF (DID convergence) — the REAL @arcblock/did-connect-js runtime, SHARED by
2
+ // the non-blocklet-server hosts (CF + arc-node embedded).
3
+ //
4
+ // Both build the same authenticator/handlers; they differ ONLY in the host adapter
5
+ // passed in here (tokenStorage, chain config, txEncoder, timeout). The signing
6
+ // wallet + appInfo are FUNCTION-VALUED and resolved per-request, per-tenant through
7
+ // the shared `resolveTenantIdentity` (AUTH_SERVICE.getInstanceAppIdentity) — never
8
+ // a fixed isolate key — so one isolate serves every tenant with its own appSk.
9
+ //
10
+ // @arcblock/did-connect-js supports function-valued `wallet` (base.js: typeof
11
+ // wallet === 'function' → resolved via getWalletInfo with a timeout before each
12
+ // sign), so per-tenant signing is safe.
13
+
14
+ import type { DidConnectRuntime, DidConnectTokenStorage } from '../auth';
15
+ import { resolveTenantIdentity } from './tenant-identity';
16
+
17
+ /** Chain config for the DID-Connect authenticator (id/type/host), or a resolver. */
18
+ export type ChainInfoOption =
19
+ | { id: string; type: string; host: string }
20
+ | ((params: any) => { id: string; type: string; host: string } | Promise<{ id: string; type: string; host: string }>);
21
+
22
+ export interface DidConnectJsRuntimeOptions {
23
+ /** Host-injected token store (CF: D1 tenant-aware adapter). Omit to let
24
+ * buildTokenStorage fall back to the file-backed nedb default (arc-node). */
25
+ tokenStorage?: DidConnectTokenStorage;
26
+ /** Chain config for `signature`/`prepareTx` claims (omit on chain-less hosts). */
27
+ chainInfo?: ChainInfoOption;
28
+ /** On-chain tx encoder (CF: @ocap/client/encode CBOR encoder). Omit to disable tx claims. */
29
+ txEncoder?: (params: { type: string; data: any; wallet: any; chainHost: string }) => Promise<Buffer>;
30
+ /** Authenticator timeout (CF chain RPC can exceed the 8s default; worker used 30s). */
31
+ timeout?: number;
32
+ /** Branding fallback when an instance's getInstanceAppIdentity omits appInfo. */
33
+ defaultAppInfo?: { name?: string; description?: string; icon?: string };
34
+ }
35
+
36
+ const DEFAULT_APP_INFO = {
37
+ name: 'Payment Kit',
38
+ description: 'Payment Kit',
39
+ icon: 'https://www.arcblock.io/favicon.ico',
40
+ };
41
+
42
+ /**
43
+ * Build a DID-Connect runtime backed by the real @arcblock/did-connect-js stack.
44
+ * Used by `createCloudflareDidConnectRuntime` (CF) and the arc-node embedded host.
45
+ */
46
+ export function createDidConnectJsRuntime(opts: DidConnectJsRuntimeOptions): DidConnectRuntime {
47
+ const fallbackAppInfo = { ...DEFAULT_APP_INFO, ...(opts.defaultAppInfo ?? {}) };
48
+
49
+ return {
50
+ tokenStorage: opts.tokenStorage,
51
+
52
+ createAuthenticator() {
53
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
54
+ const { WalletAuthenticator } = require('@arcblock/did-connect-js');
55
+
56
+ const config: Record<string, any> = {
57
+ // Function-valued, per-tenant: resolve the signing wallet from the host
58
+ // IdentityDriver.getInstanceAppIdentity each time the authenticator signs.
59
+ // `.toJSON()` matches the standalone worker (the SDK reconstructs the full
60
+ // wallet — incl. sk — via fromJSON before signing).
61
+ wallet: async () => {
62
+ const { wallet } = await resolveTenantIdentity();
63
+ return wallet.toJSON();
64
+ },
65
+ appInfo: async ({ baseUrl }: { baseUrl?: string } = {}) => {
66
+ const { appInfo } = await resolveTenantIdentity();
67
+ return {
68
+ name: appInfo.name || fallbackAppInfo.name,
69
+ description: appInfo.description || fallbackAppInfo.description,
70
+ icon: appInfo.icon || fallbackAppInfo.icon,
71
+ link: appInfo.link || baseUrl,
72
+ };
73
+ },
74
+ timeout: opts.timeout ?? 30000,
75
+ };
76
+ if (opts.chainInfo) config.chainInfo = opts.chainInfo;
77
+ if (opts.txEncoder) config.txEncoder = opts.txEncoder;
78
+
79
+ return new WalletAuthenticator(config);
80
+ },
81
+
82
+ createHandlers({ authenticator, tokenStorage }) {
83
+ // eslint-disable-next-line global-require, import/no-extraneous-dependencies
84
+ const { WalletHandlers } = require('@arcblock/did-connect-js');
85
+ return new WalletHandlers({ authenticator, tokenStorage });
86
+ },
87
+ };
88
+ }
@@ -0,0 +1,221 @@
1
+ // S3-CF (DID convergence) — the SHARED per-tenant DID-Connect identity resolver.
2
+ //
3
+ // Both non-blocklet-server runtimes (CF + arc-node embedded) build their REAL
4
+ // `@arcblock/did-connect-js` WalletAuthenticator from a per-tenant signing wallet
5
+ // resolved HERE — never from a fixed isolate-level APP_SK. The signing identity
6
+ // comes from the host IdentityDriver's `getInstanceAppIdentity(instanceDid)`
7
+ // (AUTH_SERVICE-backed), which already resolves the arc run-mode (instance app:sk
8
+ // → instance identity; else auth-service root; else fail-closed), so payment-core
9
+ // never reimplements "instance ?? root" and never reads connect-service internals.
10
+ //
11
+ // The wallet is derived from the returned `appSk` with @ocap/wallet (a stable
12
+ // public API) — the same ROLE_APPLICATION/ED25519/SHA3 wallet type the standalone
13
+ // CF worker used. The blocklet-server runtime does NOT use this resolver (it keeps
14
+ // the @blocklet/sdk wallet wrapper); only the AUTH_SERVICE/did-connect-js runtimes do.
15
+ //
16
+ // eslint-disable-next-line import/no-extraneous-dependencies
17
+ import * as Mcrypto from '@ocap/mcrypto';
18
+ // eslint-disable-next-line import/no-extraneous-dependencies
19
+ import { fromSecretKey, WalletType } from '@ocap/wallet';
20
+ import type { WalletObject } from '@ocap/wallet';
21
+
22
+ import { getInstanceDid } from '../context';
23
+ import { getIdentityDriver, type InstanceAppInfo, type BlockletDirectory } from '../drivers';
24
+ import logger from '../logger';
25
+
26
+ const walletType = {
27
+ role: Mcrypto.types.RoleType.ROLE_APPLICATION,
28
+ pk: Mcrypto.types.KeyType.ED25519,
29
+ hash: Mcrypto.types.HashType.SHA3,
30
+ };
31
+
32
+ // The ethereum business wallet is derived from the SAME instance appSk as the
33
+ // arcblock wallet, mirroring @blocklet/sdk getWallet('ethereum', appSk): the
34
+ // secp256k1/keccak WalletType('ethereum') over the first 66 chars of the appSk.
35
+ // Using @ocap/wallet directly (not the SDK) keeps this CF-worker-safe — the CF
36
+ // build shims the SDK wallet-* modules, but @ocap/wallet is a stable public API.
37
+ const ethWalletType = WalletType('ethereum');
38
+
39
+ export interface ResolvedTenantIdentity {
40
+ instanceDid: string;
41
+ /** App signing wallet derived from the instance appSk (the DID-Connect signer). */
42
+ wallet: WalletObject;
43
+ /** Ethereum business wallet derived from the same appSk (EVM chains / refunds). */
44
+ ethWallet: WalletObject;
45
+ /** Permanent app signing wallet (when keys rotated); falls back to `wallet`. */
46
+ permanentWallet: WalletObject;
47
+ /** Branding for DID-Connect prompts (name/description/icon/link). */
48
+ appInfo: InstanceAppInfo;
49
+ }
50
+
51
+ // Per-instance TTL cache of the DERIVED identity (Layer 2 of
52
+ // wallet-authenticator-dynamic.md). The business wallet proxies (libs/auth.ts)
53
+ // read it SYNCHRONOUSLY via getCachedTenantIdentity, so resolveTenantIdentity
54
+ // (async — it RPCs getInstanceAppIdentity) must run first per request/job (the
55
+ // HTTP contextMiddleware and queue runJobWithTenant warm it). TTL bounds key-
56
+ // rotation staleness; for a single-tenant deployment this holds exactly one entry.
57
+ // A hard size cap (insertion-order eviction via Map iteration) keeps a host with
58
+ // many tenants from growing the map unbounded — expired entries are pruned on
59
+ // read but never proactively, so the cap is the only growth bound.
60
+ const IDENTITY_TTL_MS = 5 * 60 * 1000;
61
+ const IDENTITY_CACHE_MAX = 512;
62
+ const identityCache = new Map<string, { value: ResolvedTenantIdentity; expiry: number }>();
63
+
64
+ function cacheIdentity(instanceDid: string, value: ResolvedTenantIdentity): void {
65
+ identityCache.set(instanceDid, { value, expiry: Date.now() + IDENTITY_TTL_MS });
66
+ // evict the oldest entry (Map preserves insertion order) once over the cap
67
+ if (identityCache.size > IDENTITY_CACHE_MAX) {
68
+ const oldest = identityCache.keys().next().value;
69
+ if (oldest !== undefined) identityCache.delete(oldest);
70
+ }
71
+ }
72
+
73
+ /** Drop a tenant's cached identity (or all) — key rotation / test isolation. */
74
+ export function clearTenantIdentityCache(instanceDid?: string): void {
75
+ if (instanceDid) identityCache.delete(instanceDid);
76
+ else identityCache.clear();
77
+ }
78
+
79
+ /**
80
+ * Whether the active host runtime resolves app identity dynamically per tenant
81
+ * (arc-node embedded + CF, via AUTH_SERVICE.getInstanceAppIdentity) vs the
82
+ * blocklet-server runtime, which keeps the @blocklet/sdk env wallet. The
83
+ * business wallet proxies branch on this so blocklet-server stays byte-for-byte
84
+ * unchanged (it never touches the resolver / cache).
85
+ */
86
+ export function hasDynamicIdentity(): boolean {
87
+ return typeof getIdentityDriver().getInstanceAppIdentity === 'function';
88
+ }
89
+
90
+ /**
91
+ * Resolve the current (or given) tenant's DID-Connect signing identity. Throws a
92
+ * clear error — never silently falls back to a fixed key — when:
93
+ * - the active IdentityDriver does not implement getInstanceAppIdentity (a
94
+ * non-SDK runtime reached an SDK-only driver), or
95
+ * - the instance has no app signing key (fail-closed; AUTH_SERVICE 4.0.3 itself
96
+ * fails closed when neither instance app:sk nor root APP_SK exists).
97
+ */
98
+ export async function resolveTenantIdentity(instanceDidArg?: string): Promise<ResolvedTenantIdentity> {
99
+ const instanceDid = instanceDidArg ?? getInstanceDid();
100
+
101
+ const cached = identityCache.get(instanceDid);
102
+ if (cached && cached.expiry > Date.now()) return cached.value;
103
+
104
+ const driver = getIdentityDriver();
105
+ if (typeof driver.getInstanceAppIdentity !== 'function') {
106
+ throw new Error(
107
+ 'resolveTenantIdentity: the active IdentityDriver does not implement getInstanceAppIdentity — ' +
108
+ 'a non-blocklet-server DID-Connect runtime requires an AUTH_SERVICE-backed identity driver'
109
+ );
110
+ }
111
+ const identity = await driver.getInstanceAppIdentity(instanceDid);
112
+ if (!identity || !identity.appSk) {
113
+ throw new Error(`resolveTenantIdentity: no app signing key for instance "${instanceDid}" (fail-closed)`);
114
+ }
115
+ // A too-short appSk would silently yield a WRONG ethereum address (slice below)
116
+ // and thus a wrong receiving/signing wallet — fail closed instead. A real
117
+ // ED25519 app sk is 128 hex chars; the eth slice needs at least 66.
118
+ if (identity.appSk.length < 66) {
119
+ throw new Error(
120
+ `resolveTenantIdentity: appSk for instance "${instanceDid}" is too short ` +
121
+ `(${identity.appSk.length} chars) to derive a wallet (fail-closed)`
122
+ );
123
+ }
124
+ const wallet = fromSecretKey(identity.appSk, walletType) as WalletObject;
125
+ // Mirror @blocklet/sdk getWallet('ethereum', appSk): secp256k1 over appSk[0..66].
126
+ const ethWallet = fromSecretKey(identity.appSk.slice(0, 66), ethWalletType) as WalletObject;
127
+ const permanentWallet = identity.appPsk ? (fromSecretKey(identity.appPsk, walletType) as WalletObject) : wallet;
128
+ const value: ResolvedTenantIdentity = {
129
+ instanceDid,
130
+ wallet,
131
+ ethWallet,
132
+ permanentWallet,
133
+ appInfo: identity.appInfo ?? {},
134
+ };
135
+ cacheIdentity(instanceDid, value);
136
+ return value;
137
+ }
138
+
139
+ /**
140
+ * SYNCHRONOUS accessor for the current (or given) tenant's resolved identity —
141
+ * the business wallet proxies (libs/auth.ts) call this on every `wallet.address`
142
+ * / `wallet.sign(...)`. It NEVER resolves (no RPC): it reads the cache that
143
+ * `warmTenantIdentity` populated earlier in the request/job. A miss means the
144
+ * identity was never warmed (fail-closed): throw rather than silently fall back
145
+ * to a wrong/default key.
146
+ */
147
+ export function getCachedTenantIdentity(instanceDidArg?: string): ResolvedTenantIdentity {
148
+ const instanceDid = instanceDidArg ?? getInstanceDid();
149
+ const cached = identityCache.get(instanceDid);
150
+ if (!cached || cached.expiry <= Date.now()) {
151
+ throw new Error(
152
+ `tenant identity for "${instanceDid}" is not resolved (fail-closed) — ` +
153
+ 'warmTenantIdentity must run in the request/job scope before any wallet access'
154
+ );
155
+ }
156
+ return cached.value;
157
+ }
158
+
159
+ /**
160
+ * Best-effort warm of the current tenant's identity into the cache so later
161
+ * SYNCHRONOUS wallet access resolves. No-op for the blocklet-server runtime
162
+ * (env wallet, no dynamic driver). Errors are swallowed (logged): a request that
163
+ * never touches a wallet must not be blocked by an identity hiccup, while one
164
+ * that DOES touch it still fails-closed at getCachedTenantIdentity.
165
+ */
166
+ export async function warmTenantIdentity(instanceDidArg?: string): Promise<void> {
167
+ if (!hasDynamicIdentity()) return;
168
+ try {
169
+ await resolveTenantIdentity(instanceDidArg);
170
+ } catch (err: unknown) {
171
+ logger.warn('[tenant-identity] warm failed — wallet access will fail-closed', {
172
+ error: err instanceof Error ? err.message : String(err),
173
+ });
174
+ }
175
+ }
176
+
177
+ // The host user directory for the embedded runtime. In the DID-Connect world the
178
+ // user's DID IS the wallet DID, so getUser echoes it as a connected wallet account
179
+ // (so getWalletDid(user) resolves to the user's own DID); the rest are no-ops. This
180
+ // is the shared semantics the CF build-alias shim also implements
181
+ // (cloudflare/shims/blocklet-sdk/auth-service.ts).
182
+ const EMBEDDED_DIRECTORY: BlockletDirectory = {
183
+ getUser(did: string) {
184
+ if (!did) return { user: null };
185
+ return {
186
+ user: { did, fullName: did, email: '', phone: '', remark: '', connectedAccounts: [{ provider: 'wallet', did }] },
187
+ };
188
+ },
189
+ getUsers() {
190
+ return { users: [] };
191
+ },
192
+ getVault() {
193
+ return null;
194
+ },
195
+ getBlocklet() {
196
+ return { id: '', site: { id: '' } };
197
+ },
198
+ };
199
+
200
+ /**
201
+ * The embedded host identity services — the AUTH_SERVICE-backed implementations of
202
+ * the optional IdentityDriver methods, derived entirely from getInstanceAppIdentity
203
+ * (so a host need not reimplement them). arc-node spreads BOTH into its identity
204
+ * driver; CF spreads `getBusinessWallet` only (it keeps its build-alias directory
205
+ * shim). Stateless — every call reads the active driver via resolveTenantIdentity /
206
+ * getCachedTenantIdentity, so one instance serves every tenant.
207
+ */
208
+ export function createEmbeddedIdentityServices(): {
209
+ getBusinessWallet(chain: 'arcblock' | 'ethereum'): WalletObject;
210
+ directory(): BlockletDirectory;
211
+ } {
212
+ return {
213
+ getBusinessWallet(chain: 'arcblock' | 'ethereum'): WalletObject {
214
+ const identity = getCachedTenantIdentity();
215
+ return chain === 'ethereum' ? identity.ethWallet : identity.wallet;
216
+ },
217
+ directory(): BlockletDirectory {
218
+ return EMBEDDED_DIRECTORY;
219
+ },
220
+ };
221
+ }
@@ -0,0 +1,118 @@
1
+ // Phase 8 (W2-1a): DID Connect token storage on the db driver.
2
+ //
3
+ // In Blocklet Server the real @arcblock/did-connect-storage-nedb (file-backed)
4
+ // is used unchanged. In the CF worker that package was aliased to a NO-OP stub
5
+ // (cloudflare/shims/nedb-storage.ts) — DID Connect state was silently dropped.
6
+ // This is the from-scratch persistent implementation on top of the db driver
7
+ // contract, so the same code runs on the node (sqlite) and d1 backends and is
8
+ // covered by the driver consistency suite.
9
+ //
10
+ // Records are flexible JSON documents keyed by `token` (the DID-Auth session
11
+ // token, globally unique — no instance_did column needed; isolation is by the
12
+ // unguessable token, matching the original file store's scope).
13
+
14
+ import type { DbDriver } from './db';
15
+
16
+ const { EventEmitter } = require('events');
17
+
18
+ const TABLE = 'did_auth_records';
19
+
20
+ export interface AuthRecord {
21
+ token: string;
22
+ [key: string]: any;
23
+ }
24
+
25
+ export class DbAuthStorage extends EventEmitter {
26
+ private driver: DbDriver;
27
+ private ready: Promise<void> | null = null;
28
+
29
+ constructor(driver: DbDriver) {
30
+ super();
31
+ if (!driver) throw new Error('DbAuthStorage requires a db driver');
32
+ this.driver = driver;
33
+ }
34
+
35
+ // idempotent lazy schema — the original file store self-managed its db file
36
+ // (it was never part of the Umzug/D1 migration chain), so we keep that here.
37
+ private ensureTable(): Promise<void> {
38
+ if (!this.ready) {
39
+ this.ready = this.driver
40
+ .exec(
41
+ `CREATE TABLE IF NOT EXISTS ${TABLE} (` +
42
+ 'token TEXT PRIMARY KEY, ' +
43
+ 'doc TEXT NOT NULL, ' +
44
+ 'created_at INTEGER NOT NULL, ' +
45
+ 'updated_at INTEGER NOT NULL)'
46
+ )
47
+ .then(() => undefined)
48
+ .catch((err) => {
49
+ // reset so a transient failure can be retried on next call
50
+ this.ready = null;
51
+ throw err;
52
+ });
53
+ }
54
+ return this.ready;
55
+ }
56
+
57
+ async read(token: string): Promise<AuthRecord | null> {
58
+ if (!token) throw new Error('token is required to read auth record');
59
+ await this.ensureTable();
60
+ const row = await this.driver.get<{ doc: string }>(`SELECT doc FROM ${TABLE} WHERE token = ?`, [token]);
61
+ if (!row) return null;
62
+ return JSON.parse(row.doc) as AuthRecord;
63
+ }
64
+
65
+ async create(token: string, status = 'created'): Promise<AuthRecord> {
66
+ if (!token) throw new Error('token is required to create auth record');
67
+ await this.ensureTable();
68
+ const now = Date.now();
69
+ const doc: AuthRecord = { token, status, createdAt: now, updatedAt: now };
70
+ await this.driver.exec(`INSERT INTO ${TABLE} (token, doc, created_at, updated_at) VALUES (?, ?, ?, ?)`, [
71
+ token,
72
+ JSON.stringify(doc),
73
+ now,
74
+ now,
75
+ ]);
76
+ this.emit('create', doc);
77
+ return doc;
78
+ }
79
+
80
+ async update(token: string, updates: Record<string, any> = {}): Promise<AuthRecord | null> {
81
+ if (!token) throw new Error('token is required to update auth record');
82
+ await this.ensureTable();
83
+ const current = await this.read(token);
84
+ if (!current) return null;
85
+ const now = Date.now();
86
+ const merged: AuthRecord = { ...current, ...updates, token, updatedAt: now };
87
+ await this.driver.exec(`UPDATE ${TABLE} SET doc = ?, updated_at = ? WHERE token = ?`, [
88
+ JSON.stringify(merged),
89
+ now,
90
+ token,
91
+ ]);
92
+ this.emit('update', merged);
93
+ return merged;
94
+ }
95
+
96
+ async delete(token: string): Promise<number> {
97
+ if (!token) throw new Error('token is required to delete auth record');
98
+ await this.ensureTable();
99
+ const res = await this.driver.exec(`DELETE FROM ${TABLE} WHERE token = ?`, [token]);
100
+ this.emit('destroy', token);
101
+ return res.changes;
102
+ }
103
+
104
+ async exist(token: string, did: any): Promise<boolean> {
105
+ if (!token) throw new Error('token is required to check auth record');
106
+ const doc = await this.read(token);
107
+ return !!doc && doc.did === did;
108
+ }
109
+
110
+ async clear(): Promise<void> {
111
+ await this.ensureTable();
112
+ await this.driver.exec(`DELETE FROM ${TABLE}`);
113
+ }
114
+ }
115
+
116
+ export function createAuthStorage(driver: DbDriver): DbAuthStorage {
117
+ return new DbAuthStorage(driver);
118
+ }