payment-kit 1.29.0 → 1.29.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (312) 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 +36 -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 +27 -24
  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/index.ts +22 -161
  12. package/api/src/integrations/app-store/client.ts +3 -4
  13. package/api/src/integrations/app-store/handlers/subscription.ts +7 -7
  14. package/api/src/integrations/app-store/signed-data-verifier.ts +3 -2
  15. package/api/src/integrations/arcblock/token.ts +21 -7
  16. package/api/src/integrations/google-play/handlers/subscription.ts +6 -6
  17. package/api/src/integrations/google-play/handlers/voided.ts +2 -2
  18. package/api/src/integrations/google-play/verify.ts +3 -2
  19. package/api/src/integrations/iap-reconcile.ts +3 -5
  20. package/api/src/integrations/stripe/handlers/invoice.ts +2 -2
  21. package/api/src/integrations/stripe/handlers/subscription.ts +3 -3
  22. package/api/src/libs/archive/query.ts +19 -0
  23. package/api/src/libs/audit.ts +61 -4
  24. package/api/src/libs/auth.ts +99 -38
  25. package/api/src/libs/context.ts +78 -1
  26. package/api/src/libs/currency.ts +2 -2
  27. package/api/src/libs/dayjs.ts +8 -2
  28. package/api/src/libs/drivers/auth-storage.ts +118 -0
  29. package/api/src/libs/drivers/cron.ts +264 -0
  30. package/api/src/libs/drivers/db.ts +170 -0
  31. package/api/src/libs/drivers/identity.ts +81 -0
  32. package/api/src/libs/drivers/index.ts +40 -0
  33. package/api/src/libs/drivers/locks.ts +226 -0
  34. package/api/src/libs/drivers/migrate-runner.ts +70 -0
  35. package/api/src/libs/drivers/queue.ts +104 -0
  36. package/api/src/libs/drivers/secrets.ts +194 -0
  37. package/api/src/libs/env.ts +170 -54
  38. package/api/src/libs/exchange-rate/service.ts +7 -6
  39. package/api/src/libs/http-fetch-adapter.ts +50 -0
  40. package/api/src/libs/invoice.ts +1 -1
  41. package/api/src/libs/lock.ts +51 -47
  42. package/api/src/libs/logger.ts +48 -8
  43. package/api/src/libs/notification/index.ts +1 -1
  44. package/api/src/libs/notification/template/customer-credit-low-balance.ts +2 -1
  45. package/api/src/libs/notification/template/customer-revenue-succeeded.ts +1 -1
  46. package/api/src/libs/notification/template/customer-reward-succeeded.ts +1 -1
  47. package/api/src/libs/overdraft-protection.ts +1 -1
  48. package/api/src/libs/payout.ts +1 -1
  49. package/api/src/libs/queue/index.ts +259 -52
  50. package/api/src/libs/queue/runtime.ts +175 -0
  51. package/api/src/libs/resource.ts +3 -3
  52. package/api/src/libs/secrets.ts +38 -0
  53. package/api/src/libs/session.ts +3 -2
  54. package/api/src/libs/subscription.ts +5 -5
  55. package/api/src/libs/tenant.ts +92 -0
  56. package/api/src/libs/url.ts +3 -3
  57. package/api/src/libs/util.ts +21 -13
  58. package/api/src/middlewares/hono/cdn.ts +63 -0
  59. package/api/src/middlewares/hono/context.ts +73 -0
  60. package/api/src/middlewares/hono/csrf.ts +72 -0
  61. package/api/src/middlewares/hono/fallback.ts +194 -0
  62. package/api/src/middlewares/hono/pipeline.ts +73 -0
  63. package/api/src/middlewares/hono/resource-mount.ts +42 -0
  64. package/api/src/middlewares/hono/resource.ts +63 -0
  65. package/api/src/middlewares/hono/security.ts +214 -0
  66. package/api/src/middlewares/hono/session.ts +114 -0
  67. package/api/src/middlewares/hono/xss.ts +61 -0
  68. package/api/src/queues/auto-recharge.ts +12 -10
  69. package/api/src/queues/checkout-session.ts +17 -12
  70. package/api/src/queues/credit-consume.ts +40 -36
  71. package/api/src/queues/credit-grant.ts +25 -18
  72. package/api/src/queues/credit-reconciliation.ts +7 -5
  73. package/api/src/queues/discount-status.ts +9 -6
  74. package/api/src/queues/event.ts +12 -4
  75. package/api/src/queues/exchange-rate-health.ts +49 -30
  76. package/api/src/queues/invoice.ts +18 -15
  77. package/api/src/queues/notification.ts +14 -7
  78. package/api/src/queues/payment.ts +41 -28
  79. package/api/src/queues/payout.ts +9 -5
  80. package/api/src/queues/refund.ts +18 -12
  81. package/api/src/queues/subscription.ts +83 -53
  82. package/api/src/queues/token-transfer.ts +15 -10
  83. package/api/src/queues/usage-record.ts +8 -5
  84. package/api/src/queues/vendors/commission.ts +7 -5
  85. package/api/src/queues/vendors/fulfillment-coordinator.ts +17 -13
  86. package/api/src/queues/vendors/fulfillment.ts +4 -2
  87. package/api/src/queues/vendors/return-processor.ts +5 -3
  88. package/api/src/queues/vendors/return-scanner.ts +5 -4
  89. package/api/src/queues/vendors/status-check.ts +10 -7
  90. package/api/src/queues/webhook.ts +60 -32
  91. package/api/src/routes/connect/shared.ts +1 -2
  92. package/api/src/routes/connect/subscribe.ts +3 -3
  93. package/api/src/routes/{archive.ts → hono/archive.ts} +69 -64
  94. package/api/src/routes/{auto-recharge-configs.ts → hono/auto-recharge-configs.ts} +39 -28
  95. package/api/src/routes/{checkout-sessions.ts → hono/checkout-sessions.ts} +790 -923
  96. package/api/src/routes/{coupons.ts → hono/coupons.ts} +93 -76
  97. package/api/src/routes/{credit-grants.ts → hono/credit-grants.ts} +140 -126
  98. package/api/src/routes/hono/credit-tokens.ts +43 -0
  99. package/api/src/routes/{credit-transactions.ts → hono/credit-transactions.ts} +37 -29
  100. package/api/src/routes/{customers.ts → hono/customers.ts} +193 -223
  101. package/api/src/routes/{donations.ts → hono/donations.ts} +41 -32
  102. package/api/src/routes/{entitlements.ts → hono/entitlements.ts} +28 -25
  103. package/api/src/routes/{events.ts → hono/events.ts} +107 -71
  104. package/api/src/routes/{exchange-rate-providers.ts → hono/exchange-rate-providers.ts} +138 -126
  105. package/api/src/routes/hono/exchange-rates.ts +77 -0
  106. package/api/src/routes/hono/index.ts +115 -0
  107. package/api/src/routes/{integrations → hono/integrations}/app-store.ts +68 -48
  108. package/api/src/routes/{integrations → hono/integrations}/google-play.ts +78 -58
  109. package/api/src/routes/hono/integrations/stripe.ts +74 -0
  110. package/api/src/routes/{invoices.ts → hono/invoices.ts} +253 -244
  111. package/api/src/routes/{meter-events.ts → hono/meter-events.ts} +120 -110
  112. package/api/src/routes/hono/meters.ts +288 -0
  113. package/api/src/routes/hono/passports.ts +73 -0
  114. package/api/src/routes/{payment-currencies.ts → hono/payment-currencies.ts} +219 -197
  115. package/api/src/routes/{payment-intents.ts → hono/payment-intents.ts} +136 -132
  116. package/api/src/routes/{payment-links.ts → hono/payment-links.ts} +145 -128
  117. package/api/src/routes/{payment-methods.ts → hono/payment-methods.ts} +125 -93
  118. package/api/src/routes/{payment-stats.ts → hono/payment-stats.ts} +30 -25
  119. package/api/src/routes/{payouts.ts → hono/payouts.ts} +55 -47
  120. package/api/src/routes/{prices.ts → hono/prices.ts} +265 -242
  121. package/api/src/routes/{pricing-table.ts → hono/pricing-table.ts} +94 -87
  122. package/api/src/routes/{products.ts → hono/products.ts} +172 -159
  123. package/api/src/routes/{promotion-codes.ts → hono/promotion-codes.ts} +207 -185
  124. package/api/src/routes/hono/redirect.ts +24 -0
  125. package/api/src/routes/{refunds.ts → hono/refunds.ts} +96 -80
  126. package/api/src/routes/{settings.ts → hono/settings.ts} +64 -55
  127. package/api/src/routes/{subscription-items.ts → hono/subscription-items.ts} +64 -57
  128. package/api/src/routes/{subscriptions.ts → hono/subscriptions.ts} +475 -528
  129. package/api/src/routes/{tax-rates.ts → hono/tax-rates.ts} +71 -70
  130. package/api/src/routes/hono/tool.ts +69 -0
  131. package/api/src/routes/{usage-records.ts → hono/usage-records.ts} +47 -42
  132. package/api/src/routes/{vendor.ts → hono/vendor.ts} +315 -167
  133. package/api/src/routes/{webhook-attempts.ts → hono/webhook-attempts.ts} +17 -13
  134. package/api/src/routes/hono/webhook-endpoints.ts +126 -0
  135. package/api/src/service.ts +667 -0
  136. package/api/src/store/migrations/20230911-seeding.ts +2 -1
  137. package/api/src/store/migrations/20260609-remove-did-space-jobs.ts +23 -0
  138. package/api/src/store/migrations/20260610-tenant-columns.ts +40 -0
  139. package/api/src/store/migrations/20260611-tenant-backfill.ts +33 -0
  140. package/api/src/store/models/auto-recharge-config.ts +22 -10
  141. package/api/src/store/models/checkout-session.ts +15 -14
  142. package/api/src/store/models/coupon.ts +29 -20
  143. package/api/src/store/models/credit-grant.ts +38 -29
  144. package/api/src/store/models/credit-transaction.ts +32 -21
  145. package/api/src/store/models/customer.ts +19 -17
  146. package/api/src/store/models/discount.ts +11 -2
  147. package/api/src/store/models/entitlement-grant.ts +21 -9
  148. package/api/src/store/models/entitlement-product.ts +21 -9
  149. package/api/src/store/models/entitlement.ts +19 -10
  150. package/api/src/store/models/event.ts +18 -9
  151. package/api/src/store/models/exchange-rate-provider.ts +17 -4
  152. package/api/src/store/models/invoice-item.ts +18 -9
  153. package/api/src/store/models/invoice.ts +16 -8
  154. package/api/src/store/models/meter-event.ts +27 -9
  155. package/api/src/store/models/meter.ts +31 -22
  156. package/api/src/store/models/payment-currency.ts +25 -8
  157. package/api/src/store/models/payment-intent.ts +15 -6
  158. package/api/src/store/models/payment-link.ts +15 -6
  159. package/api/src/store/models/payment-method.ts +38 -22
  160. package/api/src/store/models/payment-stat.ts +18 -9
  161. package/api/src/store/models/payout.ts +15 -6
  162. package/api/src/store/models/price-quote.ts +17 -8
  163. package/api/src/store/models/price.ts +24 -12
  164. package/api/src/store/models/pricing-table.ts +29 -20
  165. package/api/src/store/models/product-vendor.ts +20 -10
  166. package/api/src/store/models/product.ts +15 -6
  167. package/api/src/store/models/promotion-code.ts +14 -6
  168. package/api/src/store/models/refund.ts +15 -6
  169. package/api/src/store/models/revenue-snapshot.ts +21 -9
  170. package/api/src/store/models/setting.ts +18 -9
  171. package/api/src/store/models/setup-intent.ts +36 -27
  172. package/api/src/store/models/subscription-item.ts +21 -9
  173. package/api/src/store/models/subscription-schedule.ts +21 -9
  174. package/api/src/store/models/subscription.ts +21 -10
  175. package/api/src/store/models/tax-rate.ts +29 -21
  176. package/api/src/store/models/usage-record.ts +11 -2
  177. package/api/src/store/models/webhook-attempt.ts +18 -9
  178. package/api/src/store/models/webhook-endpoint.ts +18 -9
  179. package/api/src/store/scoped-core.ts +55 -0
  180. package/api/src/store/scoped.ts +247 -0
  181. package/api/src/store/sequelize.ts +66 -22
  182. package/api/src/store/sql-migrations.ts +20 -0
  183. package/api/src/store/tenant-backfill.ts +260 -0
  184. package/api/src/store/tenant-model.ts +124 -0
  185. package/api/src/store/tenant-tables.ts +50 -0
  186. package/api/tests/embedded/embedded-multi-mode-d3.spec.ts +257 -0
  187. package/api/tests/fixtures/bare-query-violation.ts +13 -0
  188. package/api/tests/fixtures/core-env-violation.ts +10 -0
  189. package/api/tests/fixtures/host-read-violation.ts +19 -0
  190. package/api/tests/fixtures/tenants.ts +4 -0
  191. package/api/tests/integrations/iap-tenant.spec.ts +284 -0
  192. package/api/tests/libs/archive-query.spec.ts +26 -0
  193. package/api/tests/libs/audit-tenant.spec.ts +153 -0
  194. package/api/tests/libs/context.spec.ts +204 -0
  195. package/api/tests/libs/core-config.spec.ts +115 -0
  196. package/api/tests/libs/cron-driver-d2.spec.ts +237 -0
  197. package/api/tests/libs/crons-conservation-d2.spec.ts +52 -0
  198. package/api/tests/libs/lock-tenant.spec.ts +66 -0
  199. package/api/tests/libs/scoped.spec.ts +222 -0
  200. package/api/tests/libs/secrets-facade.spec.ts +52 -0
  201. package/api/tests/libs/tenancy-slot-authority.spec.ts +209 -0
  202. package/api/tests/libs/tenant-middleware.spec.ts +42 -0
  203. package/api/tests/libs/tenant-scanner.spec.ts +120 -0
  204. package/api/tests/middlewares/hono/cdn.spec.ts +70 -0
  205. package/api/tests/middlewares/hono/context.spec.ts +113 -0
  206. package/api/tests/middlewares/hono/csrf.spec.ts +136 -0
  207. package/api/tests/middlewares/hono/fallback.spec.ts +67 -0
  208. package/api/tests/middlewares/hono/pipeline.spec.ts +47 -0
  209. package/api/tests/middlewares/hono/security.spec.ts +181 -0
  210. package/api/tests/middlewares/hono/session.spec.ts +42 -0
  211. package/api/tests/middlewares/hono/xss.spec.ts +81 -0
  212. package/api/tests/models/tenant-backfill.spec.ts +287 -0
  213. package/api/tests/models/tenant-columns-model.spec.ts +46 -0
  214. package/api/tests/models/tenant-columns.spec.ts +161 -0
  215. package/api/tests/queues/credit-consume-batch.spec.ts +8 -1
  216. package/api/tests/queues/credit-consume.spec.ts +8 -1
  217. package/api/tests/queues/event-tenant.spec.ts +236 -0
  218. package/api/tests/queues/exchange-rate-health-tenant-d6.spec.ts +62 -0
  219. package/api/tests/queues/queue-parity.spec.ts +249 -0
  220. package/api/tests/queues/queue-runtime-surface.spec.ts +277 -0
  221. package/api/tests/queues/queue-teardown-d2.spec.ts +127 -0
  222. package/api/tests/queues/tenant-matrix-a.spec.ts +245 -0
  223. package/api/tests/queues/tenant-matrix-b.spec.ts +168 -0
  224. package/api/tests/routes/connect/hono-attach.spec.ts +107 -0
  225. package/api/tests/service/collapse.spec.ts +96 -0
  226. package/api/tests/store/tenant-crosscut.spec.ts +202 -0
  227. package/api/tests/store/tenant-model-spike.spec.ts +177 -0
  228. package/api/tests/store/tenant-model.spec.ts +162 -0
  229. package/api/tests/store/tenant-residual.spec.ts +196 -0
  230. package/api/third.d.ts +4 -0
  231. package/blocklet.yml +1 -1
  232. package/cloudflare/README.md +26 -6
  233. package/cloudflare/build.ts +28 -13
  234. package/cloudflare/did-connect-auth.ts +0 -217
  235. package/cloudflare/docs/2026-06-10-bundle-size-analysis.md +288 -0
  236. package/cloudflare/migrations/0006_tenant_columns.sql +46 -0
  237. package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
  238. package/cloudflare/migrations/0008_schema_parity.sql +16 -0
  239. package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
  240. package/cloudflare/queue-runtime-mode.ts +13 -0
  241. package/cloudflare/run-build.js +31 -56
  242. package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
  243. package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
  244. package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
  245. package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
  246. package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
  247. package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
  248. package/cloudflare/shims/blocklet-sdk/util-csrf.ts +13 -0
  249. package/cloudflare/shims/blocklet-sdk/util-wallet.ts +8 -0
  250. package/cloudflare/shims/cron.ts +38 -158
  251. package/cloudflare/shims/events.ts +124 -0
  252. package/cloudflare/shims/fastq.ts +15 -1
  253. package/cloudflare/shims/nedb-storage.ts +16 -8
  254. package/cloudflare/shims/node-fetch.ts +35 -0
  255. package/cloudflare/shims/xss.ts +8 -0
  256. package/cloudflare/tenant-middleware.ts +36 -0
  257. package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
  258. package/cloudflare/tests/worker-handler-gate.spec.ts +44 -0
  259. package/cloudflare/worker.ts +204 -433
  260. package/cloudflare/wrangler.local-e2e.jsonc +26 -0
  261. package/jest.config.js +3 -1
  262. package/package.json +33 -38
  263. package/scripts/core-env-whitelist.json +1 -0
  264. package/scripts/e2e-12b-runtime.ts +149 -0
  265. package/scripts/e2e-core-config.ts +125 -0
  266. package/scripts/e2e-d1-tenancy.ts +116 -0
  267. package/scripts/e2e-d2-cron-queue.ts +139 -0
  268. package/scripts/e2e-d3-embedded-multi.ts +171 -0
  269. package/scripts/e2e-hono-s2.ts +125 -0
  270. package/scripts/e2e-hono-s3e.ts +135 -0
  271. package/scripts/e2e-hono-s4.ts +114 -0
  272. package/scripts/e2e-migration-contract.ts +100 -0
  273. package/scripts/e2e-s0.ts +61 -0
  274. package/scripts/e2e-s1.ts +107 -0
  275. package/scripts/e2e-s2.ts +178 -0
  276. package/scripts/e2e-s3.ts +110 -0
  277. package/scripts/e2e-s4.ts +191 -0
  278. package/scripts/e2e-s5.ts +139 -0
  279. package/scripts/e2e-s6.ts +127 -0
  280. package/scripts/e2e-tenant-model.ts +119 -0
  281. package/scripts/e2e-tenant-worker.ts +199 -0
  282. package/scripts/gen-sql-migrations.js +46 -0
  283. package/scripts/phase8-codemod.js +219 -0
  284. package/scripts/phase9a-env-getters-codemod.js +82 -0
  285. package/scripts/scan-core-env.js +109 -0
  286. package/scripts/scan-tenant-queries.js +235 -0
  287. package/scripts/schema-drift-guard.ts +210 -0
  288. package/scripts/tenant-scan-whitelist.json +1 -0
  289. package/src/env.d.ts +13 -1
  290. package/tsconfig.json +1 -1
  291. package/api/src/libs/did-space.ts +0 -235
  292. package/api/src/libs/middleware.ts +0 -50
  293. package/api/src/libs/security.ts +0 -192
  294. package/api/src/queues/space.ts +0 -662
  295. package/api/src/routes/credit-tokens.ts +0 -38
  296. package/api/src/routes/exchange-rates.ts +0 -87
  297. package/api/src/routes/index.ts +0 -142
  298. package/api/src/routes/integrations/stripe.ts +0 -61
  299. package/api/src/routes/meters.ts +0 -274
  300. package/api/src/routes/passports.ts +0 -68
  301. package/api/src/routes/redirect.ts +0 -20
  302. package/api/src/routes/tool.ts +0 -65
  303. package/api/src/routes/webhook-endpoints.ts +0 -126
  304. package/api/tests/routes/credit-grants.spec.ts +0 -1261
  305. package/cloudflare/shims/did-space-js.ts +0 -17
  306. package/cloudflare/shims/did-space.ts +0 -11
  307. package/cloudflare/shims/express-compat/index.ts +0 -80
  308. package/cloudflare/shims/express-compat/types.ts +0 -41
  309. package/cloudflare/shims/lock.ts +0 -115
  310. package/cloudflare/shims/queue.ts +0 -611
  311. package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
  312. 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,5 +1,6 @@
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';
@@ -7,7 +8,7 @@ import { ensureInvoiceForCheckout } from '../routes/connect/shared';
7
8
  import dayjs from '../libs/dayjs';
8
9
  import { events } from '../libs/event';
9
10
  import logger from '../libs/logger';
10
- import createQueue from '../libs/queue';
11
+ import createQueue, { assertJobObjectTenant } from '../libs/queue';
11
12
  import {
12
13
  CheckoutSession,
13
14
  Customer,
@@ -40,11 +41,12 @@ export const checkoutSessionQueue = createQueue<CheckoutSessionJob>({
40
41
  });
41
42
 
42
43
  export async function handleCheckoutSessionJob(job: CheckoutSessionJob): Promise<void> {
43
- const checkoutSession = await CheckoutSession.findByPk(job.id);
44
+ const checkoutSession = await systemFindByPk(CheckoutSession, job.id);
44
45
  if (!checkoutSession) {
45
46
  logger.warn('CheckoutSession not found', { id: job.id });
46
47
  return;
47
48
  }
49
+ assertJobObjectTenant(checkoutSession);
48
50
  if (job.action === 'expire') {
49
51
  if (checkoutSession.status !== 'open') {
50
52
  logger.info('Skip expire CheckoutSession since status is not open', {
@@ -80,7 +82,7 @@ export async function handleCheckoutSessionJob(job: CheckoutSessionJob): Promise
80
82
  export async function startCheckoutSessionQueue() {
81
83
  // Auto populate subscription queue
82
84
  const now = dayjs().unix();
83
- const checkoutSessions = await CheckoutSession.findAll({
85
+ const checkoutSessions = await systemFindAll(CheckoutSession, {
84
86
  where: {
85
87
  status: 'open',
86
88
  expires_at: { [Op.lte]: now },
@@ -146,7 +148,7 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
146
148
  });
147
149
  } else {
148
150
  // Do some reverse lookup if invoice is not related to checkout session
149
- const invoice = await Invoice.findOne({ where: { checkout_session_id: checkoutSession.id } });
151
+ const invoice = await systemFindOne(Invoice, { where: { checkout_session_id: checkoutSession.id } });
150
152
  if (invoice) {
151
153
  await destroyExistingInvoice(invoice);
152
154
  logger.info('Invoice and InvoiceItem for checkout session deleted on expire', {
@@ -165,10 +167,10 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
165
167
  }
166
168
 
167
169
  if (checkoutSession.payment_intent_id && checkoutSession.payment_status !== 'paid') {
168
- const paymentIntent = await PaymentIntent.findByPk(checkoutSession.payment_intent_id);
170
+ const paymentIntent = await systemFindByPk(PaymentIntent, checkoutSession.payment_intent_id);
169
171
  const stripePaymentId = paymentIntent?.payment_details?.stripe?.payment_intent_id;
170
172
  if (paymentIntent && stripePaymentId) {
171
- const method = await PaymentMethod.findByPk(paymentIntent.payment_method_id);
173
+ const method = await systemFindByPk(PaymentMethod, paymentIntent.payment_method_id);
172
174
  if (method?.type === 'stripe') {
173
175
  const client = method.getStripeClient();
174
176
  try {
@@ -198,12 +200,12 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
198
200
 
199
201
  const subscriptionIds = getCheckoutSessionSubscriptionIds(checkoutSession);
200
202
  if (subscriptionIds.length > 0) {
201
- const subscriptions = await Subscription.findAll({ where: { id: { [Op.in]: subscriptionIds } } });
203
+ const subscriptions = await systemFindAll(Subscription, { where: { id: { [Op.in]: subscriptionIds } } });
202
204
  await Promise.all(
203
205
  subscriptions.map(async (subscription) => {
204
206
  const stripeSubscriptionId = subscription?.payment_details?.stripe?.subscription_id;
205
207
  if (subscription && stripeSubscriptionId) {
206
- const method = await PaymentMethod.findByPk(subscription.default_payment_method_id);
208
+ const method = await systemFindByPk(PaymentMethod, subscription.default_payment_method_id);
207
209
  if (method?.type === 'stripe') {
208
210
  const client = method.getStripeClient();
209
211
  try {
@@ -245,7 +247,7 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
245
247
 
246
248
  // update price lock status
247
249
  for (const item of checkoutSession.line_items) {
248
- const price = await Price.findByPk(item.price_id);
250
+ const price = await systemFindByPk(Price, item.price_id);
249
251
  if (price?.locked) {
250
252
  const used = await price.isUsed(false);
251
253
  logger.info('Price used status recheck on expire', {
@@ -269,11 +271,12 @@ events.on('checkout.session.expired', async (checkoutSession: CheckoutSession) =
269
271
  events.on(
270
272
  'checkout.session.pending_invoice',
271
273
  async ({ checkoutSessionId, paymentIntentId }: { checkoutSessionId: string; paymentIntentId: string }) => {
272
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
274
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId);
273
275
  if (!checkoutSession) {
274
276
  logger.warn('CheckoutSession not found for pending invoice', { checkoutSessionId });
275
277
  return;
276
278
  }
279
+ assertJobObjectTenant(checkoutSession);
277
280
  if (checkoutSession.invoice_id) {
278
281
  logger.info('Invoice already exists for checkout session', {
279
282
  checkoutSessionId,
@@ -293,15 +296,17 @@ events.on(
293
296
  return;
294
297
  }
295
298
 
296
- const paymentIntent = await PaymentIntent.findByPk(paymentIntentId);
299
+ const paymentIntent = await systemFindByPk(PaymentIntent, paymentIntentId);
297
300
  if (!paymentIntent) {
298
301
  return;
299
302
  }
303
+ assertJobObjectTenant(paymentIntent);
300
304
 
301
- const customer = await Customer.findByPk(checkoutSession.customer_id);
305
+ const customer = await systemFindByPk(Customer, checkoutSession.customer_id);
302
306
  if (!customer) {
303
307
  return;
304
308
  }
309
+ assertJobObjectTenant(customer);
305
310
 
306
311
  await ensureInvoiceForCheckout({
307
312
  checkoutSession,
@@ -1,11 +1,19 @@
1
1
  import { BN, fromUnitToToken } from '@ocap/util';
2
2
  import { Op } from 'sequelize';
3
3
  import pAll from 'p-all';
4
+ import {
5
+ isCfWorker,
6
+ creditLowBalanceThresholdPercentage,
7
+ creditBatchSize,
8
+ creditBatchWindowMs,
9
+ creditQueueConcurrency,
10
+ } from '../libs/env';
11
+ import { systemFindAll, systemFindByPk, systemFindOne } from '../store/scoped';
4
12
 
5
13
  import { getLock } from '../libs/lock';
6
14
  import logger from '../libs/logger';
7
- import createQueue from '../libs/queue';
8
- import { createEvent } from '../libs/audit';
15
+ import createQueue, { assertJobObjectTenant } from '../libs/queue';
16
+ import { createEvent, reportAuditFailure } from '../libs/audit';
9
17
  import { MeterEvent, CreditGrant, CreditTransaction, Customer, Subscription, TMeterExpanded } from '../store/models';
10
18
  import { getCachedMeterExpanded } from '../libs/reference-cache';
11
19
 
@@ -20,7 +28,7 @@ import { addTokenTransferJob } from './token-transfer';
20
28
  // In Blocklet Server (no Queue ops limit), use the full MAX_RETRY_COUNT.
21
29
  // After max retries, mark as requires_action — retryFailedEventsForCustomer()
22
30
  // picks them up when credit is granted.
23
- const CREDIT_MAX_RETRY = (globalThis as any).__CF_ENV__ ? 5 : MAX_RETRY_COUNT;
31
+ const CREDIT_MAX_RETRY = isCfWorker() ? 5 : MAX_RETRY_COUNT;
24
32
 
25
33
  type CreditConsumptionJob = {
26
34
  meterEventId: string;
@@ -82,7 +90,7 @@ async function checkLowBalance(
82
90
  if (totalCreditAmountBn.lte(new BN(0))) return;
83
91
  const remainingAmountBn = new BN(remainingBalance);
84
92
  // Get threshold percentage from env var, default to 10%
85
- const thresholdPercentage = parseInt(process.env.CREDIT_LOW_BALANCE_THRESHOLD_PERCENTAGE || '10', 10);
93
+ const thresholdPercentage = creditLowBalanceThresholdPercentage();
86
94
  const threshold = totalCreditAmountBn.mul(new BN(thresholdPercentage)).div(new BN(100));
87
95
  if (remainingAmountBn.gt(new BN(0)) && remainingAmountBn.lte(threshold)) {
88
96
  const percentage = remainingAmountBn.mul(new BN(100)).div(totalCreditAmountBn).toString();
@@ -94,7 +102,7 @@ async function checkLowBalance(
94
102
  percentage,
95
103
  subscription_id: context.subscription?.id,
96
104
  },
97
- }).catch(console.error);
105
+ }).catch(reportAuditFailure);
98
106
  }
99
107
  } catch (error: any) {
100
108
  logger.error('Failed to check low balance', {
@@ -118,7 +126,7 @@ async function loadReferenceData(
118
126
 
119
127
  const [meter, customer] = await Promise.all([
120
128
  getCachedMeterExpanded(meterEvent.event_name) as Promise<TMeterExpanded | null>,
121
- Customer.findByPk(customerId),
129
+ systemFindByPk(Customer, customerId),
122
130
  ]);
123
131
 
124
132
  if (!meter) {
@@ -163,7 +171,7 @@ async function consumeAvailableCredits(
163
171
  const currencyId = context.meter.currency_id!;
164
172
  const meterEventId = context.meterEvent.id;
165
173
 
166
- const existingTransactions = await CreditTransaction.findAll({
174
+ const existingTransactions = await systemFindAll(CreditTransaction, {
167
175
  where: {
168
176
  source: meterEventId,
169
177
  },
@@ -300,7 +308,7 @@ async function handlePostConsumptionEvents(
300
308
  currency_id: currencyId,
301
309
  subscription_id: context.subscription?.id,
302
310
  },
303
- }).catch(console.error);
311
+ }).catch(reportAuditFailure);
304
312
  }
305
313
 
306
314
  // 如果有关联订阅且订阅活跃,将其标记为逾期
@@ -338,7 +346,7 @@ async function handlePostConsumptionEvents(
338
346
  currency_id: currencyId,
339
347
  subscription_id: context.subscription?.id,
340
348
  },
341
- }).catch(console.error);
349
+ }).catch(reportAuditFailure);
342
350
  }
343
351
 
344
352
  if (!insufficientTriggered) {
@@ -479,7 +487,7 @@ async function createCreditTransaction(
479
487
  });
480
488
 
481
489
  // 重新查询已存在的 transaction
482
- const duplicateTransaction = await CreditTransaction.findOne({
490
+ const duplicateTransaction = await systemFindOne(CreditTransaction, {
483
491
  where: {
484
492
  source: meterEventId,
485
493
  credit_grant_id: creditGrantId,
@@ -508,11 +516,12 @@ export async function handleCreditConsumption(job: CreditConsumptionJob) {
508
516
  logger.info('Starting credit consumption job', { meterEventId });
509
517
 
510
518
  // Pre-check before acquiring lock
511
- const preCheckEvent = await MeterEvent.findByPk(meterEventId);
519
+ const preCheckEvent = await systemFindByPk(MeterEvent, meterEventId);
512
520
  if (!preCheckEvent) {
513
521
  logger.warn('Skipping credit consumption job: MeterEvent not found', { meterEventId });
514
522
  return;
515
523
  }
524
+ assertJobObjectTenant(preCheckEvent);
516
525
  if (preCheckEvent.status === 'completed' || preCheckEvent.status === 'canceled') {
517
526
  logger.info('Skipping credit consumption job: MeterEvent already processed', {
518
527
  meterEventId,
@@ -554,8 +563,8 @@ export async function handleCreditConsumption(job: CreditConsumptionJob) {
554
563
 
555
564
  // Fresh loads inside lock for consistency
556
565
  const [freshEvent, freshSubscription] = await Promise.all([
557
- MeterEvent.findByPk(meterEventId),
558
- context._subscriptionId ? Subscription.findByPk(context._subscriptionId) : Promise.resolve(null),
566
+ systemFindByPk(MeterEvent, meterEventId),
567
+ context._subscriptionId ? systemFindByPk(Subscription, context._subscriptionId) : Promise.resolve(null),
559
568
  ]);
560
569
  if (!freshEvent) {
561
570
  logger.warn('MeterEvent disappeared after lock acquired', { meterEventId });
@@ -691,7 +700,7 @@ export async function handleCreditConsumption(job: CreditConsumptionJob) {
691
700
 
692
701
  // Handle retry logic with more robust error handling
693
702
  try {
694
- const meterEvent = await MeterEvent.findByPk(meterEventId);
703
+ const meterEvent = await systemFindByPk(MeterEvent, meterEventId);
695
704
  if (meterEvent && !['completed', 'canceled', 'requires_action'].includes(meterEvent.status)) {
696
705
  const attemptCount = meterEvent.attempt_count + 1;
697
706
  const nonRetryable = isNonRetryableCreditError(error);
@@ -762,9 +771,6 @@ export async function handleCreditConsumption(job: CreditConsumptionJob) {
762
771
  // ============================================================================
763
772
  // Batch credit consumption
764
773
  // ============================================================================
765
- const CREDIT_BATCH_SIZE = Math.max(1, parseInt(process.env.CREDIT_BATCH_SIZE || '50', 10));
766
- const CREDIT_BATCH_WINDOW_MS = Math.max(10, parseInt(process.env.CREDIT_BATCH_WINDOW_MS || '3000', 10));
767
-
768
774
  type PendingBatch = { eventIds: string[]; timer: NodeJS.Timeout | null };
769
775
  const pendingBatches = new Map<string, PendingBatch>();
770
776
  // Track in-flight batch jobs per key to avoid head-of-line blocking.
@@ -785,13 +791,13 @@ function addToBatch(customerId: string, eventName: string, meterEventId: string,
785
791
 
786
792
  batch.eventIds.push(meterEventId);
787
793
 
788
- if (batch.eventIds.length >= CREDIT_BATCH_SIZE) {
794
+ if (batch.eventIds.length >= creditBatchSize()) {
789
795
  flushBatch(key);
790
796
  return;
791
797
  }
792
798
 
793
799
  if (!batch.timer) {
794
- batch.timer = setTimeout(() => flushBatch(key), CREDIT_BATCH_WINDOW_MS);
800
+ batch.timer = setTimeout(() => flushBatch(key), creditBatchWindowMs());
795
801
  }
796
802
  }
797
803
 
@@ -864,7 +870,7 @@ async function handleBatchCreditConsumptionInner(meterEventIds: string[], batchS
864
870
  // ==========================================
865
871
  // Pre-check: filter already-processed events
866
872
  // ==========================================
867
- const preCheckEvents = await MeterEvent.findAll({
873
+ const preCheckEvents = await systemFindAll(MeterEvent, {
868
874
  where: { id: { [Op.in]: meterEventIds } },
869
875
  });
870
876
 
@@ -894,7 +900,7 @@ async function handleBatchCreditConsumptionInner(meterEventIds: string[], batchS
894
900
  // ==========================================
895
901
  const [meter, customer] = await Promise.all([
896
902
  getCachedMeterExpanded(eventName) as Promise<TMeterExpanded | null>,
897
- Customer.findByPk(customerId),
903
+ systemFindByPk(Customer, customerId),
898
904
  ]);
899
905
 
900
906
  if (!meter || !meter.currency_id || !customer) {
@@ -925,7 +931,7 @@ async function handleBatchCreditConsumptionInner(meterEventIds: string[], batchS
925
931
  // Fresh read inside lock
926
932
  // ==========================================
927
933
  const processableIds = processableEvents.map((e) => e.id);
928
- const freshEvents = await MeterEvent.findAll({
934
+ const freshEvents = await systemFindAll(MeterEvent, {
929
935
  where: { id: { [Op.in]: processableIds } },
930
936
  });
931
937
 
@@ -940,7 +946,7 @@ async function handleBatchCreditConsumptionInner(meterEventIds: string[], batchS
940
946
  let priceIds: string[] | undefined;
941
947
 
942
948
  if (subscriptionId) {
943
- subscription = await Subscription.findByPk(subscriptionId);
949
+ subscription = await systemFindByPk(Subscription, subscriptionId);
944
950
  if (!subscription) {
945
951
  logger.warn('Batch: Subscription not found inside lock, skipping', {
946
952
  customerId,
@@ -965,7 +971,7 @@ async function handleBatchCreditConsumptionInner(meterEventIds: string[], batchS
965
971
  // Batch idempotency check
966
972
  // ==========================================
967
973
  const allEventIds = freshProcessable.map((e) => e.id);
968
- const existingTransactions = await CreditTransaction.findAll({
974
+ const existingTransactions = await systemFindAll(CreditTransaction, {
969
975
  where: { source: { [Op.in]: allEventIds } },
970
976
  });
971
977
  const txBySource = new Map<string, CreditTransaction[]>();
@@ -1249,11 +1255,6 @@ async function handleBatchCreditConsumptionInner(meterEventIds: string[], batchS
1249
1255
  // Queue setup
1250
1256
  // ============================================================================
1251
1257
 
1252
- const creditQueueConcurrency = Math.max(
1253
- 1,
1254
- Math.min(20, parseInt(process.env.CREDIT_QUEUE_CONCURRENCY || '5', 10) || 5)
1255
- );
1256
-
1257
1258
  export const creditQueue = createQueue<CreditConsumptionJob | BatchCreditConsumptionJob>({
1258
1259
  name: 'credit-consumption',
1259
1260
  onJob: (job) => {
@@ -1263,7 +1264,7 @@ export const creditQueue = createQueue<CreditConsumptionJob | BatchCreditConsump
1263
1264
  return handleCreditConsumption(job as CreditConsumptionJob);
1264
1265
  },
1265
1266
  options: {
1266
- concurrency: creditQueueConcurrency,
1267
+ concurrency: creditQueueConcurrency(),
1267
1268
  maxRetries: 0,
1268
1269
  enableScheduledJob: true,
1269
1270
  },
@@ -1308,11 +1309,12 @@ const addCreditConsumptionJob = async (
1308
1309
  }
1309
1310
 
1310
1311
  if (!options.skipStatusCheck) {
1311
- const meterEvent = await MeterEvent.findByPk(meterEventId);
1312
+ const meterEvent = await systemFindByPk(MeterEvent, meterEventId);
1312
1313
  if (!meterEvent) {
1313
1314
  logger.warn('Cannot add credit consumption job: MeterEvent not found', { meterEventId });
1314
1315
  return;
1315
1316
  }
1317
+ assertJobObjectTenant(meterEvent);
1316
1318
 
1317
1319
  if (meterEvent.status === 'completed' || meterEvent.status === 'canceled') {
1318
1320
  logger.debug('Skipping credit consumption job: MeterEvent already processed', {
@@ -1342,7 +1344,7 @@ creditQueue.on('retry', ({ id, job }) => {
1342
1344
  });
1343
1345
 
1344
1346
  export async function startCreditConsumeQueue(): Promise<void> {
1345
- const lock = getLock('startCreditConsumeQueue');
1347
+ const lock = getLock('startCreditConsumeQueue', { scope: 'global' });
1346
1348
  if (lock.locked) {
1347
1349
  return;
1348
1350
  }
@@ -1359,7 +1361,7 @@ export async function startCreditConsumeQueue(): Promise<void> {
1359
1361
 
1360
1362
  do {
1361
1363
  // eslint-disable-next-line no-await-in-loop
1362
- batchEvents = await MeterEvent.findAll({
1364
+ batchEvents = await systemFindAll(MeterEvent, {
1363
1365
  where: {
1364
1366
  status: ['pending', 'requires_capture', 'processing'],
1365
1367
  },
@@ -1491,13 +1493,14 @@ events.on('customer.credit_grant.granted', async (creditGrant: CreditGrant) => {
1491
1493
  });
1492
1494
 
1493
1495
  async function retryFailedEventsForCustomer(creditGrant: CreditGrant): Promise<void> {
1494
- const grant = await CreditGrant.findByPk(creditGrant.id);
1496
+ const grant = await systemFindByPk(CreditGrant, creditGrant.id);
1495
1497
  if (!grant) {
1496
1498
  logger.error('Credit grant not found', {
1497
1499
  creditGrantId: creditGrant.id,
1498
1500
  });
1499
1501
  return;
1500
1502
  }
1503
+ assertJobObjectTenant(grant);
1501
1504
 
1502
1505
  const customerId = grant.customer_id;
1503
1506
  const currencyId = grant.currency_id;
@@ -1583,8 +1586,9 @@ async function retryFailedEventsForCustomer(creditGrant: CreditGrant): Promise<v
1583
1586
 
1584
1587
  let chunkIndex = 0;
1585
1588
  for (const [, subEventIds] of bySubscription) {
1586
- for (let i = 0; i < subEventIds.length; i += CREDIT_BATCH_SIZE) {
1587
- const chunk = subEventIds.slice(i, i + CREDIT_BATCH_SIZE);
1589
+ const batchSize = creditBatchSize();
1590
+ for (let i = 0; i < subEventIds.length; i += batchSize) {
1591
+ const chunk = subEventIds.slice(i, i + batchSize);
1588
1592
  creditQueue.push({
1589
1593
  id: `retry-batch-${customerId}-${Date.now()}-${chunkIndex++}`,
1590
1594
  job: { meterEventIds: chunk } as any,