payment-kit 1.29.1 → 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 (310) 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/migrations/0006_tenant_columns.sql +46 -0
  236. package/cloudflare/migrations/0007_tenant_backfill_indexes.sql +65 -0
  237. package/cloudflare/migrations/0008_schema_parity.sql +16 -0
  238. package/cloudflare/migrations/0009_remove_did_space_jobs.sql +5 -0
  239. package/cloudflare/queue-runtime-mode.ts +13 -0
  240. package/cloudflare/run-build.js +10 -56
  241. package/cloudflare/shims/blocklet-sdk/asset-host-transformer.ts +20 -0
  242. package/cloudflare/shims/blocklet-sdk/config.ts +8 -1
  243. package/cloudflare/shims/blocklet-sdk/login.ts +12 -0
  244. package/cloudflare/shims/blocklet-sdk/service-api.ts +14 -0
  245. package/cloudflare/shims/blocklet-sdk/session.ts +4 -2
  246. package/cloudflare/shims/blocklet-sdk/util-constants.ts +8 -0
  247. package/cloudflare/shims/blocklet-sdk/util-csrf.ts +13 -0
  248. package/cloudflare/shims/blocklet-sdk/util-wallet.ts +8 -0
  249. package/cloudflare/shims/cron.ts +38 -158
  250. package/cloudflare/shims/events.ts +124 -0
  251. package/cloudflare/shims/fastq.ts +15 -1
  252. package/cloudflare/shims/nedb-storage.ts +16 -8
  253. package/cloudflare/shims/xss.ts +8 -0
  254. package/cloudflare/tenant-middleware.ts +36 -0
  255. package/cloudflare/tests/tenant-middleware.spec.ts +160 -0
  256. package/cloudflare/tests/worker-handler-gate.spec.ts +44 -0
  257. package/cloudflare/worker.ts +204 -433
  258. package/cloudflare/wrangler.local-e2e.jsonc +26 -0
  259. package/jest.config.js +3 -1
  260. package/package.json +33 -38
  261. package/scripts/core-env-whitelist.json +1 -0
  262. package/scripts/e2e-12b-runtime.ts +149 -0
  263. package/scripts/e2e-core-config.ts +125 -0
  264. package/scripts/e2e-d1-tenancy.ts +116 -0
  265. package/scripts/e2e-d2-cron-queue.ts +139 -0
  266. package/scripts/e2e-d3-embedded-multi.ts +171 -0
  267. package/scripts/e2e-hono-s2.ts +125 -0
  268. package/scripts/e2e-hono-s3e.ts +135 -0
  269. package/scripts/e2e-hono-s4.ts +114 -0
  270. package/scripts/e2e-migration-contract.ts +100 -0
  271. package/scripts/e2e-s0.ts +61 -0
  272. package/scripts/e2e-s1.ts +107 -0
  273. package/scripts/e2e-s2.ts +178 -0
  274. package/scripts/e2e-s3.ts +110 -0
  275. package/scripts/e2e-s4.ts +191 -0
  276. package/scripts/e2e-s5.ts +139 -0
  277. package/scripts/e2e-s6.ts +127 -0
  278. package/scripts/e2e-tenant-model.ts +119 -0
  279. package/scripts/e2e-tenant-worker.ts +199 -0
  280. package/scripts/gen-sql-migrations.js +46 -0
  281. package/scripts/phase8-codemod.js +219 -0
  282. package/scripts/phase9a-env-getters-codemod.js +82 -0
  283. package/scripts/scan-core-env.js +109 -0
  284. package/scripts/scan-tenant-queries.js +235 -0
  285. package/scripts/schema-drift-guard.ts +210 -0
  286. package/scripts/tenant-scan-whitelist.json +1 -0
  287. package/src/env.d.ts +13 -1
  288. package/tsconfig.json +1 -1
  289. package/api/src/libs/did-space.ts +0 -235
  290. package/api/src/libs/middleware.ts +0 -50
  291. package/api/src/libs/security.ts +0 -192
  292. package/api/src/queues/space.ts +0 -662
  293. package/api/src/routes/credit-tokens.ts +0 -38
  294. package/api/src/routes/exchange-rates.ts +0 -87
  295. package/api/src/routes/index.ts +0 -142
  296. package/api/src/routes/integrations/stripe.ts +0 -61
  297. package/api/src/routes/meters.ts +0 -274
  298. package/api/src/routes/passports.ts +0 -68
  299. package/api/src/routes/redirect.ts +0 -20
  300. package/api/src/routes/tool.ts +0 -65
  301. package/api/src/routes/webhook-endpoints.ts +0 -126
  302. package/api/tests/routes/credit-grants.spec.ts +0 -1261
  303. package/cloudflare/shims/did-space-js.ts +0 -17
  304. package/cloudflare/shims/did-space.ts +0 -11
  305. package/cloudflare/shims/express-compat/index.ts +0 -80
  306. package/cloudflare/shims/express-compat/types.ts +0 -41
  307. package/cloudflare/shims/lock.ts +0 -115
  308. package/cloudflare/shims/queue.ts +0 -611
  309. package/cloudflare/tests/shims/queue-delayed-persist.spec.ts +0 -87
  310. package/cloudflare/tests/shims/queue-scheduled.spec.ts +0 -186
@@ -1,9 +1,10 @@
1
1
  import { BN, fromTokenToUnit, fromUnitToToken } from '@ocap/util';
2
+ import { systemFindByPk, systemFindOne } from '../store/scoped';
2
3
 
3
4
  import dayjs from '../libs/dayjs';
4
5
  import { getLock } from '../libs/lock';
5
6
  import logger from '../libs/logger';
6
- import createQueue from '../libs/queue';
7
+ import createQueue, { assertJobObjectTenant } from '../libs/queue';
7
8
  import { getPriceUintAmountByCurrency } from '../libs/price';
8
9
  import { Invoice, PaymentCurrency, Price, SubscriptionItem, TLineItemExpanded, UsageRecord } from '../store/models';
9
10
  import { Subscription } from '../store/models/subscription';
@@ -30,11 +31,12 @@ export async function handleUsageRecord(job: UsageRecordJob) {
30
31
  export const doHandleUsageRecord = async (job: UsageRecordJob) => {
31
32
  logger.info('handle usage record', job);
32
33
 
33
- const subscription = await Subscription.findByPk(job.subscriptionId);
34
+ const subscription = await systemFindByPk(Subscription, job.subscriptionId);
34
35
  if (!subscription) {
35
36
  logger.warn('Subscription not found', job);
36
37
  return;
37
38
  }
39
+ assertJobObjectTenant(subscription);
38
40
  if (subscription.isActive() === false) {
39
41
  logger.warn('Subscription not active, so usage check is skipped', job);
40
42
  return;
@@ -43,17 +45,18 @@ export const doHandleUsageRecord = async (job: UsageRecordJob) => {
43
45
  logger.warn('Subscription billing_threshold not set', job);
44
46
  return;
45
47
  }
46
- const currency = await PaymentCurrency.findByPk(subscription.currency_id);
48
+ const currency = await systemFindByPk(PaymentCurrency, subscription.currency_id);
47
49
  if (!currency) {
48
50
  logger.warn('Subscription currency not found', job);
49
51
  return;
50
52
  }
51
53
 
52
- const item = await SubscriptionItem.findByPk(job.subscriptionItemId);
54
+ const item = await systemFindByPk(SubscriptionItem, job.subscriptionItemId);
53
55
  if (!item) {
54
56
  logger.warn('SubscriptionItem not found', job);
55
57
  return;
56
58
  }
59
+ assertJobObjectTenant(item);
57
60
  // @ts-ignore
58
61
  const lines = await Price.expand([{ id: item.id, price_id: item.price_id, quantity: item.quantity }], {
59
62
  product: true,
@@ -69,7 +72,7 @@ export const doHandleUsageRecord = async (job: UsageRecordJob) => {
69
72
 
70
73
  const start = subscription.current_period_start as number;
71
74
  const end = subscription.current_period_end as number;
72
- const latestThresholdInvoice = await Invoice.findOne({
75
+ const latestThresholdInvoice = await systemFindOne(Invoice, {
73
76
  where: {
74
77
  subscription_id: subscription.id,
75
78
  billing_reason: 'subscription_threshold',
@@ -1,6 +1,6 @@
1
1
  import { events } from '../../libs/event';
2
2
  import logger from '../../libs/logger';
3
- import createQueue from '../../libs/queue';
3
+ import createQueue, { assertJobObjectTenant } from '../../libs/queue';
4
4
  import { Invoice } from '../../store/models';
5
5
  import { CheckoutSession } from '../../store/models/checkout-session';
6
6
  import { PaymentIntent } from '../../store/models/payment-intent';
@@ -8,6 +8,7 @@ import { Price } from '../../store/models/price';
8
8
  import { Product } from '../../store/models/product';
9
9
  import { depositVaultQueue } from '../payment';
10
10
  import { startVendorFulfillment, triggerCommissionProcess, triggerCoordinatorCheck } from './fulfillment-coordinator';
11
+ import { systemFindAll, systemFindByPk } from '../../store/scoped';
11
12
 
12
13
  type VendorCommissionJob = {
13
14
  invoiceId: string;
@@ -27,7 +28,7 @@ async function checkIfPaymentIntentHasVendors(checkoutSession: CheckoutSession):
27
28
  }
28
29
 
29
30
  // Find corresponding product_ids through price_ids
30
- const prices = await Price.findAll({
31
+ const prices = await systemFindAll(Price, {
31
32
  where: { id: priceIds },
32
33
  attributes: ['id', 'product_id'],
33
34
  });
@@ -43,7 +44,7 @@ async function checkIfPaymentIntentHasVendors(checkoutSession: CheckoutSession):
43
44
  }
44
45
 
45
46
  // Get product information
46
- const products = await Product.findAll({
47
+ const products = await systemFindAll(Product, {
47
48
  where: { id: productIds },
48
49
  attributes: ['id', 'vendor_config'],
49
50
  });
@@ -84,11 +85,12 @@ export const handleVendorCommission = async (job: VendorCommissionJob) => {
84
85
 
85
86
  let checkoutSession: CheckoutSession | null = null;
86
87
  try {
87
- const invoice = await Invoice.findByPk(job.invoiceId);
88
+ const invoice = await systemFindByPk(Invoice, job.invoiceId);
88
89
  if (!invoice) {
89
90
  logger.warn('invoice not found', { id: job.invoiceId });
90
91
  return;
91
92
  }
93
+ assertJobObjectTenant(invoice);
92
94
 
93
95
  // Find CheckoutSession through PaymentIntent
94
96
  checkoutSession = await CheckoutSession.findByInvoiceId(invoice.id);
@@ -148,7 +150,7 @@ export const vendorCommissionQueue = createQueue<VendorCommissionJob>({
148
150
  });
149
151
 
150
152
  export const startVendorCommissionQueue = async () => {
151
- const payments = await PaymentIntent.findAll({
153
+ const payments = await systemFindAll(PaymentIntent, {
152
154
  where: {
153
155
  status: ['requires_capture', 'processing'],
154
156
  capture_method: 'automatic',
@@ -3,7 +3,7 @@ import { Op } from 'sequelize';
3
3
  import { events } from '../../libs/event';
4
4
  import { getLock } from '../../libs/lock';
5
5
  import logger from '../../libs/logger';
6
- import createQueue from '../../libs/queue';
6
+ import createQueue, { assertJobObjectTenant } from '../../libs/queue';
7
7
  import dayjs from '../../libs/dayjs';
8
8
  import { VendorFulfillmentService } from '../../libs/vendor-util/fulfillment';
9
9
  import { CheckoutSession } from '../../store/models/checkout-session';
@@ -17,6 +17,7 @@ import { depositVaultQueue } from '../payment';
17
17
  import { addSubscriptionJob } from '../subscription';
18
18
  import { SubscriptionWillCanceledSchedule } from '../../crons/subscription-will-canceled';
19
19
  import { Invoice } from '../../store/models';
20
+ import { systemFindAll, systemFindByPk, systemFindOne } from '../../store/scoped';
20
21
 
21
22
  export type VendorInfo = NonNullable<CheckoutSession['vendor_info']>[number];
22
23
 
@@ -30,7 +31,7 @@ const MAX_FULFILLMENT_TIMEOUT = 300000;
30
31
 
31
32
  // Helper function to get order info for coordinated fulfillment
32
33
  async function getCoordinatedOrderInfo(checkoutSessionId: string) {
33
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
34
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId);
34
35
 
35
36
  if (!checkoutSession) {
36
37
  throw new Error('CheckoutSession or Invoice not found');
@@ -370,7 +371,7 @@ export async function handleFulfillmentCoordination(job: CoordinatorJob) {
370
371
  }
371
372
 
372
373
  async function getVendorConfigurations(checkoutSessionId: string): Promise<any[]> {
373
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
374
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId);
374
375
  if (!checkoutSession?.line_items) {
375
376
  return [];
376
377
  }
@@ -380,7 +381,7 @@ async function getVendorConfigurations(checkoutSessionId: string): Promise<any[]
380
381
  return [];
381
382
  }
382
383
 
383
- const prices = await Price.findAll({
384
+ const prices = await systemFindAll(Price, {
384
385
  where: { id: priceIds },
385
386
  attributes: ['id', 'product_id'],
386
387
  });
@@ -390,12 +391,12 @@ async function getVendorConfigurations(checkoutSessionId: string): Promise<any[]
390
391
  return [];
391
392
  }
392
393
 
393
- const product = await Product.findByPk(productIds[0]);
394
+ const product = await systemFindByPk(Product, productIds[0]);
394
395
  return product?.vendor_config || [];
395
396
  }
396
397
 
397
398
  async function getVendorInfo(checkoutSessionId: string): Promise<VendorInfo[]> {
398
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
399
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId);
399
400
  return (checkoutSession?.vendor_info as VendorInfo[]) || [];
400
401
  }
401
402
 
@@ -421,7 +422,7 @@ async function updateSingleVendorInfo(
421
422
 
422
423
  try {
423
424
  await sequelize.transaction(async (transaction: any) => {
424
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId, {
425
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId, {
425
426
  transaction,
426
427
  lock: transaction.LOCK.UPDATE,
427
428
  });
@@ -566,22 +567,24 @@ export function triggerCoordinatorCheck(checkoutSessionId: string, invoiceId: st
566
567
  export async function triggerCommissionProcess(checkoutSessionId: string, invoiceId: string): Promise<void> {
567
568
  logger.info('Triggering commission process', { checkoutSessionId, invoiceId });
568
569
 
569
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
570
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId);
570
571
  if (!checkoutSession) {
571
572
  logger.error('Checkout session not found[triggerCommissionProcess]', { checkoutSessionId });
572
573
  return;
573
574
  }
575
+ assertJobObjectTenant(checkoutSession);
574
576
 
575
577
  if (!invoiceId) {
576
578
  logger.warn('Invoice ID not found[triggerCommissionProcess]', { checkoutSessionId, invoiceId });
577
579
  }
578
580
 
579
- const invoice = await Invoice.findByPk(invoiceId || checkoutSession.invoice_id);
581
+ const invoice = await systemFindByPk(Invoice, invoiceId || checkoutSession.invoice_id);
580
582
  if (!invoice) {
581
583
  logger.error('Invoice not found[triggerCommissionProcess]', { invoiceId });
582
584
  return;
583
585
  }
584
- const paymentIntent = await PaymentIntent.findOne({
586
+ assertJobObjectTenant(invoice);
587
+ const paymentIntent = await systemFindOne(PaymentIntent, {
585
588
  where: {
586
589
  [Op.or]: [{ id: invoice.payment_intent_id || checkoutSession.payment_intent_id }, { invoice_id: invoiceId }],
587
590
  },
@@ -621,7 +624,7 @@ export async function initiateFullRefund(invoiceId: string, reason: string): Pro
621
624
  });
622
625
 
623
626
  try {
624
- const paymentIntent = await PaymentIntent.findOne({ where: { invoice_id: invoiceId } });
627
+ const paymentIntent = await systemFindOne(PaymentIntent, { where: { invoice_id: invoiceId } });
625
628
  const checkoutSession = await CheckoutSession.findByInvoiceId(invoiceId);
626
629
 
627
630
  if (!checkoutSession || !paymentIntent) {
@@ -644,7 +647,7 @@ export async function initiateFullRefund(invoiceId: string, reason: string): Pro
644
647
  }
645
648
 
646
649
  // Calculate remaining amount using the same logic as subscription createProration
647
- const refunds = await Refund.findAll({
650
+ const refunds = await systemFindAll(Refund, {
648
651
  where: {
649
652
  status: { [Op.not]: 'canceled' },
650
653
  payment_intent_id: paymentIntent.id,
@@ -721,11 +724,12 @@ export async function initiateFullRefund(invoiceId: string, reason: string): Pro
721
724
  */
722
725
  async function cancelSubscriptionForRefund(subscriptionId: string, reason: string): Promise<void> {
723
726
  try {
724
- const subscription = await Subscription.findByPk(subscriptionId);
727
+ const subscription = await systemFindByPk(Subscription, subscriptionId);
725
728
  if (!subscription) {
726
729
  logger.warn('Subscription not found for cancellation', { subscriptionId });
727
730
  return;
728
731
  }
732
+ assertJobObjectTenant(subscription);
729
733
 
730
734
  // Check if subscription is already canceled
731
735
  if (subscription.status === 'canceled') {
@@ -1,9 +1,10 @@
1
1
  import { events } from '../../libs/event';
2
2
  import logger from '../../libs/logger';
3
- import createQueue from '../../libs/queue';
3
+ import createQueue, { assertJobObjectTenant } from '../../libs/queue';
4
4
  import { VendorFulfillmentService } from '../../libs/vendor-util/fulfillment';
5
5
  import { CheckoutSession } from '../../store/models/checkout-session';
6
6
  import { updateVendorFulfillmentStatus } from './fulfillment-coordinator';
7
+ import { systemFindByPk } from '../../store/scoped';
7
8
 
8
9
  type VendorFulfillmentJob = {
9
10
  checkoutSessionId: string;
@@ -22,10 +23,11 @@ export const handleVendorFulfillment = async (job: VendorFulfillmentJob) => {
22
23
  const { checkoutSessionId, invoiceId, vendorId, vendorConfig } = job;
23
24
 
24
25
  try {
25
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
26
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId);
26
27
  if (!checkoutSession) {
27
28
  throw new Error(`CheckoutSession not found: ${checkoutSessionId}`);
28
29
  }
30
+ assertJobObjectTenant(checkoutSession);
29
31
 
30
32
  const orderInfo = {
31
33
  checkoutSessionId,
@@ -1,10 +1,11 @@
1
1
  import logger from '../../libs/logger';
2
- import createQueue from '../../libs/queue';
2
+ import createQueue, { assertJobObjectTenant } from '../../libs/queue';
3
3
  import { VendorFulfillmentService } from '../../libs/vendor-util/fulfillment';
4
4
  import { buildRefundInfoFromPayout } from '../../libs/vendor-util/tool';
5
5
  import { CheckoutSession, Payout } from '../../store/models';
6
6
  import { payoutQueue } from '../payout';
7
7
  import { VendorInfo } from './fulfillment-coordinator';
8
+ import { systemFindByPk, systemFindOne } from '../../store/scoped';
8
9
 
9
10
  export const MAX_RETURN_RETRY = 3;
10
11
 
@@ -30,7 +31,8 @@ async function handleReturnProcessorJob(job: ReturnProcessorJob): Promise<void>
30
31
  });
31
32
 
32
33
  try {
33
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
34
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId);
35
+ assertJobObjectTenant(checkoutSession);
34
36
 
35
37
  if (!checkoutSession) {
36
38
  logger.warn('CheckoutSession not found', { checkoutSessionId });
@@ -156,7 +158,7 @@ async function callVendorReturn(vendor: VendorInfo, checkoutSession: CheckoutSes
156
158
  throw new Error(`No adapter found for vendor: ${vendor.vendor_id}`);
157
159
  }
158
160
 
159
- const payoutInfo = await Payout.findOne({
161
+ const payoutInfo = await systemFindOne(Payout, {
160
162
  where: { vendor_info: { order_id: vendor.order_id, vendor_id: vendor.vendor_id } },
161
163
  });
162
164
  let refundInfo;
@@ -6,6 +6,7 @@ import { CheckoutSession, Subscription } from '../../store/models';
6
6
  import { vendorReturnProcessorQueue } from './return-processor';
7
7
  import { VendorInfo } from './fulfillment-coordinator';
8
8
  import { events } from '../../libs/event';
9
+ import { systemFindAll, systemFindOne } from '../../store/scoped';
9
10
 
10
11
  export const vendorReturnScannerQueue = createQueue({
11
12
  name: 'vendor-return-scanner',
@@ -56,7 +57,7 @@ async function findSessionsNeedingVendorReturn(): Promise<CheckoutSession[]> {
56
57
  // First, find canceled subscriptions
57
58
  const oneWeekAgo = dayjs().subtract(7, 'day').unix();
58
59
 
59
- const canceledSubscriptions = await Subscription.findAll({
60
+ const canceledSubscriptions = await systemFindAll(Subscription, {
60
61
  where: { status: 'canceled', canceled_at: { [Op.gt]: oneWeekAgo } },
61
62
  attributes: ['id'],
62
63
  });
@@ -64,7 +65,7 @@ async function findSessionsNeedingVendorReturn(): Promise<CheckoutSession[]> {
64
65
  const canceledSubscriptionIds = canceledSubscriptions.map((sub) => sub.id);
65
66
 
66
67
  // Find checkout sessions with completed fulfillment and canceled subscriptions
67
- const readyToReturnSessions = await CheckoutSession.findAll({
68
+ const readyToReturnSessions = await systemFindAll(CheckoutSession, {
68
69
  where: {
69
70
  fulfillment_status: { [Op.notIn]: ['returning', 'returned', 'failed'] },
70
71
  subscription_id: { [Op.in]: canceledSubscriptionIds },
@@ -75,7 +76,7 @@ async function findSessionsNeedingVendorReturn(): Promise<CheckoutSession[]> {
75
76
  });
76
77
 
77
78
  // Find checkout sessions already in returning status
78
- const returningSessions = await CheckoutSession.findAll({
79
+ const returningSessions = await systemFindAll(CheckoutSession, {
79
80
  where: {
80
81
  fulfillment_status: 'returning',
81
82
  subscription_id: { [Op.ne]: null as any },
@@ -135,7 +136,7 @@ events.on('customer.subscription.deleted', async (subscription: Subscription) =>
135
136
  return;
136
137
  }
137
138
 
138
- const session = await CheckoutSession.findOne({
139
+ const session = await systemFindOne(CheckoutSession, {
139
140
  where: { subscription_id: subscription.id },
140
141
  attributes: ['id', 'vendor_info'],
141
142
  });
@@ -1,15 +1,16 @@
1
1
  import { joinURL } from 'ufo';
2
2
  import { Auth as VendorAuth } from '@blocklet/payment-vendor';
3
- import createQueue from '../../libs/queue';
3
+ import createQueue, { assertJobObjectTenant } from '../../libs/queue';
4
4
  import { CheckoutSession } from '../../store/models/checkout-session';
5
5
  import { ProductVendor } from '../../store/models';
6
6
  import { fulfillmentCoordinatorQueue } from './fulfillment-coordinator';
7
7
  import logger from '../../libs/logger';
8
8
  import { vendorTimeoutMinutes } from '../../libs/env';
9
9
  import { VendorFulfillmentService } from '../../libs/vendor-util/fulfillment';
10
+ import { systemFindAll, systemFindByPk } from '../../store/scoped';
10
11
 
11
12
  export const startVendorStatusCheckSchedule = async () => {
12
- const checkoutSessions = await CheckoutSession.findAll({
13
+ const checkoutSessions = await systemFindAll(CheckoutSession, {
13
14
  where: { fulfillment_status: 'sent' },
14
15
  });
15
16
 
@@ -41,7 +42,8 @@ export const handleVendorStatusCheck = async (job: VendorStatusCheckJob) => {
41
42
  const { checkoutSessionId, vendorId } = job;
42
43
  logger.info('handleVendorStatusCheck', { checkoutSessionId, vendorId });
43
44
 
44
- const checkoutSession = await CheckoutSession.findByPk(checkoutSessionId);
45
+ const checkoutSession = await systemFindByPk(CheckoutSession, checkoutSessionId);
46
+ assertJobObjectTenant(checkoutSession);
45
47
 
46
48
  const vendor = checkoutSession?.vendor_info?.find((v) => v.vendor_id === vendorId);
47
49
  if (!vendor) {
@@ -49,7 +51,7 @@ export const handleVendorStatusCheck = async (job: VendorStatusCheckJob) => {
49
51
  return { status: 'fail' };
50
52
  }
51
53
 
52
- const TIMEOUT_THRESHOLD = vendorTimeoutMinutes * 60 * 1000;
54
+ const TIMEOUT_THRESHOLD = vendorTimeoutMinutes() * 60 * 1000;
53
55
  const now = Date.now();
54
56
 
55
57
  if (vendor.lastAttemptAt) {
@@ -61,11 +63,11 @@ export const handleVendorStatusCheck = async (job: VendorStatusCheckJob) => {
61
63
  vendorId,
62
64
  status: vendor.status,
63
65
  timeSinceLastAttempt: `${Math.floor(timeSinceLastAttempt / 1000)}s`,
64
- timeoutThreshold: `${vendorTimeoutMinutes}min (${Math.floor(TIMEOUT_THRESHOLD / 1000)}s)`,
66
+ timeoutThreshold: `${vendorTimeoutMinutes()}min (${Math.floor(TIMEOUT_THRESHOLD / 1000)}s)`,
65
67
  });
66
68
 
67
69
  vendor.status = 'failed';
68
- vendor.error_message = `Timeout: ${vendor.status} status exceeded ${vendorTimeoutMinutes} minutes`;
70
+ vendor.error_message = `Timeout: ${vendor.status} status exceeded ${vendorTimeoutMinutes()} minutes`;
69
71
  vendor.lastAttemptAt = new Date().toISOString();
70
72
 
71
73
  const updatedVendorInfo = checkoutSession?.vendor_info?.map((v) => {
@@ -92,7 +94,8 @@ export const handleVendorStatusCheck = async (job: VendorStatusCheckJob) => {
92
94
 
93
95
  try {
94
96
  if (!vendor.app_url) {
95
- const productVendor = await ProductVendor.findByPk(vendorId);
97
+ const productVendor = await systemFindByPk(ProductVendor, vendorId);
98
+ if (productVendor) assertJobObjectTenant(productVendor);
96
99
  const url = productVendor?.app_url;
97
100
  logger.info('found vendor url', { url, productVendor });
98
101
 
@@ -1,9 +1,12 @@
1
1
  import componentApi from '@blocklet/sdk/lib/util/component-api';
2
2
  import { AxiosError } from 'axios';
3
3
  import { Op } from 'sequelize';
4
+ import { systemFindAll, systemFindByPk } from '../store/scoped';
4
5
 
5
6
  import { wallet } from '../libs/auth';
7
+ import { context } from '../libs/context';
6
8
  import logger from '../libs/logger';
9
+ import { TENANT_MISMATCH, resolveRowTenant } from '../libs/tenant';
7
10
  import createQueue from '../libs/queue';
8
11
  import { MAX_RETRY_COUNT, getNextRetry, getWebhookJobId } from '../libs/util';
9
12
  import { webhookAlertWindowMinutes, webhookAlertMinFailures } from '../libs/env';
@@ -46,13 +49,13 @@ type WebhookJob = {
46
49
  export const handleWebhook = async (job: WebhookJob) => {
47
50
  logger.info('handle webhook', job);
48
51
 
49
- const event = await Event.findByPk(job.eventId);
52
+ const event = await systemFindByPk(Event, job.eventId);
50
53
  if (!event) {
51
54
  logger.warn('event not found when attempt webhook', job);
52
55
  return;
53
56
  }
54
57
 
55
- const webhook = await WebhookEndpoint.findByPk(job.webhookId);
58
+ const webhook = await systemFindByPk(WebhookEndpoint, job.webhookId);
56
59
  if (!webhook) {
57
60
  logger.warn('webhook not found on attempt', job);
58
61
  return;
@@ -62,9 +65,28 @@ export const handleWebhook = async (job: WebhookJob) => {
62
65
  return;
63
66
  }
64
67
 
65
- const lastRetryCount = await WebhookAttempt.max('retry_count', {
66
- where: { event_id: event.id, webhook_endpoint_id: webhook.id },
67
- });
68
+ // Phase 4 (W1-3) handler invariant: a forged/mixed job payload must never
69
+ // deliver one tenant's event to another tenant's endpoint. No attempt row
70
+ // is written for rejected pairs (alert only).
71
+ const eventTenant = resolveRowTenant(event);
72
+ if (resolveRowTenant(webhook) !== eventTenant) {
73
+ logger.error('[webhook] tenant mismatch between event and endpoint, delivery refused', {
74
+ code: TENANT_MISMATCH,
75
+ eventId: event.id,
76
+ webhookId: webhook.id,
77
+ });
78
+ return;
79
+ }
80
+
81
+ // The webhook job is tenant-agnostic at dispatch (it only carries ids); the
82
+ // handler derives the authoritative tenant from the loaded event. Run the
83
+ // tenant-scoped reads/writes under that tenant so TenantModel scopes/stamps
84
+ // consistently (the cross-tenant loads above used system* on purpose).
85
+ const lastRetryCount = await context.withTenant(eventTenant, () =>
86
+ WebhookAttempt.max('retry_count', {
87
+ where: { event_id: event.id, webhook_endpoint_id: webhook.id },
88
+ })
89
+ );
68
90
 
69
91
  const retryCount = lastRetryCount ? +lastRetryCount + 1 : 1;
70
92
 
@@ -74,10 +96,10 @@ export const handleWebhook = async (job: WebhookJob) => {
74
96
  // expand basic fields
75
97
  const { object } = json.data;
76
98
  if (object.customer_id && !object.customer) {
77
- object.customer = await Customer.findByPk(object.customer_id);
99
+ object.customer = await systemFindByPk(Customer, object.customer_id);
78
100
  }
79
101
  if (object.currency_id && !object.currency) {
80
- object.currency = await PaymentCurrency.findByPk(object.currency_id);
102
+ object.currency = await systemFindByPk(PaymentCurrency, object.currency_id);
81
103
  }
82
104
 
83
105
  // verify similar to component call, but supports external urls
@@ -92,33 +114,39 @@ export const handleWebhook = async (job: WebhookJob) => {
92
114
  },
93
115
  });
94
116
 
95
- await WebhookAttempt.create({
96
- livemode: event.livemode,
97
- event_id: event.id,
98
- webhook_endpoint_id: webhook.id,
99
- status: 'succeeded',
100
- response_status: result.status,
101
- response_body: result.data || {},
102
- retry_count: retryCount,
103
- });
117
+ await context.withTenant(eventTenant, () =>
118
+ WebhookAttempt.create({
119
+ livemode: event.livemode,
120
+ instance_did: eventTenant,
121
+ event_id: event.id,
122
+ webhook_endpoint_id: webhook.id,
123
+ status: 'succeeded',
124
+ response_status: result.status,
125
+ response_body: result.data || {},
126
+ retry_count: retryCount,
127
+ })
128
+ );
104
129
  logger.info('WebhookAttempt created successfully', { eventId: event.id, webhookId: webhook.id });
105
130
 
106
- await decrementPendingWebhooks(event, webhook.id);
131
+ await context.withTenant(eventTenant, () => decrementPendingWebhooks(event, webhook.id));
107
132
 
108
133
  logger.info('webhook attempt success', { ...job, retryCount });
109
134
  } catch (err: any) {
110
135
  logger.warn('webhook attempt error', { ...job, retryCount, message: err.message });
111
136
  const errorStatus = (err as AxiosError).response?.status || 500;
112
137
 
113
- await WebhookAttempt.create({
114
- livemode: event.livemode,
115
- event_id: event.id,
116
- webhook_endpoint_id: webhook.id,
117
- status: 'failed',
118
- response_status: errorStatus,
119
- response_body: (err as AxiosError).response?.data || {},
120
- retry_count: retryCount,
121
- });
138
+ await context.withTenant(eventTenant, () =>
139
+ WebhookAttempt.create({
140
+ livemode: event.livemode,
141
+ instance_did: eventTenant,
142
+ event_id: event.id,
143
+ webhook_endpoint_id: webhook.id,
144
+ status: 'failed',
145
+ response_status: errorStatus,
146
+ response_body: (err as AxiosError).response?.data || {},
147
+ retry_count: retryCount,
148
+ })
149
+ );
122
150
  logger.info('Failed WebhookAttempt created', { eventId: event.id, webhookId: webhook.id });
123
151
 
124
152
  try {
@@ -143,16 +171,16 @@ export const handleWebhook = async (job: WebhookJob) => {
143
171
  });
144
172
  });
145
173
  } else {
146
- await decrementPendingWebhooks(event, webhook.id);
174
+ await context.withTenant(eventTenant, () => decrementPendingWebhooks(event, webhook.id));
147
175
  }
148
176
  }
149
177
  };
150
178
 
151
179
  // Alert if webhook continuously fails within configured time window
152
180
  async function checkAndNotifyWebhookFailures(webhookId: string) {
153
- const alertWindowStart = new Date(Date.now() - webhookAlertWindowMinutes * 60 * 1000);
181
+ const alertWindowStart = new Date(Date.now() - webhookAlertWindowMinutes() * 60 * 1000);
154
182
 
155
- const recentAttempts = await WebhookAttempt.findAll({
183
+ const recentAttempts = await systemFindAll(WebhookAttempt, {
156
184
  where: {
157
185
  webhook_endpoint_id: webhookId,
158
186
  created_at: { [Op.gte]: alertWindowStart },
@@ -167,7 +195,7 @@ async function checkAndNotifyWebhookFailures(webhookId: string) {
167
195
  }
168
196
 
169
197
  const failedCountInWindow = recentAttempts.filter((attempt) => attempt.status === 'failed').length;
170
- if (failedCountInWindow < webhookAlertMinFailures) {
198
+ if (failedCountInWindow < webhookAlertMinFailures()) {
171
199
  return;
172
200
  }
173
201
 
@@ -185,8 +213,8 @@ async function checkAndNotifyWebhookFailures(webhookId: string) {
185
213
  logger.info('Notification job added for webhook consecutive failures', {
186
214
  webhookId,
187
215
  failedCountInWindow,
188
- alertWindowMinutes: webhookAlertWindowMinutes,
189
- minFailures: webhookAlertMinFailures,
216
+ alertWindowMinutes: webhookAlertWindowMinutes(),
217
+ minFailures: webhookAlertMinFailures(),
190
218
  });
191
219
  }
192
220
 
@@ -5,7 +5,6 @@ import { toDelegateAddress } from '@arcblock/did-util/cbor';
5
5
  import type { DelegateState as DelegateStateType, Transaction } from '@ocap/client';
6
6
  import { BN, fromTokenToUnit, toBase58 } from '@ocap/util';
7
7
  import { fromPublicKey } from '@ocap/wallet';
8
- import type { Request } from 'express';
9
8
  import isEmpty from 'lodash/isEmpty';
10
9
  import dayjs from '../../libs/dayjs';
11
10
  import { estimateMaxGasForTx, hasStakedForGas } from '../../integrations/arcblock/stake';
@@ -1583,7 +1582,7 @@ export async function executeOcapTransactions(
1583
1582
  userPk: string,
1584
1583
  claims: any[],
1585
1584
  paymentMethod: PaymentMethod,
1586
- requestSource: Request | Request[] | any[],
1585
+ requestSource: any | any[],
1587
1586
  subscriptionId?: string,
1588
1587
  paymentCurrencyContract?: string,
1589
1588
  nonce?: string
@@ -29,7 +29,7 @@ const updateInvoices = async (invoices: Invoice[], update: Partial<Invoice>) =>
29
29
  invoices.map((invoice) => async () => {
30
30
  await invoice.update(update);
31
31
  }),
32
- { concurrency: updateDataConcurrency }
32
+ { concurrency: updateDataConcurrency() }
33
33
  );
34
34
  };
35
35
 
@@ -38,7 +38,7 @@ const updateSubscriptions = async (subscriptions: Subscription[], update: Partia
38
38
  subscriptions.map((subscription) => async () => {
39
39
  await subscription.update(update);
40
40
  }),
41
- { concurrency: updateDataConcurrency }
41
+ { concurrency: updateDataConcurrency() }
42
42
  );
43
43
  };
44
44
 
@@ -254,7 +254,7 @@ export default {
254
254
  subscriptions.map((subscription) => async () => {
255
255
  await addSubscriptionJob(subscription, 'cycle', false, subscription.trial_end);
256
256
  }),
257
- { concurrency: updateDataConcurrency }
257
+ { concurrency: updateDataConcurrency() }
258
258
  );
259
259
 
260
260
  logger.info('CheckoutSession updated with multiple subscriptions', {