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,114 @@
1
+ // Phase 3 (express→hono) — hono fork of @blocklet/sdk/lib/middlewares/session.js
2
+ // (sessionMiddleware). NOT in the Phase 1 fork set, but used by many resource
3
+ // routes (customers, checkout-sessions, payment-currencies, auto-recharge-configs,
4
+ // …) via `sessionMiddleware({ accessKey: true })`. Unlike authenticate(), this is
5
+ // NOT a gate: it POPULATES c.set('user', …) when a login token OR access key is
6
+ // present and otherwise just calls next() (the downstream handler decides). The
7
+ // token verification (verifyLoginToken / verifyAccessKey / verifyComponentCall /
8
+ // verifySignedToken) + the optional blacklist check are reused verbatim; only the
9
+ // req plumbing becomes a thin hono shim for getTokenFromReq.
10
+ import type { MiddlewareHandler } from 'hono';
11
+ // eslint-disable-next-line import/no-extraneous-dependencies
12
+ import { getTokenFromReq } from '@abtnode/util/lib/get-token-from-req';
13
+ // eslint-disable-next-line import/no-extraneous-dependencies
14
+ import {
15
+ verifyLoginToken,
16
+ verifyAccessKey,
17
+ verifyComponentCall,
18
+ verifySignedToken,
19
+ } from '@blocklet/sdk/lib/util/verify-session';
20
+ // eslint-disable-next-line import/no-extraneous-dependencies
21
+ import { isLoginToken, isAccessKey } from '@blocklet/sdk/lib/util/login';
22
+ // eslint-disable-next-line import/no-extraneous-dependencies
23
+ import config from '@blocklet/sdk/lib/config';
24
+ // eslint-disable-next-line import/no-extraneous-dependencies
25
+ import serviceApi from '@blocklet/sdk/lib/util/service-api';
26
+ import { isTestEnv } from '../../libs/env';
27
+
28
+ interface SessionOptions {
29
+ loginToken?: boolean;
30
+ componentCall?: boolean;
31
+ signedToken?: string;
32
+ strictMode?: boolean;
33
+ accessKey?: boolean;
34
+ signedTokenKey?: string;
35
+ }
36
+
37
+ export function sessionMiddleware(options: SessionOptions = {}): MiddlewareHandler {
38
+ const {
39
+ loginToken = true,
40
+ componentCall = false,
41
+ signedToken = '',
42
+ strictMode = false,
43
+ accessKey = false,
44
+ signedTokenKey = '__jwt',
45
+ } = options;
46
+
47
+ return async (c, next) => {
48
+ let result: any = null;
49
+ // a thin express-req shim covering exactly what getTokenFromReq /
50
+ // verifyComponentCall read (query / cookie+authorization headers / method / url / body).
51
+ const url = new URL(c.req.url);
52
+ const shimReq: any = {
53
+ query: c.req.query(),
54
+ headers: { cookie: c.req.header('cookie'), authorization: c.req.header('authorization') },
55
+ get: (h: string) => c.req.header(h),
56
+ method: c.req.method,
57
+ originalUrl: url.pathname + url.search,
58
+ body: c.get('sanitizedBody') ?? {},
59
+ };
60
+
61
+ try {
62
+ if (loginToken || accessKey) {
63
+ const { _duplicate: hasDuplicate, token: loginTokenValue } = await getTokenFromReq(shimReq, {
64
+ cookie: { key: 'login_token' },
65
+ });
66
+ if (hasDuplicate) {
67
+ return c.text('Access token found in multiple locations', 400);
68
+ }
69
+ if (loginTokenValue && typeof loginTokenValue === 'string') {
70
+ if (!isTestEnv()) {
71
+ const blockletSettings = config.getBlockletSettings();
72
+ if (blockletSettings.enableBlacklist) {
73
+ const { data: checkResult } = await serviceApi.post('/api/user/checkToken', { token: loginTokenValue });
74
+ if (!checkResult.valid) {
75
+ if (strictMode) {
76
+ return c.text('Access token is blocked', 401);
77
+ }
78
+ // not strict → treat as unauthenticated and proceed. NOT awaited
79
+ // on purpose: awaiting would pull downstream route errors into this
80
+ // try/catch and wrongly report them as 401 (parity with the express
81
+ // `next(); return;`, which does not await downstream).
82
+ // eslint-disable-next-line @typescript-eslint/return-await
83
+ return next();
84
+ }
85
+ }
86
+ }
87
+ if (isLoginToken(loginTokenValue)) {
88
+ result = await verifyLoginToken({ token: loginTokenValue, strictMode });
89
+ } else if (isAccessKey(loginTokenValue) && accessKey) {
90
+ result = await verifyAccessKey({ token: loginTokenValue, strictMode });
91
+ }
92
+ }
93
+ }
94
+
95
+ if (!result && componentCall) {
96
+ result = await verifyComponentCall({ req: shimReq, strictMode });
97
+ }
98
+
99
+ if (!result && signedToken) {
100
+ const token = c.req.query(signedTokenKey) || '';
101
+ result = await verifySignedToken({ token, strictMode });
102
+ }
103
+ } catch (err: any) {
104
+ return c.json({ error: err.message }, 401);
105
+ }
106
+
107
+ if (result) {
108
+ c.set('user', result);
109
+ }
110
+ return next();
111
+ };
112
+ }
113
+
114
+ export default sessionMiddleware;
@@ -0,0 +1,61 @@
1
+ // Phase 1 (express→hono) — hono fork of @blocklet/xss (cjs/index.js:26).
2
+ //
3
+ // The express version sanitizes body/params/headers/query IN PLACE. hono's
4
+ // Request is immutable and the query/params/headers are never reflected as HTML
5
+ // (pure JSON API → Joi + parameterized Sequelize), so this fork DELIBERATELY
6
+ // NARROWS to body-only sanitization (design §7, decided 2026-06-12). The
7
+ // query/params/headers narrowing is locked by an explicit spec assertion.
8
+ //
9
+ // This middleware is the SINGLE body read-point: it consumes the request body
10
+ // once, sanitizes it, and stores it on c.set('sanitizedBody'). Downstream routes
11
+ // MUST read c.get('sanitizedBody') and NEVER call c.req.json() again — hono's
12
+ // bodyCache holds the UN-sanitized original, so a re-read is a security hole
13
+ // (design §7). The Stripe webhook (raw-body) is mounted so it never passes
14
+ // through this middleware (Phase 3e).
15
+ import type { MiddlewareHandler } from 'hono';
16
+ // eslint-disable-next-line import/no-extraneous-dependencies
17
+ import { initSanitize } from '@blocklet/xss';
18
+
19
+ // allowedKeys: [] matches the express call site `xss({ allowedKeys: [] })`.
20
+ const sanitize = initSanitize({ allowedKeys: [] });
21
+
22
+ // Raw-body routes whose bytes must reach the handler unconsumed — the Stripe
23
+ // webhook verifies its HMAC signature over the EXACT received bytes. Parity with
24
+ // the express app shell, which routes this path around the json/body middleware
25
+ // (service.ts buildNodeHandler). The route reads c.req.arrayBuffer() itself.
26
+ const RAW_BODY_PREFIXES = ['/api/integrations/stripe/webhook'];
27
+
28
+ export function xss(): MiddlewareHandler {
29
+ return async (c, next) => {
30
+ if (RAW_BODY_PREFIXES.some((p) => c.req.path.startsWith(p))) {
31
+ c.set('sanitizedBody', undefined);
32
+ return next();
33
+ }
34
+ // Parse the body by CONTENT-TYPE, not method — express's body-parser parses a
35
+ // json/form body on ANY method (including a GET with a body, which a few
36
+ // routes use, e.g. customers GET /:id reading body.create). A request with no
37
+ // parseable content-type yields no sanitizedBody (routes read `?? {}`, matching
38
+ // express.json()'s empty {} ).
39
+ const contentType = c.req.header('content-type') || '';
40
+ let body: unknown;
41
+ if (contentType.includes('application/json')) {
42
+ // single read; empty body → {} to match express.json() (which yields {}),
43
+ // malformed JSON throws (→ app.onError, parity with the express 400 path).
44
+ const raw = await c.req.text();
45
+ body = raw === '' ? {} : JSON.parse(raw);
46
+ } else if (
47
+ contentType.includes('application/x-www-form-urlencoded') ||
48
+ contentType.includes('multipart/form-data')
49
+ ) {
50
+ body = await c.req.parseBody();
51
+ } else {
52
+ // no parseable body content-type (or no body) — nothing to sanitize
53
+ body = undefined;
54
+ }
55
+
56
+ c.set('sanitizedBody', body === undefined ? undefined : sanitize(body));
57
+ return next();
58
+ };
59
+ }
60
+
61
+ export default xss;
@@ -1,7 +1,8 @@
1
1
  import { BN } from '@ocap/util';
2
-
3
2
  import { Op } from 'sequelize';
4
- import createQueue from '../libs/queue';
3
+ import { systemFindByPk, systemFindOne } from '../store/scoped';
4
+
5
+ import createQueue, { assertJobObjectTenant } from '../libs/queue';
5
6
  import {
6
7
  AutoRechargeConfig,
7
8
  ChainType,
@@ -48,13 +49,14 @@ export async function processAutoRecharge(job: AutoRechargeJobData) {
48
49
  logger.info('Processing auto recharge job', { job });
49
50
  const { customer_id: customerId, currency_id: currencyId } = job;
50
51
 
51
- const customer = await Customer.findByPk(customerId);
52
+ const customer = await systemFindByPk(Customer, customerId);
52
53
  if (!customer) {
53
54
  logger.error('Customer not found', { customerId });
54
55
  return;
55
56
  }
57
+ assertJobObjectTenant(customer);
56
58
 
57
- const currency = await PaymentCurrency.findByPk(currencyId);
59
+ const currency = await systemFindByPk(PaymentCurrency, currencyId);
58
60
  if (!currency) {
59
61
  logger.error('Currency not found', { currencyId });
60
62
  return;
@@ -63,7 +65,7 @@ export async function processAutoRecharge(job: AutoRechargeJobData) {
63
65
  // Check if the associated meter is inactive
64
66
  if (currency.type === 'credit') {
65
67
  // Find meter by currency_id (meter.currency_id -> PaymentCurrency) or by metadata.meter_id
66
- const meter = await Meter.findOne({
68
+ const meter = await systemFindOne(Meter, {
67
69
  where: { currency_id: currencyId },
68
70
  });
69
71
  if (meter && meter.status === 'inactive') {
@@ -77,7 +79,7 @@ export async function processAutoRecharge(job: AutoRechargeJobData) {
77
79
  }
78
80
 
79
81
  // 1. find auto recharge config
80
- const config = (await AutoRechargeConfig.findOne({
82
+ const config = (await systemFindOne(AutoRechargeConfig, {
81
83
  where: {
82
84
  customer_id: customerId,
83
85
  currency_id: currencyId,
@@ -378,7 +380,7 @@ async function createInvoiceForAutoRecharge({
378
380
  }
379
381
 
380
382
  // Check for existing invoice
381
- const existInvoice = await Invoice.findOne({
383
+ const existInvoice = await systemFindOne(Invoice, {
382
384
  where: {
383
385
  customer_id: customer.id,
384
386
  currency_id: rechargeCurrency.id,
@@ -633,21 +635,21 @@ export async function checkAndTriggerAutoRecharge(
633
635
  }
634
636
 
635
637
  // T2b: Reuse caller-provided currency to avoid redundant DB query
636
- const currency = options?.currency ?? (await PaymentCurrency.findByPk(currencyId));
638
+ const currency = options?.currency ?? (await systemFindByPk(PaymentCurrency, currencyId));
637
639
 
638
640
  // Reuse caller-provided meter when available to avoid redundant DB query
639
641
  let meterPromise: Promise<any>;
640
642
  if (options?.meter != null) {
641
643
  meterPromise = Promise.resolve(options.meter);
642
644
  } else if (currency?.type === 'credit') {
643
- meterPromise = Meter.findOne({ where: { currency_id: currencyId } });
645
+ meterPromise = systemFindOne(Meter, { where: { currency_id: currencyId } });
644
646
  } else {
645
647
  meterPromise = Promise.resolve(null);
646
648
  }
647
649
 
648
650
  const [meter, config] = await Promise.all([
649
651
  meterPromise,
650
- AutoRechargeConfig.findOne({
652
+ systemFindOne(AutoRechargeConfig, {
651
653
  where: {
652
654
  customer_id: customer.id,
653
655
  currency_id: currencyId,
@@ -1,13 +1,15 @@
1
1
  /* eslint-disable no-await-in-loop */
2
2
  import { Op } from 'sequelize';
3
+ import { systemFindAll, systemFindByPk, systemFindOne } from '../store/scoped';
3
4
 
4
5
  import { mintNftForCheckoutSession } from '../integrations/arcblock/nft';
5
6
  import { ensurePassportIssued } from '../integrations/blocklet/passport';
6
7
  import { ensureInvoiceForCheckout } from '../routes/connect/shared';
8
+ import { withTenant } from '../libs/context';
7
9
  import dayjs from '../libs/dayjs';
8
10
  import { events } from '../libs/event';
9
11
  import logger from '../libs/logger';
10
- import createQueue from '../libs/queue';
12
+ import createQueue, { assertJobObjectTenant } from '../libs/queue';
11
13
  import {
12
14
  CheckoutSession,
13
15
  Customer,
@@ -40,11 +42,12 @@ export const checkoutSessionQueue = createQueue<CheckoutSessionJob>({
40
42
  });
41
43
 
42
44
  export async function handleCheckoutSessionJob(job: CheckoutSessionJob): Promise<void> {
43
- const checkoutSession = await CheckoutSession.findByPk(job.id);
45
+ const checkoutSession = await systemFindByPk(CheckoutSession, job.id);
44
46
  if (!checkoutSession) {
45
47
  logger.warn('CheckoutSession not found', { id: job.id });
46
48
  return;
47
49
  }
50
+ assertJobObjectTenant(checkoutSession);
48
51
  if (job.action === 'expire') {
49
52
  if (checkoutSession.status !== 'open') {
50
53
  logger.info('Skip expire CheckoutSession since status is not open', {
@@ -80,23 +83,34 @@ export async function handleCheckoutSessionJob(job: CheckoutSessionJob): Promise
80
83
  export async function startCheckoutSessionQueue() {
81
84
  // Auto populate subscription queue
82
85
  const now = dayjs().unix();
83
- const checkoutSessions = await CheckoutSession.findAll({
86
+ const checkoutSessions = await systemFindAll(CheckoutSession, {
84
87
  where: {
85
88
  status: 'open',
86
89
  expires_at: { [Op.lte]: now },
87
90
  },
88
91
  });
89
92
 
90
- checkoutSessions.forEach(async (checkoutSession) => {
91
- const exist = await checkoutSessionQueue.get(checkoutSession.id);
92
- if (!exist) {
93
- checkoutSessionQueue.push({
94
- id: checkoutSession.id,
95
- job: { id: checkoutSession.id, action: 'expire' },
96
- runAt: checkoutSession.expires_at,
97
- });
93
+ // Per-record tenant context: queue push stamps the job tenant via getInstanceDid,
94
+ // which fails closed in multi mode without it. for-of + await (not forEach(async),
95
+ // which leaks an unhandledRejection → FATAL on boot); per-record catch isolates.
96
+ for (const checkoutSession of checkoutSessions) {
97
+ const dispatch = async () => {
98
+ const exist = await checkoutSessionQueue.get(checkoutSession.id);
99
+ if (!exist) {
100
+ checkoutSessionQueue.push({
101
+ id: checkoutSession.id,
102
+ job: { id: checkoutSession.id, action: 'expire' },
103
+ runAt: checkoutSession.expires_at,
104
+ });
105
+ }
106
+ };
107
+ try {
108
+ // eslint-disable-next-line no-await-in-loop
109
+ await (checkoutSession.instance_did ? withTenant(checkoutSession.instance_did, dispatch) : dispatch());
110
+ } catch (error) {
111
+ logger.error('startCheckoutSessionQueue: re-queue failed', { id: checkoutSession.id, error });
98
112
  }
99
- });
113
+ }
100
114
  }
101
115
 
102
116
  checkoutSessionQueue.on('failed', ({ id, job, error }) => {
@@ -146,7 +160,7 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
146
160
  });
147
161
  } else {
148
162
  // Do some reverse lookup if invoice is not related to checkout session
149
- const invoice = await Invoice.findOne({ where: { checkout_session_id: checkoutSession.id } });
163
+ const invoice = await systemFindOne(Invoice, { where: { checkout_session_id: checkoutSession.id } });
150
164
  if (invoice) {
151
165
  await destroyExistingInvoice(invoice);
152
166
  logger.info('Invoice and InvoiceItem for checkout session deleted on expire', {
@@ -165,10 +179,10 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
165
179
  }
166
180
 
167
181
  if (checkoutSession.payment_intent_id && checkoutSession.payment_status !== 'paid') {
168
- const paymentIntent = await PaymentIntent.findByPk(checkoutSession.payment_intent_id);
182
+ const paymentIntent = await systemFindByPk(PaymentIntent, checkoutSession.payment_intent_id);
169
183
  const stripePaymentId = paymentIntent?.payment_details?.stripe?.payment_intent_id;
170
184
  if (paymentIntent && stripePaymentId) {
171
- const method = await PaymentMethod.findByPk(paymentIntent.payment_method_id);
185
+ const method = await systemFindByPk(PaymentMethod, paymentIntent.payment_method_id);
172
186
  if (method?.type === 'stripe') {
173
187
  const client = method.getStripeClient();
174
188
  try {
@@ -198,12 +212,12 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
198
212
 
199
213
  const subscriptionIds = getCheckoutSessionSubscriptionIds(checkoutSession);
200
214
  if (subscriptionIds.length > 0) {
201
- const subscriptions = await Subscription.findAll({ where: { id: { [Op.in]: subscriptionIds } } });
215
+ const subscriptions = await systemFindAll(Subscription, { where: { id: { [Op.in]: subscriptionIds } } });
202
216
  await Promise.all(
203
217
  subscriptions.map(async (subscription) => {
204
218
  const stripeSubscriptionId = subscription?.payment_details?.stripe?.subscription_id;
205
219
  if (subscription && stripeSubscriptionId) {
206
- const method = await PaymentMethod.findByPk(subscription.default_payment_method_id);
220
+ const method = await systemFindByPk(PaymentMethod, subscription.default_payment_method_id);
207
221
  if (method?.type === 'stripe') {
208
222
  const client = method.getStripeClient();
209
223
  try {
@@ -245,7 +259,7 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
245
259
 
246
260
  // update price lock status
247
261
  for (const item of checkoutSession.line_items) {
248
- const price = await Price.findByPk(item.price_id);
262
+ const price = await systemFindByPk(Price, item.price_id);
249
263
  if (price?.locked) {
250
264
  const used = await price.isUsed(false);
251
265
  logger.info('Price used status recheck on expire', {
@@ -269,11 +283,12 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
269
283
  events.on(
270
284
  'checkout.session.pending_invoice',
271
285
  async ({ checkoutSessionId, paymentIntentId }: { checkoutSessionId: string; paymentIntentId: string }) => {
272
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
286
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId);
273
287
  if (!checkoutSession) {
274
288
  logger.warn('CheckoutSession not found for pending invoice', { checkoutSessionId });
275
289
  return;
276
290
  }
291
+ assertJobObjectTenant(checkoutSession);
277
292
  if (checkoutSession.invoice_id) {
278
293
  logger.info('Invoice already exists for checkout session', {
279
294
  checkoutSessionId,
@@ -293,15 +308,17 @@ events.on(
293
308
  return;
294
309
  }
295
310
 
296
- const paymentIntent = await PaymentIntent.findByPk(paymentIntentId);
311
+ const paymentIntent = await systemFindByPk(PaymentIntent, paymentIntentId);
297
312
  if (!paymentIntent) {
298
313
  return;
299
314
  }
315
+ assertJobObjectTenant(paymentIntent);
300
316
 
301
- const customer = await Customer.findByPk(checkoutSession.customer_id);
317
+ const customer = await systemFindByPk(Customer, checkoutSession.customer_id);
302
318
  if (!customer) {
303
319
  return;
304
320
  }
321
+ assertJobObjectTenant(customer);
305
322
 
306
323
  await ensureInvoiceForCheckout({
307
324
  checkoutSession,