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
@@ -1,6 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
- import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
2
+ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes } from 'sequelize';
3
3
  import type { LiteralUnion } from 'type-fest';
4
+ import { TenantModel } from '../tenant-model';
5
+ import { getInstanceDid } from '../../libs/context';
4
6
 
5
7
  import { createIdGenerator } from '../../libs/util';
6
8
 
@@ -10,9 +12,13 @@ export const nextEntitlementGrantId = createIdGenerator('eg', 24);
10
12
  // Multiple grants for the same (customer, entitlement) can coexist across channels;
11
13
  // the "currently effective" grant is determined by status + active_until.
12
14
  // eslint-disable-next-line prettier/prettier
13
- export class EntitlementGrant extends Model<InferAttributes<EntitlementGrant>, InferCreationAttributes<EntitlementGrant>> {
15
+ export class EntitlementGrant extends TenantModel<
16
+ InferAttributes<EntitlementGrant>,
17
+ InferCreationAttributes<EntitlementGrant>
18
+ > {
14
19
  declare id: CreationOptional<string>;
15
20
  declare livemode: boolean;
21
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
16
22
 
17
23
  declare entitlement_id: string;
18
24
  declare customer_id: string;
@@ -90,13 +96,19 @@ export class EntitlementGrant extends Model<InferAttributes<EntitlementGrant>, I
90
96
  };
91
97
 
92
98
  public static initialize(sequelize: any) {
93
- this.init(EntitlementGrant.GENESIS_ATTRIBUTES, {
94
- sequelize,
95
- modelName: 'EntitlementGrant',
96
- tableName: 'entitlement_grants',
97
- createdAt: 'created_at',
98
- updatedAt: 'updated_at',
99
- });
99
+ this.init(
100
+ {
101
+ ...EntitlementGrant.GENESIS_ATTRIBUTES,
102
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
103
+ },
104
+ {
105
+ sequelize,
106
+ modelName: 'EntitlementGrant',
107
+ tableName: 'entitlement_grants',
108
+ createdAt: 'created_at',
109
+ updatedAt: 'updated_at',
110
+ }
111
+ );
100
112
  }
101
113
 
102
114
  public static associate(models: any) {
@@ -1,11 +1,17 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
- import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
2
+ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes } from 'sequelize';
3
+ import { TenantModel } from '../tenant-model';
4
+ import { getInstanceDid } from '../../libs/context';
3
5
 
4
6
  // Join table: which products unlock which entitlements (many-to-many)
5
7
  // eslint-disable-next-line prettier/prettier
6
- export class EntitlementProduct extends Model<InferAttributes<EntitlementProduct>, InferCreationAttributes<EntitlementProduct>> {
8
+ export class EntitlementProduct extends TenantModel<
9
+ InferAttributes<EntitlementProduct>,
10
+ InferCreationAttributes<EntitlementProduct>
11
+ > {
7
12
  declare entitlement_id: string;
8
13
  declare product_id: string;
14
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
9
15
  declare created_at: CreationOptional<Date>;
10
16
  declare updated_at: CreationOptional<Date>;
11
17
 
@@ -33,13 +39,19 @@ export class EntitlementProduct extends Model<InferAttributes<EntitlementProduct
33
39
  };
34
40
 
35
41
  public static initialize(sequelize: any) {
36
- this.init(EntitlementProduct.GENESIS_ATTRIBUTES, {
37
- sequelize,
38
- modelName: 'EntitlementProduct',
39
- tableName: 'entitlement_products',
40
- createdAt: 'created_at',
41
- updatedAt: 'updated_at',
42
- });
42
+ this.init(
43
+ {
44
+ ...EntitlementProduct.GENESIS_ATTRIBUTES,
45
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
46
+ },
47
+ {
48
+ sequelize,
49
+ modelName: 'EntitlementProduct',
50
+ tableName: 'entitlement_products',
51
+ createdAt: 'created_at',
52
+ updatedAt: 'updated_at',
53
+ }
54
+ );
43
55
  }
44
56
 
45
57
  public static associate() {}
@@ -1,14 +1,17 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
- import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
2
+ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes } from 'sequelize';
3
+ import { TenantModel } from '../tenant-model';
4
+ import { getInstanceDid } from '../../libs/context';
3
5
 
4
6
  import { createIdGenerator } from '../../libs/util';
5
7
 
6
8
  export const nextEntitlementId = createIdGenerator('ent', 24);
7
9
 
8
10
  // eslint-disable-next-line prettier/prettier
9
- export class Entitlement extends Model<InferAttributes<Entitlement>, InferCreationAttributes<Entitlement>> {
11
+ export class Entitlement extends TenantModel<InferAttributes<Entitlement>, InferCreationAttributes<Entitlement>> {
10
12
  declare id: CreationOptional<string>;
11
13
  declare livemode: boolean;
14
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
12
15
 
13
16
  declare key: string;
14
17
  declare name?: string;
@@ -33,7 +36,7 @@ export class Entitlement extends Model<InferAttributes<Entitlement>, InferCreati
33
36
  key: {
34
37
  type: DataTypes.STRING(64),
35
38
  allowNull: false,
36
- unique: true,
39
+ // uniqueness is per tenant since Phase 2: see uq_* composite indexes in tenant-backfill.ts
37
40
  },
38
41
  name: {
39
42
  type: DataTypes.STRING(255),
@@ -60,13 +63,19 @@ export class Entitlement extends Model<InferAttributes<Entitlement>, InferCreati
60
63
  };
61
64
 
62
65
  public static initialize(sequelize: any) {
63
- this.init(Entitlement.GENESIS_ATTRIBUTES, {
64
- sequelize,
65
- modelName: 'Entitlement',
66
- tableName: 'entitlements',
67
- createdAt: 'created_at',
68
- updatedAt: 'updated_at',
69
- });
66
+ this.init(
67
+ {
68
+ ...Entitlement.GENESIS_ATTRIBUTES,
69
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
70
+ },
71
+ {
72
+ sequelize,
73
+ modelName: 'Entitlement',
74
+ tableName: 'entitlements',
75
+ createdAt: 'created_at',
76
+ updatedAt: 'updated_at',
77
+ }
78
+ );
70
79
  }
71
80
 
72
81
  public static associate(models: any) {
@@ -1,5 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
- import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
2
+ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes } from 'sequelize';
3
+ import { TenantModel } from '../tenant-model';
4
+ import { getInstanceDid } from '../../libs/context';
3
5
 
4
6
  import { createIdGenerator } from '../../libs/util';
5
7
  import type { EventType } from './types';
@@ -7,10 +9,11 @@ import type { EventType } from './types';
7
9
  const nextId = createIdGenerator('evt', 24);
8
10
 
9
11
  // eslint-disable-next-line prettier/prettier
10
- export class Event extends Model<InferAttributes<Event>, InferCreationAttributes<Event>> {
12
+ export class Event extends TenantModel<InferAttributes<Event>, InferCreationAttributes<Event>> {
11
13
  declare id: CreationOptional<string>;
12
14
 
13
15
  declare livemode: boolean;
16
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
14
17
 
15
18
  declare type: EventType;
16
19
  declare api_version: string;
@@ -91,13 +94,19 @@ export class Event extends Model<InferAttributes<Event>, InferCreationAttributes
91
94
  };
92
95
 
93
96
  public static initialize(sequelize: any) {
94
- this.init(Event.GENESIS_ATTRIBUTES, {
95
- sequelize,
96
- modelName: 'Event',
97
- tableName: 'events',
98
- createdAt: 'created_at',
99
- updatedAt: 'updated_at',
100
- });
97
+ this.init(
98
+ {
99
+ ...Event.GENESIS_ATTRIBUTES,
100
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
101
+ },
102
+ {
103
+ sequelize,
104
+ modelName: 'Event',
105
+ tableName: 'events',
106
+ createdAt: 'created_at',
107
+ updatedAt: 'updated_at',
108
+ }
109
+ );
101
110
  }
102
111
 
103
112
  public static associate(models: any) {
@@ -1,9 +1,16 @@
1
+ // Phase 11 (W2-3) carve-out: exchange_rate_providers is the platform-global
2
+ // table (W1 §2.2 D2 exemption — exchange rates are a global fact, no instance_did
3
+ // column). Its provider API keys are platform-wide config, NOT per-tenant, so it
4
+ // deliberately uses the platform process key (@blocklet/sdk security) rather than
5
+ // the per-tenant secrets keyring — routing it through the tenant facade would
6
+ // encrypt/decrypt the shared config under a different key per request and break
7
+ // it. Phase 12's shim relocation must preserve this platform-key path.
1
8
  import security from '@blocklet/sdk/lib/security';
2
9
  import cloneDeep from 'lodash/cloneDeep';
3
10
  import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model, Op } from 'sequelize';
4
11
  import type { LiteralUnion } from 'type-fest';
5
12
 
6
- import { createEvent } from '../../libs/audit';
13
+ import { createEvent, reportAuditFailure } from '../../libs/audit';
7
14
  import { createIdGenerator } from '../../libs/util';
8
15
 
9
16
  export const nextExchangeRateProviderId = createIdGenerator('erp', 24);
@@ -100,13 +107,19 @@ export class ExchangeRateProvider extends Model<
100
107
  updatedAt: 'updated_at',
101
108
  hooks: {
102
109
  afterCreate: (model: ExchangeRateProvider, options) => {
103
- createEvent('ExchangeRateProvider', 'exchange_rate_provider.created', model, options).catch(console.error);
110
+ createEvent('ExchangeRateProvider', 'exchange_rate_provider.created', model, options).catch(
111
+ reportAuditFailure
112
+ );
104
113
  },
105
114
  afterUpdate: (model: ExchangeRateProvider, options) => {
106
- createEvent('ExchangeRateProvider', 'exchange_rate_provider.updated', model, options).catch(console.error);
115
+ createEvent('ExchangeRateProvider', 'exchange_rate_provider.updated', model, options).catch(
116
+ reportAuditFailure
117
+ );
107
118
  },
108
119
  afterDestroy: (model: ExchangeRateProvider, options) => {
109
- createEvent('ExchangeRateProvider', 'exchange_rate_provider.deleted', model, options).catch(console.error);
120
+ createEvent('ExchangeRateProvider', 'exchange_rate_provider.deleted', model, options).catch(
121
+ reportAuditFailure
122
+ );
110
123
  },
111
124
  },
112
125
  }
@@ -1,6 +1,8 @@
1
1
  /* eslint-disable @typescript-eslint/indent */
2
2
  /* eslint-disable @typescript-eslint/lines-between-class-members */
3
- import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
3
+ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes } from 'sequelize';
4
+ import { TenantModel } from '../tenant-model';
5
+ import { getInstanceDid } from '../../libs/context';
4
6
 
5
7
  import { createIdGenerator } from '../../libs/util';
6
8
  import type { DiscountAmount } from './types';
@@ -13,9 +15,10 @@ export type InvoiceLineItem = {
13
15
 
14
16
  // @link https://stripe.com/docs/api/invoiceitems
15
17
  // eslint-disable-next-line prettier/prettier
16
- export class InvoiceItem extends Model<InferAttributes<InvoiceItem>, InferCreationAttributes<InvoiceItem>> {
18
+ export class InvoiceItem extends TenantModel<InferAttributes<InvoiceItem>, InferCreationAttributes<InvoiceItem>> {
17
19
  declare id: CreationOptional<string>;
18
20
  declare livemode: boolean;
21
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
19
22
 
20
23
  declare amount: string;
21
24
  declare quantity: number;
@@ -149,13 +152,19 @@ export class InvoiceItem extends Model<InferAttributes<InvoiceItem>, InferCreati
149
152
  };
150
153
 
151
154
  public static initialize(sequelize: any) {
152
- this.init(InvoiceItem.GENESIS_ATTRIBUTES, {
153
- sequelize,
154
- modelName: 'InvoiceItem',
155
- tableName: 'invoice_items',
156
- createdAt: 'created_at',
157
- updatedAt: 'updated_at',
158
- });
155
+ this.init(
156
+ {
157
+ ...InvoiceItem.GENESIS_ATTRIBUTES,
158
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
159
+ },
160
+ {
161
+ sequelize,
162
+ modelName: 'InvoiceItem',
163
+ tableName: 'invoice_items',
164
+ createdAt: 'created_at',
165
+ updatedAt: 'updated_at',
166
+ }
167
+ );
159
168
  }
160
169
 
161
170
  public static associate(models: any) {
@@ -6,14 +6,15 @@ import {
6
6
  DataTypes,
7
7
  InferAttributes,
8
8
  InferCreationAttributes,
9
- Model,
10
9
  Op,
11
10
  Sequelize,
12
11
  WhereOptions,
13
12
  } from 'sequelize';
14
13
  import type { LiteralUnion } from 'type-fest';
14
+ import { TenantModel } from '../tenant-model';
15
+ import { getInstanceDid } from '../../libs/context';
15
16
 
16
- import { createEvent, createStatusEvent } from '../../libs/audit';
17
+ import { createEvent, createStatusEvent, reportAuditFailure } from '../../libs/audit';
17
18
  import { createIdGenerator } from '../../libs/util';
18
19
  import type {
19
20
  CustomerAddress,
@@ -29,13 +30,14 @@ import type {
29
30
  export const nextInvoiceId = createIdGenerator('in', 24);
30
31
 
31
32
  // eslint-disable-next-line prettier/prettier
32
- export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttributes<Invoice>> {
33
+ export class Invoice extends TenantModel<InferAttributes<Invoice>, InferCreationAttributes<Invoice>> {
33
34
  declare id: CreationOptional<string>;
34
35
 
35
36
  // A unique, identifying string that appears on emails sent to the customer for this invoice
36
37
  declare number: string;
37
38
 
38
39
  declare livemode: boolean;
40
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
39
41
  declare description?: string;
40
42
  declare statement_descriptor: string;
41
43
  declare period_end: number;
@@ -479,6 +481,12 @@ export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttrib
479
481
  this.init(
480
482
  {
481
483
  ...Invoice.GENESIS_ATTRIBUTES,
484
+ instance_did: {
485
+ type: DataTypes.STRING(64),
486
+ allowNull: true,
487
+ // bare creates must still land in the active tenant (single mode = app DID)
488
+ defaultValue: () => getInstanceDid(),
489
+ },
482
490
  starting_token_balance: {
483
491
  type: DataTypes.JSON,
484
492
  defaultValue: {},
@@ -496,23 +504,23 @@ export class Invoice extends Model<InferAttributes<Invoice>, InferCreationAttrib
496
504
  updatedAt: 'updated_at',
497
505
  hooks: {
498
506
  afterCreate: (model: Invoice, options) => {
499
- createEvent('Invoice', 'invoice.created', model, options).catch(console.error);
507
+ createEvent('Invoice', 'invoice.created', model, options).catch(reportAuditFailure);
500
508
  if (model.status === 'paid') {
501
- createEvent('Invoice', 'invoice.paid', model, options).catch(console.error);
509
+ createEvent('Invoice', 'invoice.paid', model, options).catch(reportAuditFailure);
502
510
  }
503
511
  },
504
512
  afterUpdate: (model: Invoice, options) => {
505
- createEvent('Invoice', 'invoice.updated', model, options).catch(console.error);
513
+ createEvent('Invoice', 'invoice.updated', model, options).catch(reportAuditFailure);
506
514
  createStatusEvent(
507
515
  'Invoice',
508
516
  'invoice',
509
517
  { open: 'finalized', void: 'voided', paid: 'paid', uncollectible: 'marked_uncollectible' },
510
518
  model,
511
519
  options
512
- ).catch(console.error);
520
+ ).catch(reportAuditFailure);
513
521
  },
514
522
  afterDestroy: (model: Invoice, options) => {
515
- createEvent('Invoice', 'invoice.deleted', model, options).catch(console.error);
523
+ createEvent('Invoice', 'invoice.deleted', model, options).catch(reportAuditFailure);
516
524
  },
517
525
  },
518
526
  }
@@ -4,7 +4,6 @@ import {
4
4
  DataTypes,
5
5
  InferAttributes,
6
6
  InferCreationAttributes,
7
- Model,
8
7
  Op,
9
8
  QueryTypes,
10
9
  Sequelize,
@@ -13,7 +12,9 @@ import {
13
12
  import type { LiteralUnion } from 'type-fest';
14
13
 
15
14
  import { BN } from '@ocap/util';
16
- import { createEvent } from '../../libs/audit';
15
+ import { TenantModel } from '../tenant-model';
16
+ import { getInstanceDid } from '../../libs/context';
17
+ import { createEvent, reportAuditFailure } from '../../libs/audit';
17
18
  import { createIdGenerator } from '../../libs/util';
18
19
  import { GroupedBN, GroupedStrList, MeterEventPayload, MeterEventStatus, SourceData } from './types';
19
20
  import { Customer } from './customer';
@@ -30,13 +31,14 @@ type TMeterEventExpanded = MeterEvent & {
30
31
  paymentCurrency: TPaymentCurrency;
31
32
  };
32
33
 
33
- export class MeterEvent extends Model<InferAttributes<MeterEvent>, InferCreationAttributes<MeterEvent>> {
34
+ export class MeterEvent extends TenantModel<InferAttributes<MeterEvent>, InferCreationAttributes<MeterEvent>> {
34
35
  declare id: CreationOptional<string>;
35
36
  declare event_name: string;
36
37
  declare payload: MeterEventPayload;
37
38
  declare identifier: string; // 防重复的唯一标识
38
39
  declare timestamp: number;
39
40
  declare livemode: boolean;
41
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
40
42
  declare status: MeterEventStatus;
41
43
  declare processed_at?: number; // 处理时间
42
44
  declare attempt_count: number; // 重试次数
@@ -85,7 +87,7 @@ export class MeterEvent extends Model<InferAttributes<MeterEvent>, InferCreation
85
87
  identifier: {
86
88
  type: DataTypes.STRING(255),
87
89
  allowNull: false,
88
- unique: true, // 确保幂等性
90
+ // uniqueness is per tenant since Phase 2: see uq_* composite indexes in tenant-backfill.ts
89
91
  },
90
92
  livemode: {
91
93
  type: DataTypes.BOOLEAN,
@@ -220,6 +222,12 @@ export class MeterEvent extends Model<InferAttributes<MeterEvent>, InferCreation
220
222
  this.init(
221
223
  {
222
224
  ...this.GENESIS_ATTRIBUTES,
225
+ instance_did: {
226
+ type: DataTypes.STRING(64),
227
+ allowNull: true,
228
+ // bare creates must still land in the active tenant (single mode = app DID)
229
+ defaultValue: () => getInstanceDid(),
230
+ },
223
231
  source_data: {
224
232
  type: DataTypes.JSON,
225
233
  allowNull: true,
@@ -231,10 +239,16 @@ export class MeterEvent extends Model<InferAttributes<MeterEvent>, InferCreation
231
239
  tableName: 'meter_events',
232
240
  createdAt: 'created_at',
233
241
  updatedAt: 'updated_at',
234
- indexes: [{ fields: ['identifier'], unique: true }, { fields: ['status'] }, { fields: ['event_name'] }],
242
+ // identifier uniqueness is per tenant since Phase 2 (uq_meter_events_tenant_identifier);
243
+ // these model-level indexes only apply on sync(), which production never calls
244
+ indexes: [
245
+ { fields: ['instance_did', 'identifier'], unique: true },
246
+ { fields: ['status'] },
247
+ { fields: ['event_name'] },
248
+ ],
235
249
  hooks: {
236
250
  afterCreate: (model: MeterEvent, options) => {
237
- createEvent('MeterEvent', 'billing.meter_event.created', model, options).catch(console.error);
251
+ createEvent('MeterEvent', 'billing.meter_event.created', model, options).catch(reportAuditFailure);
238
252
  },
239
253
  },
240
254
  }
@@ -294,16 +308,20 @@ export class MeterEvent extends Model<InferAttributes<MeterEvent>, InferCreation
294
308
  const [totalEvents, processedEvents, totalValue] = await Promise.all([
295
309
  this.count({ where: whereClause }),
296
310
  this.count({ where: { ...whereClause, status: 'completed' } }),
311
+ // 洞 G (Phase 4): raw read on a tenant table — guard with instance_did so
312
+ // the aggregate never crosses tenants (the sibling count() calls above are
313
+ // already scoped by TenantModel; this keeps the raw SUM consistent).
297
314
  // @ts-ignore
298
315
  this.sequelize.query(
299
- `SELECT COALESCE(SUM(CAST(payload->>'value' AS DECIMAL)), 0) as total
300
- FROM meter_events
301
- WHERE event_name = :eventName
316
+ `SELECT COALESCE(SUM(CAST(payload->>'value' AS DECIMAL)), 0) as total
317
+ FROM meter_events
318
+ WHERE event_name = :eventName AND instance_did = :instance_did
302
319
  ${startTime ? 'AND created_at >= :startTime' : ''}
303
320
  ${endTime ? 'AND created_at <= :endTime' : ''}`,
304
321
  {
305
322
  replacements: {
306
323
  eventName,
324
+ instance_did: getInstanceDid(),
307
325
  startTime: startTime?.toISOString(),
308
326
  endTime: endTime?.toISOString(),
309
327
  },
@@ -1,8 +1,10 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
- import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
2
+ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes } from 'sequelize';
3
3
  import type { LiteralUnion } from 'type-fest';
4
+ import { TenantModel } from '../tenant-model';
5
+ import { getInstanceDid } from '../../libs/context';
4
6
 
5
- import { createEvent } from '../../libs/audit';
7
+ import { createEvent, reportAuditFailure } from '../../libs/audit';
6
8
  import { createIdGenerator } from '../../libs/util';
7
9
  import type { TPaymentCurrency } from './payment-currency';
8
10
 
@@ -12,7 +14,7 @@ export type TMeterExpanded = TMeter & {
12
14
  paymentCurrency: TPaymentCurrency;
13
15
  };
14
16
 
15
- export class Meter extends Model<InferAttributes<Meter>, InferCreationAttributes<Meter>> {
17
+ export class Meter extends TenantModel<InferAttributes<Meter>, InferCreationAttributes<Meter>> {
16
18
  declare id: CreationOptional<string>;
17
19
  declare object: CreationOptional<string>;
18
20
  declare name: string;
@@ -23,6 +25,7 @@ export class Meter extends Model<InferAttributes<Meter>, InferCreationAttributes
23
25
  declare description?: string;
24
26
  declare component_did?: string;
25
27
  declare livemode: boolean;
28
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
26
29
 
27
30
  declare metadata?: Record<string, any>;
28
31
  declare currency_id?: string;
@@ -54,7 +57,7 @@ export class Meter extends Model<InferAttributes<Meter>, InferCreationAttributes
54
57
  event_name: {
55
58
  type: DataTypes.STRING(128),
56
59
  allowNull: false,
57
- unique: true,
60
+ // uniqueness is per tenant since Phase 2: see uq_* composite indexes in tenant-backfill.ts
58
61
  },
59
62
  aggregation_method: {
60
63
  type: DataTypes.ENUM('sum', 'count', 'last'),
@@ -115,25 +118,31 @@ export class Meter extends Model<InferAttributes<Meter>, InferCreationAttributes
115
118
  };
116
119
 
117
120
  public static initialize(sequelize: any) {
118
- this.init(this.GENESIS_ATTRIBUTES, {
119
- sequelize,
120
- modelName: 'Meter',
121
- tableName: 'meters',
122
- createdAt: 'created_at',
123
- updatedAt: 'updated_at',
124
- indexes: [{ fields: ['event_name'] }, { fields: ['currency_id'] }],
125
- hooks: {
126
- afterCreate: (model: Meter, options) => {
127
- createEvent('Meter', 'meter.created', model, options).catch(console.error);
128
- },
129
- afterUpdate: (model: Meter, options) => {
130
- createEvent('Meter', 'meter.updated', model, options).catch(console.error);
131
- },
132
- afterDestroy: (model: Meter, options) => {
133
- createEvent('Meter', 'meter.deleted', model, options).catch(console.error);
134
- },
121
+ this.init(
122
+ {
123
+ ...this.GENESIS_ATTRIBUTES,
124
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
135
125
  },
136
- });
126
+ {
127
+ sequelize,
128
+ modelName: 'Meter',
129
+ tableName: 'meters',
130
+ createdAt: 'created_at',
131
+ updatedAt: 'updated_at',
132
+ indexes: [{ fields: ['event_name'] }, { fields: ['currency_id'] }],
133
+ hooks: {
134
+ afterCreate: (model: Meter, options) => {
135
+ createEvent('Meter', 'meter.created', model, options).catch(reportAuditFailure);
136
+ },
137
+ afterUpdate: (model: Meter, options) => {
138
+ createEvent('Meter', 'meter.updated', model, options).catch(reportAuditFailure);
139
+ },
140
+ afterDestroy: (model: Meter, options) => {
141
+ createEvent('Meter', 'meter.deleted', model, options).catch(reportAuditFailure);
142
+ },
143
+ },
144
+ }
145
+ );
137
146
  }
138
147
 
139
148
  public static associate(models: any) {
@@ -5,26 +5,34 @@ import {
5
5
  FindOptions,
6
6
  InferAttributes,
7
7
  InferCreationAttributes,
8
- Model,
9
8
  Op,
10
9
  QueryTypes,
11
10
  } from 'sequelize';
12
- import { getUrl } from '@blocklet/sdk';
11
+ // Phase 13b2: import the submodule, not the @blocklet/sdk index. The index eagerly
12
+ // require()s ./wallet-authenticator -> @arcblock/did-connect-js, which a host with
13
+ // skewed @arcblock deps (e.g. arc's @arcblock/validator@1.30.24 override dropping
14
+ // the Joi export that the old did-connect-js@1.29.27 needs) crashes at module-init.
15
+ // This model is bound by initialize() during createEmbeddedPaymentService, so the
16
+ // index would otherwise load that whole auth chain on the entitlements path.
17
+ import { getUrl } from '@blocklet/sdk/lib/component';
13
18
  import type { LiteralUnion } from 'type-fest';
14
19
  import { fromTokenToUnit } from '@ocap/util';
20
+ import { TenantModel } from '../tenant-model';
21
+ import { getInstanceDid } from '../../libs/context';
15
22
  import { createIdGenerator } from '../../libs/util';
16
23
  import { RechargeConfig, VaultConfig } from './types';
17
24
 
18
25
  const nextId = createIdGenerator('pc', 12);
19
26
 
20
27
  // eslint-disable-next-line prettier/prettier
21
- export class PaymentCurrency extends Model<InferAttributes<PaymentCurrency>, InferCreationAttributes<PaymentCurrency>> {
28
+ export class PaymentCurrency extends TenantModel<InferAttributes<PaymentCurrency>, InferCreationAttributes<PaymentCurrency>> {
22
29
  // Unique identifier for the object.
23
30
  declare id: CreationOptional<string>;
24
31
  declare payment_method_id: string;
25
32
 
26
33
  declare active: boolean;
27
34
  declare livemode: boolean;
35
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
28
36
  declare locked: boolean;
29
37
  declare is_base_currency: boolean;
30
38
 
@@ -137,6 +145,12 @@ export class PaymentCurrency extends Model<InferAttributes<PaymentCurrency>, Inf
137
145
  this.init(
138
146
  {
139
147
  ...PaymentCurrency.GENESIS_ATTRIBUTES,
148
+ instance_did: {
149
+ type: DataTypes.STRING(64),
150
+ allowNull: true,
151
+ // bare creates must still land in the active tenant (single mode = app DID)
152
+ defaultValue: () => getInstanceDid(),
153
+ },
140
154
  type: {
141
155
  type: DataTypes.ENUM('standard', 'credit'),
142
156
  defaultValue: 'standard',
@@ -203,14 +217,17 @@ export class PaymentCurrency extends Model<InferAttributes<PaymentCurrency>, Inf
203
217
  if (price) {
204
218
  return true;
205
219
  }
220
+ // 洞 G (Phase 4): raw read on prices (tenant table) — guard with
221
+ // instance_did so currency-usage never counts another tenant's prices.
206
222
  // @ts-ignore
207
223
  const [{ count }] = await this.sequelize.query(
208
- `SELECT count(p.id) AS count
209
- FROM prices AS p
210
- JOIN json_each(p.currency_options) AS option
211
- ON json_extract(option.value, '$.currency_id') = ?`,
224
+ `SELECT count(p.id) AS count
225
+ FROM prices AS p
226
+ JOIN json_each(p.currency_options) AS option
227
+ ON json_extract(option.value, '$.currency_id') = :currency_id
228
+ WHERE p.instance_did = :instance_did`,
212
229
  {
213
- replacements: [this.id],
230
+ replacements: { currency_id: this.id, instance_did: getInstanceDid() },
214
231
  type: QueryTypes.SELECT,
215
232
  }
216
233
  );