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,13 +1,14 @@
1
1
  import { fromTokenToUnit } from '@ocap/util';
2
2
  import Sequelize from 'sequelize';
3
3
  import { joinURL } from 'ufo';
4
+ import { blockletMountPoints } from '../../libs/env';
4
5
 
5
6
  import { createIdGenerator } from '../../libs/util';
6
7
  import type { Migration } from '../migrate';
7
8
 
8
9
  const getUrl = (...parts: string[]): string => {
9
10
  const { BLOCKLET_COMPONENT_DID, BLOCKLET_APP_URL } = process.env;
10
- const components = JSON.parse(process.env.BLOCKLET_MOUNT_POINTS || '[]');
11
+ const components = JSON.parse(blockletMountPoints() || '[]');
11
12
  const component = components.find((x: any) => [x.title, x.name, x.did].includes(BLOCKLET_COMPONENT_DID));
12
13
  const mountPoint = component?.mountPoint || '/';
13
14
  return joinURL(BLOCKLET_APP_URL as string, mountPoint === '/' ? '' : mountPoint, ...parts);
@@ -0,0 +1,23 @@
1
+ import { type Migration } from '../migrate';
2
+
3
+ // The DID Space billing-mirror integration was removed (libs/did-space.ts,
4
+ // queues/space.ts). Rows in `jobs` with queue='did-space' can never be picked
5
+ // up again — the scheduled-job scan only matches registered queue names — so
6
+ // they would sit in the table forever. Delete them.
7
+ //
8
+ // Idempotent: re-running deletes nothing.
9
+ //
10
+ // Dated 20260609 (before the tenant-columns/tenant-backfill pair; written
11
+ // 2026-06-12): the tenant-backfill acceptance spec drives umzug positionally
12
+ // and requires 20260611-tenant-backfill to stay the LAST migration. The jobs
13
+ // table exists since genesis, so running earlier is semantically identical;
14
+ // on already-migrated deployments umzug executes pending migrations
15
+ // regardless of how their names sort against executed ones.
16
+
17
+ export const up: Migration = async ({ context }) => {
18
+ await context.sequelize.query("DELETE FROM `jobs` WHERE `queue` = 'did-space'");
19
+ };
20
+
21
+ export const down: Migration = async () => {
22
+ // Deleted jobs are not restorable (and the queue no longer exists).
23
+ };
@@ -0,0 +1,40 @@
1
+ import { type Migration } from '../migrate';
2
+ import { TENANT_TABLES } from '../tenant-tables';
3
+
4
+ // Phase 1 (W1-1a): add a nullable instance_did column to all 38 tenant tables.
5
+ // No constraints, no indexes, no backfill — those land in Phase 2 (W1-1b).
6
+ // Until then the column stays NULL and no code reads or writes it, so runtime
7
+ // behavior is byte-for-byte identical to the previous release.
8
+ //
9
+ // Uses PRAGMA table_info + raw ALTER TABLE (mirroring cloudflare/migrations/
10
+ // 0006_tenant_columns.sql) instead of describeTable/removeColumn: sequelize's
11
+ // sqlite describeTable chokes on some of our partial indexes, and removeColumn
12
+ // rebuilds the whole table for no benefit here. Table names are static literals
13
+ // from TENANT_TABLES — nothing is interpolated from config or env.
14
+
15
+ async function hasInstanceDid(context: any, table: string): Promise<boolean> {
16
+ const [rows] = await context.sequelize.query(`PRAGMA table_info(${table})`);
17
+ return (rows as { name: string }[]).some((row) => row.name === 'instance_did');
18
+ }
19
+
20
+ export const up: Migration = async ({ context }) => {
21
+ for (const table of TENANT_TABLES) {
22
+ // eslint-disable-next-line no-await-in-loop
23
+ if (!(await hasInstanceDid(context, table))) {
24
+ // eslint-disable-next-line no-await-in-loop
25
+ await context.sequelize.query(`ALTER TABLE ${table} ADD COLUMN instance_did VARCHAR(64)`);
26
+ }
27
+ }
28
+ };
29
+
30
+ export const down: Migration = async ({ context }) => {
31
+ for (const table of TENANT_TABLES) {
32
+ // eslint-disable-next-line no-await-in-loop
33
+ if (await hasInstanceDid(context, table)) {
34
+ // SQLite >= 3.35 supports DROP COLUMN; safe here because Phase 1 adds
35
+ // no indexes or constraints on the column.
36
+ // eslint-disable-next-line no-await-in-loop
37
+ await context.sequelize.query(`ALTER TABLE ${table} DROP COLUMN instance_did`);
38
+ }
39
+ }
40
+ };
@@ -0,0 +1,33 @@
1
+ import { type Migration } from '../migrate';
2
+ import { UNIQUE_KEY_REVISIONS, runTenantBackfill } from '../tenant-backfill';
3
+ import { TENANT_TABLES } from '../tenant-tables';
4
+
5
+ // Phase 2 (W1-1b): backfill instance_did with the deployment app DID, revise
6
+ // business unique keys to (instance_did, ...) composites, add tenant indexes.
7
+ // All heavy lifting lives in ../tenant-backfill.ts, shared with the CF worker
8
+ // runtime compensation path (static D1 SQL cannot know the app DID).
9
+ //
10
+ // Idempotent: backfill touches NULL rows only; rebuilds detect already-rebuilt
11
+ // DDL; index creation is IF NOT EXISTS. Re-running after a crash continues.
12
+
13
+ export const up: Migration = async ({ context }) => {
14
+ await runTenantBackfill(context.sequelize as any);
15
+ };
16
+
17
+ export const down: Migration = async ({ context }) => {
18
+ const { sequelize } = context as any;
19
+ // Reverting Phase 2 = dropping the composite uniques and tenant indexes.
20
+ // Backfilled values stay (the column is nullable in DDL for non-rebuilt
21
+ // tables and harmless when unused); restoring the old inline single-column
22
+ // uniques is deliberately NOT done — they would forbid the cross-tenant
23
+ // duplicates this phase exists to allow, and recreating them on a DB that
24
+ // already contains such duplicates would corrupt the rollback.
25
+ for (const revision of UNIQUE_KEY_REVISIONS) {
26
+ // eslint-disable-next-line no-await-in-loop
27
+ await sequelize.query(`DROP INDEX IF EXISTS \`${revision.index}\``);
28
+ }
29
+ for (const table of TENANT_TABLES) {
30
+ // eslint-disable-next-line no-await-in-loop
31
+ await sequelize.query(`DROP INDEX IF EXISTS \`idx_${table}_instance_did\``);
32
+ }
33
+ };
@@ -1,17 +1,23 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
- import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, Model } from 'sequelize';
3
-
2
+ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes } from 'sequelize';
4
3
  import { BN } from '@ocap/util';
4
+ import { TenantModel } from '../tenant-model';
5
+ import { getInstanceDid } from '../../libs/context';
6
+
5
7
  import { createIdGenerator } from '../../libs/util';
6
8
  import { PaymentDetails, PaymentSettings } from './types';
7
9
 
8
10
  const nextId = createIdGenerator('carc', 24);
9
11
 
10
12
  // eslint-disable-next-line prettier/prettier
11
- export class AutoRechargeConfig extends Model<InferAttributes<AutoRechargeConfig>, InferCreationAttributes<AutoRechargeConfig>> {
13
+ export class AutoRechargeConfig extends TenantModel<
14
+ InferAttributes<AutoRechargeConfig>,
15
+ InferCreationAttributes<AutoRechargeConfig>
16
+ > {
12
17
  declare id: CreationOptional<string>;
13
18
  declare customer_id: string;
14
19
  declare livemode: boolean;
20
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
15
21
 
16
22
  declare enabled: boolean;
17
23
  declare threshold: string;
@@ -150,13 +156,19 @@ export class AutoRechargeConfig extends Model<InferAttributes<AutoRechargeConfig
150
156
  };
151
157
 
152
158
  public static initialize(sequelize: any) {
153
- this.init(AutoRechargeConfig.GENESIS_ATTRIBUTES, {
154
- sequelize,
155
- modelName: 'AutoRechargeConfig',
156
- tableName: 'auto_recharge_configs',
157
- createdAt: 'created_at',
158
- updatedAt: 'updated_at',
159
- });
159
+ this.init(
160
+ {
161
+ ...AutoRechargeConfig.GENESIS_ATTRIBUTES,
162
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
163
+ },
164
+ {
165
+ sequelize,
166
+ modelName: 'AutoRechargeConfig',
167
+ tableName: 'auto_recharge_configs',
168
+ createdAt: 'created_at',
169
+ updatedAt: 'updated_at',
170
+ }
171
+ );
160
172
  }
161
173
 
162
174
  public static associate(models: any) {
@@ -1,17 +1,11 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
2
  /* eslint-disable @typescript-eslint/indent */
3
- import {
4
- CreationOptional,
5
- DataTypes,
6
- FindOptions,
7
- InferAttributes,
8
- InferCreationAttributes,
9
- Model,
10
- Op,
11
- } from 'sequelize';
3
+ import { CreationOptional, DataTypes, FindOptions, InferAttributes, InferCreationAttributes, Op } from 'sequelize';
12
4
  import type { LiteralUnion } from 'type-fest';
5
+ import { TenantModel } from '../tenant-model';
6
+ import { getInstanceDid } from '../../libs/context';
13
7
 
14
- import { createCustomEvent, createEvent, createStatusEvent } from '../../libs/audit';
8
+ import { createCustomEvent, createEvent, createStatusEvent, reportAuditFailure } from '../../libs/audit';
15
9
  import dayjs from '../../libs/dayjs';
16
10
  import { CHECKOUT_SESSION_TTL, createIdGenerator } from '../../libs/util';
17
11
  import type {
@@ -35,10 +29,11 @@ export const nextCheckoutSessionId = createIdGenerator('cs', 58);
35
29
 
36
30
  // @link https://stripe.com/docs/api/checkout/sessions
37
31
  // eslint-disable-next-line prettier/prettier
38
- export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, InferCreationAttributes<CheckoutSession>> {
32
+ export class CheckoutSession extends TenantModel<InferAttributes<CheckoutSession>, InferCreationAttributes<CheckoutSession>> {
39
33
  // Unique identifier for the object.
40
34
  declare id: CreationOptional<string>;
41
35
  declare livemode: boolean;
36
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
42
37
  declare client_reference_id?: string;
43
38
 
44
39
  // ID of the payment currency
@@ -445,6 +440,12 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
445
440
  this.init(
446
441
  {
447
442
  ...CheckoutSession.GENESIS_ATTRIBUTES,
443
+ instance_did: {
444
+ type: DataTypes.STRING(64),
445
+ allowNull: true,
446
+ // bare creates must still land in the active tenant (single mode = app DID)
447
+ defaultValue: () => getInstanceDid(),
448
+ },
448
449
  discounts: {
449
450
  type: DataTypes.JSON,
450
451
  allowNull: true,
@@ -503,7 +504,7 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
503
504
  updatedAt: 'updated_at',
504
505
  hooks: {
505
506
  afterCreate: (model: CheckoutSession, options) => {
506
- createEvent('CheckoutSession', 'checkout.session.created', model, options).catch(console.error);
507
+ createEvent('CheckoutSession', 'checkout.session.created', model, options).catch(reportAuditFailure);
507
508
  },
508
509
  afterUpdate: (model: CheckoutSession, options) => {
509
510
  createStatusEvent(
@@ -512,7 +513,7 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
512
513
  { complete: 'completed', expired: 'expired' },
513
514
  model,
514
515
  options
515
- ).catch(console.error);
516
+ ).catch(reportAuditFailure);
516
517
  createCustomEvent(
517
518
  'CheckoutSession',
518
519
  'checkout.session',
@@ -524,7 +525,7 @@ export class CheckoutSession extends Model<InferAttributes<CheckoutSession>, Inf
524
525
  },
525
526
  model,
526
527
  options
527
- ).catch(console.error);
528
+ ).catch(reportAuditFailure);
528
529
  },
529
530
  },
530
531
  }
@@ -1,9 +1,11 @@
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
4
  import { fromTokenToUnit } from '@ocap/util';
5
+ import { getInstanceDid } from '../../libs/context';
6
+ import { TenantModel } from '../tenant-model';
5
7
 
6
- import { createEvent } from '../../libs/audit';
8
+ import { createEvent, reportAuditFailure } from '../../libs/audit';
7
9
  import { createCodeGenerator, formatMetadata } from '../../libs/util';
8
10
  import { trimDecimals } from '../../libs/math-utils';
9
11
  import logger from '../../libs/logger';
@@ -12,9 +14,10 @@ import type { TPaymentCurrency } from './payment-currency';
12
14
  const nextId = createCodeGenerator('', 8);
13
15
 
14
16
  // eslint-disable-next-line prettier/prettier
15
- export class Coupon extends Model<InferAttributes<Coupon>, InferCreationAttributes<Coupon>> {
17
+ export class Coupon extends TenantModel<InferAttributes<Coupon>, InferCreationAttributes<Coupon>> {
16
18
  declare id: CreationOptional<string>;
17
19
  declare livemode: boolean;
20
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
18
21
  declare locked: CreationOptional<boolean>;
19
22
 
20
23
  declare amount_off: string;
@@ -146,24 +149,30 @@ export class Coupon extends Model<InferAttributes<Coupon>, InferCreationAttribut
146
149
  };
147
150
 
148
151
  public static initialize(sequelize: any) {
149
- this.init(Coupon.GENESIS_ATTRIBUTES, {
150
- sequelize,
151
- modelName: 'Coupon',
152
- tableName: 'coupons',
153
- createdAt: 'created_at',
154
- updatedAt: 'updated_at',
155
- hooks: {
156
- afterCreate: (model: Coupon, options) => {
157
- createEvent('Coupon', 'coupon.created', model, options).catch(console.error);
158
- },
159
- afterUpdate: (model: Coupon, options) => {
160
- createEvent('Coupon', 'coupon.updated', model, options).catch(console.error);
161
- },
162
- afterDestroy: (model: Coupon, options) => {
163
- createEvent('Coupon', 'coupon.deleted', model, options).catch(console.error);
164
- },
152
+ this.init(
153
+ {
154
+ ...Coupon.GENESIS_ATTRIBUTES,
155
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
165
156
  },
166
- });
157
+ {
158
+ sequelize,
159
+ modelName: 'Coupon',
160
+ tableName: 'coupons',
161
+ createdAt: 'created_at',
162
+ updatedAt: 'updated_at',
163
+ hooks: {
164
+ afterCreate: (model: Coupon, options) => {
165
+ createEvent('Coupon', 'coupon.created', model, options).catch(reportAuditFailure);
166
+ },
167
+ afterUpdate: (model: Coupon, options) => {
168
+ createEvent('Coupon', 'coupon.updated', model, options).catch(reportAuditFailure);
169
+ },
170
+ afterDestroy: (model: Coupon, options) => {
171
+ createEvent('Coupon', 'coupon.deleted', model, options).catch(reportAuditFailure);
172
+ },
173
+ },
174
+ }
175
+ );
167
176
  }
168
177
 
169
178
  public static associate(models: any) {
@@ -1,9 +1,11 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
2
  import { BN } from '@ocap/util';
3
- import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, literal, Model, Op } from 'sequelize';
3
+ import { CreationOptional, DataTypes, InferAttributes, InferCreationAttributes, literal, Op } from 'sequelize';
4
4
  import type { LiteralUnion } from 'type-fest';
5
+ import { TenantModel } from '../tenant-model';
6
+ import { getInstanceDid } from '../../libs/context';
5
7
 
6
- import { createEvent } from '../../libs/audit';
8
+ import { createEvent, reportAuditFailure } from '../../libs/audit';
7
9
  import { createIdGenerator } from '../../libs/util';
8
10
  import dayjs from '../../libs/dayjs';
9
11
  import { CreditGrantApplicabilityConfig, CreditGrantChainDetail, CreditGrantChainStatus } from './types';
@@ -43,7 +45,7 @@ async function createCreditGrantStatusEvent(model: CreditGrant, options: any): P
43
45
 
44
46
  export const nextCreditGrantId = createIdGenerator('credgr', 14);
45
47
 
46
- export class CreditGrant extends Model<InferAttributes<CreditGrant>, InferCreationAttributes<CreditGrant>> {
48
+ export class CreditGrant extends TenantModel<InferAttributes<CreditGrant>, InferCreationAttributes<CreditGrant>> {
47
49
  declare id: CreationOptional<string>;
48
50
  declare object: CreationOptional<string>;
49
51
  declare amount: string;
@@ -54,6 +56,7 @@ export class CreditGrant extends Model<InferAttributes<CreditGrant>, InferCreati
54
56
  declare effective_at?: number;
55
57
  declare expires_at?: number;
56
58
  declare livemode: boolean;
59
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
57
60
  declare metadata?: Record<string, any>;
58
61
  declare name?: string;
59
62
  declare priority: number; // 0-100, 0为最高优先级
@@ -265,7 +268,7 @@ export class CreditGrant extends Model<InferAttributes<CreditGrant>, InferCreati
265
268
  // Fire-and-forget: audit event should not block the consumption hot path.
266
269
  // Trade-off: if the process crashes between save() and this call, the audit event is lost,
267
270
  // but grant state is already persisted and can be reconciled from DB.
268
- createEvent('CreditGrant', 'customer.credit_grant.consumed', this).catch(console.error);
271
+ createEvent('CreditGrant', 'customer.credit_grant.consumed', this).catch(reportAuditFailure);
269
272
  }
270
273
 
271
274
  return {
@@ -302,32 +305,38 @@ export class CreditGrant extends Model<InferAttributes<CreditGrant>, InferCreati
302
305
  }
303
306
 
304
307
  public static initialize(sequelize: any) {
305
- this.init(this.GENESIS_ATTRIBUTES, {
306
- sequelize,
307
- modelName: 'CreditGrant',
308
- tableName: 'credit_grants',
309
- createdAt: 'created_at',
310
- updatedAt: 'updated_at',
311
- indexes: [
312
- { fields: ['status'], name: 'idx_credit_grant_status' },
313
- { fields: ['effective_at'], name: 'idx_credit_grant_effective_at' },
314
- { fields: ['expires_at'], name: 'idx_credit_grant_expires_at' },
315
- ],
316
- hooks: {
317
- afterCreate: (model: CreditGrant, options) => {
318
- createEvent('CreditGrant', 'customer.credit_grant.created', model, options).catch(console.error);
319
- },
320
- afterUpdate: (model: CreditGrant, options) => {
321
- createEvent('CreditGrant', 'customer.credit_grant.updated', model, options).catch(console.error);
322
-
323
- if (model.changed('status')) {
324
- createCreditGrantStatusEvent(model, options).catch(console.error);
325
- }
326
- },
327
- afterDestroy: (model: CreditGrant, options) =>
328
- createEvent('CreditGrant', 'customer.credit_grant.deleted', model, options).catch(console.error),
308
+ this.init(
309
+ {
310
+ ...this.GENESIS_ATTRIBUTES,
311
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
329
312
  },
330
- });
313
+ {
314
+ sequelize,
315
+ modelName: 'CreditGrant',
316
+ tableName: 'credit_grants',
317
+ createdAt: 'created_at',
318
+ updatedAt: 'updated_at',
319
+ indexes: [
320
+ { fields: ['status'], name: 'idx_credit_grant_status' },
321
+ { fields: ['effective_at'], name: 'idx_credit_grant_effective_at' },
322
+ { fields: ['expires_at'], name: 'idx_credit_grant_expires_at' },
323
+ ],
324
+ hooks: {
325
+ afterCreate: (model: CreditGrant, options) => {
326
+ createEvent('CreditGrant', 'customer.credit_grant.created', model, options).catch(reportAuditFailure);
327
+ },
328
+ afterUpdate: (model: CreditGrant, options) => {
329
+ createEvent('CreditGrant', 'customer.credit_grant.updated', model, options).catch(reportAuditFailure);
330
+
331
+ if (model.changed('status')) {
332
+ createCreditGrantStatusEvent(model, options).catch(reportAuditFailure);
333
+ }
334
+ },
335
+ afterDestroy: (model: CreditGrant, options) =>
336
+ createEvent('CreditGrant', 'customer.credit_grant.deleted', model, options).catch(reportAuditFailure),
337
+ },
338
+ }
339
+ );
331
340
  }
332
341
 
333
342
  public static associate(models: any) {
@@ -1,18 +1,21 @@
1
1
  /* eslint-disable @typescript-eslint/lines-between-class-members */
2
- import { col, CreationOptional, DataTypes, fn, InferAttributes, InferCreationAttributes, Model, Op } from 'sequelize';
3
-
2
+ import { col, CreationOptional, DataTypes, fn, InferAttributes, InferCreationAttributes, Op } from 'sequelize';
4
3
  import { BN } from '@ocap/util';
5
- import { createEvent } from '../../libs/audit';
4
+ import { TenantModel } from '../tenant-model';
5
+ import { getInstanceDid } from '../../libs/context';
6
+
7
+ import { createEvent, reportAuditFailure } from '../../libs/audit';
6
8
  import { createIdGenerator } from '../../libs/util';
7
9
  import { CreditGrant } from './credit-grant';
8
10
 
9
11
  export const nextCreditTransactionId = createIdGenerator('cbtxn', 14);
10
12
 
11
- export class CreditTransaction extends Model<
13
+ export class CreditTransaction extends TenantModel<
12
14
  InferAttributes<CreditTransaction>,
13
15
  InferCreationAttributes<CreditTransaction>
14
16
  > {
15
17
  declare id: CreationOptional<string>;
18
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
16
19
  declare quantity: string;
17
20
  declare credit_amount: string;
18
21
  declare remaining_balance: string;
@@ -113,24 +116,32 @@ export class CreditTransaction extends Model<
113
116
  };
114
117
 
115
118
  public static initialize(sequelize: any) {
116
- this.init(this.GENESIS_ATTRIBUTES, {
117
- sequelize,
118
- modelName: 'CreditTransaction',
119
- tableName: 'credit_transactions',
120
- createdAt: 'created_at',
121
- updatedAt: 'updated_at',
122
- indexes: [
123
- { fields: ['customer_id'] },
124
- { fields: ['credit_grant_id'] },
125
- { fields: ['source'] },
126
- { fields: ['transfer_status'] },
127
- ],
128
- hooks: {
129
- afterCreate: (model: CreditTransaction, options) => {
130
- createEvent('CreditTransaction', 'customer.credit_transaction.created', model, options).catch(console.error);
131
- },
119
+ this.init(
120
+ {
121
+ ...this.GENESIS_ATTRIBUTES,
122
+ instance_did: { type: DataTypes.STRING(64), allowNull: true, defaultValue: () => getInstanceDid() },
132
123
  },
133
- });
124
+ {
125
+ sequelize,
126
+ modelName: 'CreditTransaction',
127
+ tableName: 'credit_transactions',
128
+ createdAt: 'created_at',
129
+ updatedAt: 'updated_at',
130
+ indexes: [
131
+ { fields: ['customer_id'] },
132
+ { fields: ['credit_grant_id'] },
133
+ { fields: ['source'] },
134
+ { fields: ['transfer_status'] },
135
+ ],
136
+ hooks: {
137
+ afterCreate: (model: CreditTransaction, options) => {
138
+ createEvent('CreditTransaction', 'customer.credit_transaction.created', model, options).catch(
139
+ reportAuditFailure
140
+ );
141
+ },
142
+ },
143
+ }
144
+ );
134
145
  }
135
146
 
136
147
  public static associate(models: any) {
@@ -2,19 +2,14 @@
2
2
  import { BN } from '@ocap/util';
3
3
  import cloneDeep from 'lodash/cloneDeep';
4
4
  import padStart from 'lodash/padStart';
5
- import {
6
- CreationOptional,
7
- DataTypes,
8
- FindOptions,
9
- InferAttributes,
10
- InferCreationAttributes,
11
- Model,
12
- Op,
13
- } from 'sequelize';
14
-
5
+ import { CreationOptional, DataTypes, FindOptions, InferAttributes, InferCreationAttributes, Op } from 'sequelize';
15
6
  import merge from 'lodash/merge';
7
+ import { cfEnv } from '../../libs/env';
8
+ import { TenantModel } from '../tenant-model';
9
+
10
+ import { getInstanceDid } from '../../libs/context';
16
11
  import { getLock } from '../../libs/lock';
17
- import { createEvent } from '../../libs/audit';
12
+ import { createEvent, reportAuditFailure } from '../../libs/audit';
18
13
  import CustomError from '../../libs/error';
19
14
  import { createCodeGenerator, createIdGenerator } from '../../libs/util';
20
15
  import type { CustomerAddress, CustomerPreferences, CustomerShipping } from './types';
@@ -23,9 +18,10 @@ export const nextCustomerId = createIdGenerator('cus', 14);
23
18
  export const nextInvoicePrefix = createCodeGenerator('', 8);
24
19
 
25
20
  // eslint-disable-next-line prettier/prettier
26
- export class Customer extends Model<InferAttributes<Customer>, InferCreationAttributes<Customer>> {
21
+ export class Customer extends TenantModel<InferAttributes<Customer>, InferCreationAttributes<Customer>> {
27
22
  declare id: CreationOptional<string>;
28
23
  declare livemode: boolean;
24
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
29
25
 
30
26
  declare did: string;
31
27
  declare description: string;
@@ -85,7 +81,7 @@ export class Customer extends Model<InferAttributes<Customer>, InferCreationAttr
85
81
  did: {
86
82
  type: DataTypes.STRING(40),
87
83
  allowNull: false,
88
- unique: true,
84
+ // uniqueness is per tenant since Phase 2: see uq_* composite indexes in tenant-backfill.ts
89
85
  },
90
86
  address: {
91
87
  type: DataTypes.JSON,
@@ -177,7 +173,7 @@ export class Customer extends Model<InferAttributes<Customer>, InferCreationAttr
177
173
  //
178
174
  // SQLite serializes writes at the DB level, so the application-level lock is
179
175
  // redundant for both D1 and Blocklet Server's SQLite.
180
- const d1 = (globalThis as any).__CF_ENV__?.DB;
176
+ const d1 = cfEnv()?.DB;
181
177
  if (d1) {
182
178
  try {
183
179
  const sql =
@@ -265,6 +261,12 @@ export class Customer extends Model<InferAttributes<Customer>, InferCreationAttr
265
261
  this.init(
266
262
  {
267
263
  ...Customer.GENESIS_ATTRIBUTES,
264
+ instance_did: {
265
+ type: DataTypes.STRING(64),
266
+ allowNull: true,
267
+ // bare creates must still land in the active tenant (single mode = app DID)
268
+ defaultValue: () => getInstanceDid(),
269
+ },
268
270
  token_balance: {
269
271
  type: DataTypes.JSON,
270
272
  defaultValue: {},
@@ -295,13 +297,13 @@ export class Customer extends Model<InferAttributes<Customer>, InferCreationAttr
295
297
  updatedAt: 'updated_at',
296
298
  hooks: {
297
299
  afterCreate: (model: Customer, options) => {
298
- createEvent('Customer', 'customer.created', model, options).catch(console.error);
300
+ createEvent('Customer', 'customer.created', model, options).catch(reportAuditFailure);
299
301
  },
300
302
  afterUpdate: (model: Customer, options) => {
301
- createEvent('Customer', 'customer.updated', model, options).catch(console.error);
303
+ createEvent('Customer', 'customer.updated', model, options).catch(reportAuditFailure);
302
304
  },
303
305
  afterDestroy: (model: Customer, options) => {
304
- createEvent('Customer', 'customer.deleted', model, options).catch(console.error);
306
+ createEvent('Customer', 'customer.deleted', model, options).catch(reportAuditFailure);
305
307
  },
306
308
  },
307
309
  }
@@ -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 { createCodeGenerator } from '../../libs/util';
5
7
 
@@ -7,9 +9,10 @@ const nextId = createCodeGenerator('', 8);
7
9
 
8
10
  // @link https://stripe.com/docs/api/discounts
9
11
  // eslint-disable-next-line prettier/prettier
10
- export class Discount extends Model<InferAttributes<Discount>, InferCreationAttributes<Discount>> {
12
+ export class Discount extends TenantModel<InferAttributes<Discount>, InferCreationAttributes<Discount>> {
11
13
  declare id: CreationOptional<string>;
12
14
  declare livemode: boolean;
15
+ declare instance_did?: string; // tenant column (Phase 1, W1-1a), nullable until Phase 2 backfill; scoped queries land in Phase 3
13
16
 
14
17
  declare coupon_id: string;
15
18
  declare promotion_code_id?: string;
@@ -105,6 +108,12 @@ export class Discount extends Model<InferAttributes<Discount>, InferCreationAttr
105
108
  this.init(
106
109
  {
107
110
  ...Discount.GENESIS_ATTRIBUTES,
111
+ instance_did: {
112
+ type: DataTypes.STRING(64),
113
+ allowNull: true,
114
+ // bare creates must still land in the active tenant (single mode = app DID)
115
+ defaultValue: () => getInstanceDid(),
116
+ },
108
117
  confirmed: {
109
118
  type: DataTypes.BOOLEAN,
110
119
  defaultValue: true,