@wopr-network/platform-core 1.13.2 → 1.14.0

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 (238) hide show
  1. package/dist/api/routes/admin-credits.d.ts +2 -2
  2. package/dist/api/routes/admin-credits.js +9 -4
  3. package/dist/api/routes/quota.d.ts +2 -2
  4. package/dist/api/routes/verify-email.d.ts +3 -3
  5. package/dist/backup/on-demand-snapshot-service.d.ts +2 -2
  6. package/dist/billing/payram/webhook.d.ts +3 -3
  7. package/dist/billing/payram/webhook.js +5 -1
  8. package/dist/billing/payram/webhook.test.js +5 -4
  9. package/dist/billing/stripe/stripe-payment-processor.d.ts +2 -2
  10. package/dist/billing/stripe/stripe-payment-processor.test.js +7 -0
  11. package/dist/billing/stripe/tenant-store.d.ts +1 -1
  12. package/dist/billing/stripe/tenant-store.js +1 -1
  13. package/dist/credits/auto-topup-charge.d.ts +2 -2
  14. package/dist/credits/auto-topup-charge.js +5 -1
  15. package/dist/credits/auto-topup-charge.test.js +5 -4
  16. package/dist/credits/auto-topup-usage.d.ts +2 -2
  17. package/dist/credits/auto-topup-usage.test.js +53 -12
  18. package/dist/credits/credit-expiry-cron.d.ts +2 -2
  19. package/dist/credits/credit-expiry-cron.js +7 -4
  20. package/dist/credits/credit-expiry-cron.test.js +25 -8
  21. package/dist/credits/credit-ledger.d.ts +2 -2
  22. package/dist/credits/credit-ledger.js +1 -1
  23. package/dist/credits/dividend-cron.d.ts +4 -6
  24. package/dist/credits/dividend-cron.js +10 -16
  25. package/dist/credits/dividend-cron.test.js +31 -44
  26. package/dist/credits/dividend-repository.js +19 -22
  27. package/dist/credits/dividend-repository.test.js +4 -3
  28. package/dist/credits/index.d.ts +4 -2
  29. package/dist/credits/index.js +2 -1
  30. package/dist/credits/ledger.d.ts +195 -0
  31. package/dist/credits/ledger.js +561 -0
  32. package/dist/credits/ledger.test.js +418 -0
  33. package/dist/credits/signup-grant.d.ts +2 -2
  34. package/dist/credits/signup-grant.js +4 -4
  35. package/dist/credits/signup-grant.test.js +5 -3
  36. package/dist/credits/trial-balance-cron.d.ts +19 -0
  37. package/dist/credits/trial-balance-cron.js +30 -0
  38. package/dist/credits/trial-balance-cron.test.js +55 -0
  39. package/dist/db/schema/gateway-service-keys.d.ts +109 -0
  40. package/dist/db/schema/gateway-service-keys.js +18 -0
  41. package/dist/db/schema/index.d.ts +2 -0
  42. package/dist/db/schema/index.js +2 -0
  43. package/dist/db/schema/ledger.d.ts +442 -0
  44. package/dist/db/schema/ledger.js +76 -0
  45. package/dist/gateway/credit-gate.d.ts +2 -2
  46. package/dist/gateway/credit-gate.js +5 -1
  47. package/dist/gateway/credit-gate.test.js +35 -33
  48. package/dist/gateway/gateway-routes.test.js +1 -1
  49. package/dist/gateway/index.d.ts +2 -0
  50. package/dist/gateway/index.js +1 -0
  51. package/dist/gateway/protocol/anthropic.js +1 -1
  52. package/dist/gateway/protocol/deps.d.ts +5 -5
  53. package/dist/gateway/protocol/openai.js +1 -1
  54. package/dist/gateway/proxy.d.ts +4 -4
  55. package/dist/gateway/route-mounting.test.js +1 -1
  56. package/dist/gateway/service-key-auth.d.ts +1 -1
  57. package/dist/gateway/service-key-auth.js +1 -1
  58. package/dist/gateway/service-key-repository.d.ts +27 -0
  59. package/dist/gateway/service-key-repository.js +64 -0
  60. package/dist/gateway/types.d.ts +5 -5
  61. package/dist/metering/reconciliation-cron.test.js +9 -8
  62. package/dist/metering/reconciliation-repository.js +12 -10
  63. package/dist/metering/reconciliation-repository.test.js +9 -8
  64. package/dist/monetization/affiliate/affiliate-admin-repository.js +10 -8
  65. package/dist/monetization/affiliate/affiliate-admin-repository.test.js +32 -13
  66. package/dist/monetization/affiliate/credit-match.d.ts +2 -2
  67. package/dist/monetization/affiliate/credit-match.js +4 -1
  68. package/dist/monetization/affiliate/credit-match.test.js +58 -13
  69. package/dist/monetization/affiliate/new-user-bonus.d.ts +2 -2
  70. package/dist/monetization/affiliate/new-user-bonus.js +4 -1
  71. package/dist/monetization/affiliate/new-user-bonus.test.js +4 -3
  72. package/dist/monetization/credits/auto-topup-charge.d.ts +2 -2
  73. package/dist/monetization/credits/auto-topup-charge.js +5 -1
  74. package/dist/monetization/credits/auto-topup-charge.test.js +5 -4
  75. package/dist/monetization/credits/auto-topup-usage.d.ts +2 -2
  76. package/dist/monetization/credits/auto-topup-usage.test.js +53 -12
  77. package/dist/monetization/credits/bot-billing.d.ts +3 -3
  78. package/dist/monetization/credits/bot-billing.test.js +18 -5
  79. package/dist/monetization/credits/credit-expiry-cron.test.js +25 -8
  80. package/dist/monetization/credits/dividend-cron.d.ts +2 -4
  81. package/dist/monetization/credits/dividend-cron.js +7 -4
  82. package/dist/monetization/credits/dividend-cron.test.js +26 -46
  83. package/dist/monetization/credits/dividend-repository.js +15 -24
  84. package/dist/monetization/credits/dividend-repository.test.js +4 -3
  85. package/dist/monetization/credits/index.d.ts +2 -2
  86. package/dist/monetization/credits/index.js +1 -1
  87. package/dist/monetization/credits/member-usage.test.js +23 -10
  88. package/dist/monetization/credits/phone-billing.d.ts +2 -2
  89. package/dist/monetization/credits/phone-billing.js +5 -1
  90. package/dist/monetization/credits/phone-billing.test.js +9 -12
  91. package/dist/monetization/credits/runtime-cron.d.ts +2 -2
  92. package/dist/monetization/credits/runtime-cron.js +32 -8
  93. package/dist/monetization/credits/runtime-cron.test.js +28 -27
  94. package/dist/monetization/credits/runtime-scheduler.d.ts +2 -2
  95. package/dist/monetization/credits/runtime-scheduler.test.js +1 -1
  96. package/dist/monetization/credits/signup-grant.test.js +5 -3
  97. package/dist/monetization/credits/storage-tier-cron.test.js +3 -2
  98. package/dist/monetization/credits/trial-balance-cron.test.js +42 -0
  99. package/dist/monetization/feature-gate.d.ts +3 -3
  100. package/dist/monetization/index.d.ts +3 -3
  101. package/dist/monetization/index.js +1 -1
  102. package/dist/monetization/metering/reconciliation-cron.test.js +9 -8
  103. package/dist/monetization/metering/reconciliation-repository.js +11 -10
  104. package/dist/monetization/metering/reconciliation-repository.test.js +9 -8
  105. package/dist/monetization/payram/webhook.d.ts +2 -2
  106. package/dist/monetization/payram/webhook.js +5 -1
  107. package/dist/monetization/payram/webhook.test.js +5 -4
  108. package/dist/monetization/promotions/engine.d.ts +2 -2
  109. package/dist/monetization/promotions/engine.js +4 -1
  110. package/dist/monetization/promotions/engine.test.js +3 -1
  111. package/dist/monetization/repository-types.d.ts +1 -1
  112. package/dist/monetization/socket/socket.d.ts +3 -3
  113. package/dist/monetization/stripe/stripe-payment-processor.d.ts +2 -2
  114. package/dist/monetization/stripe/stripe-payment-processor.test.js +7 -0
  115. package/dist/monetization/stripe/webhook.d.ts +2 -2
  116. package/dist/monetization/stripe/webhook.js +70 -6
  117. package/dist/monetization/stripe/webhook.test.js +20 -15
  118. package/dist/onboarding/onboarding-service.d.ts +2 -2
  119. package/dist/onboarding/onboarding-service.js +6 -2
  120. package/drizzle/migrations/0002_gateway_service_keys.sql +14 -0
  121. package/drizzle/migrations/0003_double_entry_ledger.sql +82 -0
  122. package/drizzle/migrations/meta/_journal.json +14 -0
  123. package/package.json +1 -1
  124. package/src/api/routes/admin-credits.ts +11 -14
  125. package/src/api/routes/quota.ts +2 -2
  126. package/src/api/routes/verify-email.ts +4 -4
  127. package/src/backup/on-demand-snapshot-service.test.ts +3 -3
  128. package/src/backup/on-demand-snapshot-service.ts +3 -3
  129. package/src/billing/payram/webhook.test.ts +7 -5
  130. package/src/billing/payram/webhook.ts +8 -11
  131. package/src/billing/stripe/stripe-payment-processor.test.ts +10 -3
  132. package/src/billing/stripe/stripe-payment-processor.ts +3 -3
  133. package/src/billing/stripe/tenant-store.ts +1 -1
  134. package/src/credits/auto-topup-charge.test.ts +7 -5
  135. package/src/credits/auto-topup-charge.ts +7 -10
  136. package/src/credits/auto-topup-usage.test.ts +55 -13
  137. package/src/credits/auto-topup-usage.ts +2 -2
  138. package/src/credits/credit-expiry-cron.test.ts +26 -45
  139. package/src/credits/credit-expiry-cron.ts +9 -12
  140. package/src/credits/credit-ledger.ts +3 -3
  141. package/src/credits/dividend-cron.test.ts +38 -45
  142. package/src/credits/dividend-cron.ts +12 -26
  143. package/src/credits/dividend-repository.test.ts +4 -3
  144. package/src/credits/dividend-repository.ts +21 -23
  145. package/src/credits/index.ts +23 -4
  146. package/src/credits/ledger.test.ts +514 -0
  147. package/src/credits/ledger.ts +851 -0
  148. package/src/credits/signup-grant.test.ts +7 -4
  149. package/src/credits/signup-grant.ts +6 -12
  150. package/src/credits/trial-balance-cron.test.ts +68 -0
  151. package/src/credits/trial-balance-cron.ts +46 -0
  152. package/src/db/schema/gateway-service-keys.ts +23 -0
  153. package/src/db/schema/index.ts +2 -0
  154. package/src/db/schema/ledger.ts +94 -0
  155. package/src/gateway/credit-gate-wiring.test.ts +3 -3
  156. package/src/gateway/credit-gate.test.ts +35 -33
  157. package/src/gateway/credit-gate.ts +6 -10
  158. package/src/gateway/gateway-routes.test.ts +6 -6
  159. package/src/gateway/index.ts +2 -0
  160. package/src/gateway/protocol/anthropic.ts +2 -2
  161. package/src/gateway/protocol/deps.ts +5 -5
  162. package/src/gateway/protocol/openai.ts +2 -2
  163. package/src/gateway/proxy.ts +4 -4
  164. package/src/gateway/route-mounting.test.ts +3 -3
  165. package/src/gateway/service-key-auth.ts +4 -2
  166. package/src/gateway/service-key-repository.ts +87 -0
  167. package/src/gateway/types.ts +5 -5
  168. package/src/metering/reconciliation-cron.test.ts +10 -9
  169. package/src/metering/reconciliation-repository.test.ts +10 -9
  170. package/src/metering/reconciliation-repository.ts +14 -11
  171. package/src/monetization/affiliate/affiliate-admin-repository.test.ts +32 -19
  172. package/src/monetization/affiliate/affiliate-admin-repository.ts +16 -8
  173. package/src/monetization/affiliate/credit-match.test.ts +60 -14
  174. package/src/monetization/affiliate/credit-match.ts +6 -9
  175. package/src/monetization/affiliate/new-user-bonus.test.ts +6 -4
  176. package/src/monetization/affiliate/new-user-bonus.ts +6 -9
  177. package/src/monetization/credits/auto-topup-charge.test.ts +7 -5
  178. package/src/monetization/credits/auto-topup-charge.ts +7 -10
  179. package/src/monetization/credits/auto-topup-usage.test.ts +55 -13
  180. package/src/monetization/credits/auto-topup-usage.ts +2 -2
  181. package/src/monetization/credits/bot-billing.test.ts +20 -6
  182. package/src/monetization/credits/bot-billing.ts +3 -3
  183. package/src/monetization/credits/credit-expiry-cron.test.ts +26 -45
  184. package/src/monetization/credits/dividend-cron.test.ts +34 -48
  185. package/src/monetization/credits/dividend-cron.ts +9 -14
  186. package/src/monetization/credits/dividend-repository.test.ts +4 -3
  187. package/src/monetization/credits/dividend-repository.ts +19 -25
  188. package/src/monetization/credits/index.ts +4 -4
  189. package/src/monetization/credits/member-usage.test.ts +25 -11
  190. package/src/monetization/credits/phone-billing.test.ts +18 -26
  191. package/src/monetization/credits/phone-billing.ts +7 -10
  192. package/src/monetization/credits/runtime-cron.test.ts +29 -28
  193. package/src/monetization/credits/runtime-cron.ts +34 -58
  194. package/src/monetization/credits/runtime-scheduler.test.ts +1 -1
  195. package/src/monetization/credits/runtime-scheduler.ts +2 -2
  196. package/src/monetization/credits/signup-grant.test.ts +7 -4
  197. package/src/monetization/credits/storage-tier-cron.test.ts +5 -3
  198. package/src/monetization/credits/trial-balance-cron.test.ts +52 -0
  199. package/src/monetization/feature-gate.ts +3 -3
  200. package/src/monetization/index.ts +4 -4
  201. package/src/monetization/metering/reconciliation-cron.test.ts +10 -9
  202. package/src/monetization/metering/reconciliation-repository.test.ts +11 -9
  203. package/src/monetization/metering/reconciliation-repository.ts +13 -11
  204. package/src/monetization/payram/webhook.test.ts +7 -5
  205. package/src/monetization/payram/webhook.ts +7 -10
  206. package/src/monetization/promotions/engine.test.ts +6 -5
  207. package/src/monetization/promotions/engine.ts +6 -3
  208. package/src/monetization/repository-types.ts +1 -1
  209. package/src/monetization/socket/socket.ts +4 -4
  210. package/src/monetization/stripe/stripe-payment-processor.test.ts +10 -3
  211. package/src/monetization/stripe/stripe-payment-processor.ts +3 -3
  212. package/src/monetization/stripe/webhook.test.ts +22 -16
  213. package/src/monetization/stripe/webhook.ts +75 -50
  214. package/src/onboarding/onboarding-service.ts +8 -11
  215. package/dist/credits/credit-ledger-extra.test.js +0 -40
  216. package/dist/credits/credit-ledger.bench.js +0 -33
  217. package/dist/credits/credit-ledger.test.d.ts +0 -4
  218. package/dist/credits/credit-ledger.test.js +0 -203
  219. package/dist/credits/credit-transaction-repository.test.js +0 -232
  220. package/dist/monetization/credits/credit-ledger-extra.test.d.ts +0 -1
  221. package/dist/monetization/credits/credit-ledger-extra.test.js +0 -39
  222. package/dist/monetization/credits/credit-ledger.bench.d.ts +0 -1
  223. package/dist/monetization/credits/credit-ledger.bench.js +0 -32
  224. package/dist/monetization/credits/credit-ledger.test.d.ts +0 -4
  225. package/dist/monetization/credits/credit-ledger.test.js +0 -202
  226. package/dist/monetization/credits/credit-transaction-repository.test.d.ts +0 -1
  227. package/dist/monetization/credits/credit-transaction-repository.test.js +0 -232
  228. package/src/credits/credit-ledger-extra.test.ts +0 -57
  229. package/src/credits/credit-ledger.bench.ts +0 -56
  230. package/src/credits/credit-ledger.test.ts +0 -276
  231. package/src/credits/credit-transaction-repository.test.ts +0 -274
  232. package/src/monetization/credits/credit-ledger-extra.test.ts +0 -56
  233. package/src/monetization/credits/credit-ledger.bench.ts +0 -55
  234. package/src/monetization/credits/credit-ledger.test.ts +0 -275
  235. package/src/monetization/credits/credit-transaction-repository.test.ts +0 -274
  236. /package/dist/credits/{credit-ledger-extra.test.d.ts → ledger.test.d.ts} +0 -0
  237. /package/dist/credits/{credit-ledger.bench.d.ts → trial-balance-cron.test.d.ts} +0 -0
  238. /package/dist/{credits/credit-transaction-repository.test.d.ts → monetization/credits/trial-balance-cron.test.d.ts} +0 -0
@@ -1,9 +1,9 @@
1
1
  import { Hono } from "hono";
2
2
  import type { AuthEnv } from "../../auth/index.js";
3
- import type { ICreditLedger } from "../../credits/index.js";
3
+ import type { ILedger } from "../../credits/index.js";
4
4
  import type { AdminAuditLogger } from "./admin-audit-helper.js";
5
5
  /**
6
6
  * Create admin credit API routes.
7
7
  * Pass a ledger directly or a factory for lazy init.
8
8
  */
9
- export declare function createAdminCreditApiRoutes(ledgerOrFactory: ICreditLedger | (() => ICreditLedger), auditLogger?: () => AdminAuditLogger): Hono<AuthEnv>;
9
+ export declare function createAdminCreditApiRoutes(ledgerOrFactory: ILedger | (() => ILedger), auditLogger?: () => AdminAuditLogger): Hono<AuthEnv>;
@@ -53,7 +53,10 @@ export function createAdminCreditApiRoutes(ledgerOrFactory, auditLogger) {
53
53
  const adminUser = user?.id ?? "unknown";
54
54
  let result;
55
55
  try {
56
- result = await ledger.credit(tenant, Credit.fromCents(amountCents), "admin_grant", reason, undefined, undefined, adminUser);
56
+ result = await ledger.credit(tenant, Credit.fromCents(amountCents), "admin_grant", {
57
+ description: reason,
58
+ createdBy: adminUser,
59
+ });
57
60
  }
58
61
  catch (err) {
59
62
  safeAuditLog(auditLogger, {
@@ -106,7 +109,7 @@ export function createAdminCreditApiRoutes(ledgerOrFactory, auditLogger) {
106
109
  const adminUser = user?.id ?? "unknown";
107
110
  let result;
108
111
  try {
109
- result = await ledger.credit(tenant, Credit.fromCents(amountCents), "admin_grant", reason);
112
+ result = await ledger.credit(tenant, Credit.fromCents(amountCents), "admin_grant", { description: reason });
110
113
  }
111
114
  catch (err) {
112
115
  safeAuditLog(auditLogger, {
@@ -163,10 +166,12 @@ export function createAdminCreditApiRoutes(ledgerOrFactory, auditLogger) {
163
166
  let result;
164
167
  try {
165
168
  if (amountCents >= 0) {
166
- result = await ledger.credit(tenant, Credit.fromCents(amountCents), "promo", reason);
169
+ result = await ledger.credit(tenant, Credit.fromCents(amountCents), "correction", { description: reason });
167
170
  }
168
171
  else {
169
- result = await ledger.debit(tenant, Credit.fromCents(Math.abs(amountCents)), "correction", reason);
172
+ result = await ledger.debit(tenant, Credit.fromCents(Math.abs(amountCents)), "correction", {
173
+ description: reason,
174
+ });
170
175
  }
171
176
  }
172
177
  catch (err) {
@@ -1,8 +1,8 @@
1
1
  import { Hono } from "hono";
2
- import type { ICreditLedger } from "../../credits/credit-ledger.js";
2
+ import type { ILedger } from "../../credits/ledger.js";
3
3
  /**
4
4
  * Create quota routes.
5
5
  *
6
6
  * @param ledgerFactory - Factory returning the credit ledger
7
7
  */
8
- export declare function createQuotaRoutes(ledgerFactory: () => ICreditLedger): Hono;
8
+ export declare function createQuotaRoutes(ledgerFactory: () => ILedger): Hono;
@@ -1,9 +1,9 @@
1
1
  import { Hono } from "hono";
2
2
  import type { Pool } from "pg";
3
- import type { ICreditLedger } from "../../credits/index.js";
3
+ import type { ILedger } from "../../credits/index.js";
4
4
  export interface VerifyEmailRouteDeps {
5
5
  pool: Pool;
6
- creditLedger: ICreditLedger;
6
+ creditLedger: ILedger;
7
7
  }
8
8
  export interface VerifyEmailRouteConfig {
9
9
  /** UI origin for redirect URLs (default: http://localhost:3001) */
@@ -16,4 +16,4 @@ export declare function createVerifyEmailRoutes(deps: VerifyEmailRouteDeps, conf
16
16
  /**
17
17
  * Create verify-email routes with factory functions (for lazy init).
18
18
  */
19
- export declare function createVerifyEmailRoutesLazy(poolFactory: () => Pool, creditLedgerFactory: () => ICreditLedger, config?: VerifyEmailRouteConfig): Hono;
19
+ export declare function createVerifyEmailRoutesLazy(poolFactory: () => Pool, creditLedgerFactory: () => ILedger, config?: VerifyEmailRouteConfig): Hono;
@@ -1,9 +1,9 @@
1
- import type { CreditLedger } from "@wopr-network/platform-core/credits";
1
+ import type { ILedger } from "@wopr-network/platform-core/credits";
2
2
  import type { SnapshotManager } from "./snapshot-manager.js";
3
3
  import type { Snapshot, Tier } from "./types.js";
4
4
  export interface OnDemandSnapshotServiceConfig {
5
5
  manager: SnapshotManager;
6
- ledger: CreditLedger;
6
+ ledger: ILedger;
7
7
  }
8
8
  export interface CreateSnapshotParams {
9
9
  tenant: string;
@@ -1,13 +1,13 @@
1
- import type { ICreditLedger } from "../../credits/credit-ledger.js";
1
+ import type { ILedger } from "../../credits/ledger.js";
2
2
  import type { IWebhookSeenRepository } from "../webhook-seen-repository.js";
3
3
  import type { PayRamChargeRepository } from "./charge-store.js";
4
4
  import type { PayRamWebhookPayload, PayRamWebhookResult } from "./types.js";
5
5
  export interface PayRamWebhookDeps {
6
6
  chargeStore: PayRamChargeRepository;
7
- creditLedger: ICreditLedger;
7
+ creditLedger: ILedger;
8
8
  replayGuard: IWebhookSeenRepository;
9
9
  /** Called after credits are purchased — consumer can reactivate suspended resources. Returns reactivated resource IDs. */
10
- onCreditsPurchased?: (tenantId: string, ledger: ICreditLedger) => Promise<string[]>;
10
+ onCreditsPurchased?: (tenantId: string, ledger: ILedger) => Promise<string[]>;
11
11
  }
12
12
  /**
13
13
  * Process a PayRam webhook event.
@@ -36,7 +36,11 @@ export async function handlePayRamWebhook(deps, payload) {
36
36
  // For OVER_FILLED, we still credit the requested amount — the
37
37
  // overpayment stays in the PayRam wallet as a buffer.
38
38
  const creditCents = charge.amountUsdCents;
39
- await creditLedger.credit(charge.tenantId, Credit.fromCents(creditCents), "purchase", `Crypto credit purchase via PayRam (ref: ${payload.reference_id}, ${payload.currency ?? "crypto"})`, `payram:${payload.reference_id}`, "payram");
39
+ await creditLedger.credit(charge.tenantId, Credit.fromCents(creditCents), "purchase", {
40
+ description: `Crypto credit purchase via PayRam (ref: ${payload.reference_id}, ${payload.currency ?? "crypto"})`,
41
+ referenceId: `payram:${payload.reference_id}`,
42
+ fundingSource: "payram",
43
+ });
40
44
  await chargeStore.markCredited(payload.reference_id);
41
45
  // Reactivate suspended resources after credit purchase.
42
46
  let reactivatedBots;
@@ -5,7 +5,7 @@
5
5
  * no-op status, idempotency, replay guard, and bot reactivation.
6
6
  */
7
7
  import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
8
- import { CreditLedger } from "../../credits/credit-ledger.js";
8
+ import { DrizzleLedger } from "../../credits/ledger.js";
9
9
  import { createTestDb, truncateAllTables } from "../../test/db.js";
10
10
  import { DrizzleWebhookSeenRepository } from "../drizzle-webhook-seen-repository.js";
11
11
  import { noOpReplayGuard } from "../webhook-seen-repository.js";
@@ -37,7 +37,8 @@ describe("handlePayRamWebhook", () => {
37
37
  beforeEach(async () => {
38
38
  await truncateAllTables(pool);
39
39
  chargeStore = new PayRamChargeRepository(db);
40
- creditLedger = new CreditLedger(db);
40
+ creditLedger = new DrizzleLedger(db);
41
+ await creditLedger.seedSystemAccounts();
41
42
  deps = { chargeStore, creditLedger, replayGuard: noOpReplayGuard };
42
43
  // Create a default test charge
43
44
  await chargeStore.create("ref-test-001", "tenant-a", 2500);
@@ -60,12 +61,12 @@ describe("handlePayRamWebhook", () => {
60
61
  const history = await creditLedger.history("tenant-a");
61
62
  expect(history).toHaveLength(1);
62
63
  expect(history[0].referenceId).toBe("payram:ref-test-001");
63
- expect(history[0].type).toBe("purchase");
64
+ expect(history[0].entryType).toBe("purchase");
64
65
  });
65
66
  it("records fundingSource as payram", async () => {
66
67
  await handlePayRamWebhook(deps, makePayload({ status: "FILLED" }));
67
68
  const history = await creditLedger.history("tenant-a");
68
- expect(history[0].fundingSource).toBe("payram");
69
+ expect(history[0].metadata?.fundingSource).toBe("payram");
69
70
  });
70
71
  it("marks the charge as credited after FILLED", async () => {
71
72
  await handlePayRamWebhook(deps, makePayload({ status: "FILLED" }));
@@ -1,6 +1,6 @@
1
1
  import type Stripe from "stripe";
2
2
  import type { IAutoTopupEventLogRepository } from "../../credits/auto-topup-event-log-repository.js";
3
- import type { ICreditLedger } from "../../credits/credit-ledger.js";
3
+ import type { ILedger } from "../../credits/ledger.js";
4
4
  import { type ChargeOpts, type ChargeResult, type CheckoutOpts, type CheckoutSession, type Invoice, type IPaymentProcessor, type PortalOpts, type SavedPaymentMethod, type SetupResult, type WebhookResult } from "../payment-processor.js";
5
5
  import type { CreditPriceMap } from "./credit-prices.js";
6
6
  import type { ITenantCustomerRepository } from "./tenant-store.js";
@@ -18,7 +18,7 @@ export interface StripePaymentProcessorDeps {
18
18
  tenantRepo: ITenantCustomerRepository;
19
19
  webhookSecret: string;
20
20
  priceMap?: CreditPriceMap;
21
- creditLedger: ICreditLedger;
21
+ creditLedger: ILedger;
22
22
  autoTopupEventLog?: IAutoTopupEventLogRepository;
23
23
  /** Consumer-supplied webhook handler (handles domain-specific event processing). */
24
24
  webhookHandler?: (event: Stripe.Event) => Promise<StripeWebhookHandlerResult>;
@@ -45,6 +45,7 @@ function createMocks() {
45
45
  buildCustomerIdMap: vi.fn(),
46
46
  };
47
47
  const creditLedger = {
48
+ post: vi.fn(),
48
49
  credit: vi.fn(),
49
50
  debit: vi.fn(),
50
51
  balance: vi.fn(),
@@ -55,6 +56,12 @@ function createMocks() {
55
56
  expiredCredits: vi.fn(),
56
57
  lifetimeSpend: vi.fn(),
57
58
  lifetimeSpendBatch: vi.fn().mockResolvedValue(new Map()),
59
+ trialBalance: vi.fn(),
60
+ accountBalance: vi.fn(),
61
+ seedSystemAccounts: vi.fn(),
62
+ existsByReferenceIdLike: vi.fn(),
63
+ sumPurchasesForPeriod: vi.fn(),
64
+ getActiveTenantIdsInWindow: vi.fn(),
58
65
  };
59
66
  const autoTopupEventLog = {
60
67
  writeEvent: vi.fn(),
@@ -23,7 +23,7 @@ export interface ITenantCustomerRepository {
23
23
  * All billing operations look up the processor customer via this store.
24
24
  *
25
25
  * Note: No subscription tracking — WOPR uses credits, not subscriptions.
26
- * Credit balances are managed by ICreditLedger / DrizzleCreditLedger.
26
+ * Credit balances are managed by ILedger / DrizzleCreditLedger.
27
27
  */
28
28
  export declare class DrizzleTenantCustomerRepository implements ITenantCustomerRepository {
29
29
  private readonly db;
@@ -7,7 +7,7 @@ import { tenantCustomers } from "../../db/schema/tenant-customers.js";
7
7
  * All billing operations look up the processor customer via this store.
8
8
  *
9
9
  * Note: No subscription tracking — WOPR uses credits, not subscriptions.
10
- * Credit balances are managed by ICreditLedger / DrizzleCreditLedger.
10
+ * Credit balances are managed by ILedger / DrizzleCreditLedger.
11
11
  */
12
12
  export class DrizzleTenantCustomerRepository {
13
13
  db;
@@ -1,14 +1,14 @@
1
1
  import Stripe from "stripe";
2
2
  import type { IAutoTopupEventLogRepository } from "./auto-topup-event-log-repository.js";
3
3
  import type { Credit } from "./credit.js";
4
- import type { ICreditLedger } from "./credit-ledger.js";
4
+ import type { ILedger } from "./ledger.js";
5
5
  import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
6
6
  /** After this many consecutive Stripe failures, the auto-topup mode is disabled. */
7
7
  export declare const MAX_CONSECUTIVE_FAILURES = 3;
8
8
  export interface AutoTopupChargeDeps {
9
9
  stripe: Stripe;
10
10
  tenantRepo: ITenantCustomerRepository;
11
- creditLedger: ICreditLedger;
11
+ creditLedger: ILedger;
12
12
  eventLogRepo: IAutoTopupEventLogRepository;
13
13
  }
14
14
  export interface AutoTopupChargeResult {
@@ -113,7 +113,11 @@ export async function chargeAutoTopup(deps, tenantId, amount, source) {
113
113
  // 5. Credit the ledger (idempotent via referenceId = PI ID)
114
114
  try {
115
115
  if (!(await deps.creditLedger.hasReferenceId(paymentIntent.id))) {
116
- await deps.creditLedger.credit(tenantId, amount, "purchase", `Auto-topup (${source})`, paymentIntent.id, "stripe");
116
+ await deps.creditLedger.credit(tenantId, amount, "purchase", {
117
+ description: `Auto-topup (${source})`,
118
+ referenceId: paymentIntent.id,
119
+ fundingSource: "stripe",
120
+ });
117
121
  }
118
122
  }
119
123
  catch (err) {
@@ -6,7 +6,7 @@ import { createTestDb, truncateAllTables } from "../test/db.js";
6
6
  import { chargeAutoTopup, MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
7
7
  import { DrizzleAutoTopupEventLogRepository } from "./auto-topup-event-log-repository.js";
8
8
  import { Credit } from "./credit.js";
9
- import { CreditLedger } from "./credit-ledger.js";
9
+ import { DrizzleLedger } from "./ledger.js";
10
10
  function mockStripe(overrides) {
11
11
  const piId = overrides?.paymentIntentId ?? `pi_${crypto.randomUUID()}`;
12
12
  return {
@@ -47,7 +47,8 @@ describe("chargeAutoTopup", () => {
47
47
  });
48
48
  beforeEach(async () => {
49
49
  await truncateAllTables(pool);
50
- ledger = new CreditLedger(db);
50
+ ledger = new DrizzleLedger(db);
51
+ await ledger.seedSystemAccounts();
51
52
  });
52
53
  it("charges Stripe and credits ledger on success", async () => {
53
54
  const stripe = mockStripe();
@@ -63,8 +64,8 @@ describe("chargeAutoTopup", () => {
63
64
  expect(result.paymentReference).toEqual(expect.any(String));
64
65
  expect((await ledger.balance("t1")).toCents()).toBe(500);
65
66
  const history = await ledger.history("t1");
66
- expect(history[0].type).toBe("purchase");
67
- expect(history[0].fundingSource).toBe("stripe");
67
+ expect(history[0].entryType).toBe("purchase");
68
+ expect(history[0].metadata?.fundingSource).toBe("stripe");
68
69
  });
69
70
  it("writes success event to credit_auto_topup log", async () => {
70
71
  const stripe = mockStripe();
@@ -1,10 +1,10 @@
1
1
  import type { AutoTopupChargeResult } from "./auto-topup-charge.js";
2
2
  import type { IAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
3
3
  import type { Credit } from "./credit.js";
4
- import type { ICreditLedger } from "./credit-ledger.js";
4
+ import type { ILedger } from "./ledger.js";
5
5
  export interface UsageTopupDeps {
6
6
  settingsRepo: IAutoTopupSettingsRepository;
7
- creditLedger: ICreditLedger;
7
+ creditLedger: ILedger;
8
8
  /** Injected charge function (allows mocking in tests). */
9
9
  chargeAutoTopup: (tenantId: string, amount: Credit, source: string) => Promise<AutoTopupChargeResult>;
10
10
  /** Optional tenant status check. If provided and returns non-null, skip the charge. */
@@ -3,7 +3,7 @@ import { createTestDb, truncateAllTables } from "../test/db.js";
3
3
  import { DrizzleAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
4
4
  import { maybeTriggerUsageTopup } from "./auto-topup-usage.js";
5
5
  import { Credit } from "./credit.js";
6
- import { CreditLedger } from "./credit-ledger.js";
6
+ import { DrizzleLedger } from "./ledger.js";
7
7
  describe("maybeTriggerUsageTopup", () => {
8
8
  let pool;
9
9
  let db;
@@ -17,7 +17,8 @@ describe("maybeTriggerUsageTopup", () => {
17
17
  });
18
18
  beforeEach(async () => {
19
19
  await truncateAllTables(pool);
20
- ledger = new CreditLedger(db);
20
+ ledger = new DrizzleLedger(db);
21
+ await ledger.seedSystemAccounts();
21
22
  settingsRepo = new DrizzleAutoTopupSettingsRepository(db);
22
23
  });
23
24
  it("does nothing when tenant has no auto-topup settings", async () => {
@@ -28,7 +29,11 @@ describe("maybeTriggerUsageTopup", () => {
28
29
  });
29
30
  it("does nothing when usage_enabled is false", async () => {
30
31
  await settingsRepo.upsert("t1", { usageEnabled: false });
31
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
32
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
33
+ description: "buy",
34
+ referenceId: "ref-1",
35
+ fundingSource: "stripe",
36
+ });
32
37
  const mockCharge = vi.fn();
33
38
  const deps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
34
39
  await maybeTriggerUsageTopup(deps, "t1");
@@ -40,7 +45,11 @@ describe("maybeTriggerUsageTopup", () => {
40
45
  usageThreshold: Credit.fromCents(100),
41
46
  usageTopup: Credit.fromCents(500),
42
47
  });
43
- await ledger.credit("t1", Credit.fromCents(200), "purchase", "buy", "ref-1", "stripe");
48
+ await ledger.credit("t1", Credit.fromCents(200), "purchase", {
49
+ description: "buy",
50
+ referenceId: "ref-1",
51
+ fundingSource: "stripe",
52
+ });
44
53
  const mockCharge = vi.fn();
45
54
  const deps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
46
55
  await maybeTriggerUsageTopup(deps, "t1");
@@ -52,7 +61,11 @@ describe("maybeTriggerUsageTopup", () => {
52
61
  usageThreshold: Credit.fromCents(100),
53
62
  usageTopup: Credit.fromCents(500),
54
63
  });
55
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
64
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
65
+ description: "buy",
66
+ referenceId: "ref-1",
67
+ fundingSource: "stripe",
68
+ });
56
69
  const mockCharge = vi.fn().mockResolvedValue({ success: true, paymentReference: "pi_123" });
57
70
  const deps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
58
71
  await maybeTriggerUsageTopup(deps, "t1");
@@ -61,7 +74,11 @@ describe("maybeTriggerUsageTopup", () => {
61
74
  it("skips when charge is already in-flight", async () => {
62
75
  await settingsRepo.upsert("t1", { usageEnabled: true, usageThreshold: Credit.fromCents(100) });
63
76
  await settingsRepo.setUsageChargeInFlight("t1", true);
64
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
77
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
78
+ description: "buy",
79
+ referenceId: "ref-1",
80
+ fundingSource: "stripe",
81
+ });
65
82
  const mockCharge = vi.fn();
66
83
  const deps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
67
84
  await maybeTriggerUsageTopup(deps, "t1");
@@ -74,7 +91,11 @@ describe("maybeTriggerUsageTopup", () => {
74
91
  usageThreshold: Credit.fromCents(500),
75
92
  usageTopup: Credit.fromCents(2000),
76
93
  });
77
- await ledger.credit("t1", Credit.fromCents(100), "purchase", "buy", "ref-1", "stripe");
94
+ await ledger.credit("t1", Credit.fromCents(100), "purchase", {
95
+ description: "buy",
96
+ referenceId: "ref-1",
97
+ fundingSource: "stripe",
98
+ });
78
99
  const mockCharge = vi.fn().mockResolvedValue({ success: true, paymentReference: "pi_race" });
79
100
  const deps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
80
101
  // Fire two concurrent calls — both see balance < threshold,
@@ -93,7 +114,11 @@ describe("maybeTriggerUsageTopup", () => {
93
114
  usageThreshold: Credit.fromCents(100),
94
115
  usageTopup: Credit.fromCents(500),
95
116
  });
96
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
117
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
118
+ description: "buy",
119
+ referenceId: "ref-1",
120
+ fundingSource: "stripe",
121
+ });
97
122
  const mockCharge = vi.fn().mockResolvedValue({ success: true, paymentReference: "pi_123" });
98
123
  const deps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
99
124
  // First call — triggers charge, flag set then cleared
@@ -111,7 +136,11 @@ describe("maybeTriggerUsageTopup", () => {
111
136
  usageThreshold: Credit.fromCents(100),
112
137
  usageTopup: Credit.fromCents(500),
113
138
  });
114
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
139
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
140
+ description: "buy",
141
+ referenceId: "ref-1",
142
+ fundingSource: "stripe",
143
+ });
115
144
  const mockCharge = vi
116
145
  .fn()
117
146
  .mockRejectedValueOnce(new Error("Stripe network error"))
@@ -134,7 +163,11 @@ describe("maybeTriggerUsageTopup", () => {
134
163
  });
135
164
  await settingsRepo.incrementUsageFailures("t1");
136
165
  await settingsRepo.incrementUsageFailures("t1");
137
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
166
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
167
+ description: "buy",
168
+ referenceId: "ref-1",
169
+ fundingSource: "stripe",
170
+ });
138
171
  const mockCharge = vi.fn().mockResolvedValue({ success: true });
139
172
  const deps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
140
173
  await maybeTriggerUsageTopup(deps, "t1");
@@ -146,7 +179,11 @@ describe("maybeTriggerUsageTopup", () => {
146
179
  usageThreshold: Credit.fromCents(100),
147
180
  usageTopup: Credit.fromCents(500),
148
181
  });
149
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
182
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
183
+ description: "buy",
184
+ referenceId: "ref-1",
185
+ fundingSource: "stripe",
186
+ });
150
187
  const mockCharge = vi.fn().mockResolvedValue({ success: false, error: "declined" });
151
188
  const deps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
152
189
  await maybeTriggerUsageTopup(deps, "t1");
@@ -172,7 +209,11 @@ describe("maybeTriggerUsageTopup", () => {
172
209
  });
173
210
  await settingsRepo.incrementUsageFailures("t1");
174
211
  await settingsRepo.incrementUsageFailures("t1");
175
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
212
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
213
+ description: "buy",
214
+ referenceId: "ref-1",
215
+ fundingSource: "stripe",
216
+ });
176
217
  const mockCharge = vi.fn().mockResolvedValue({ success: false, error: "declined" });
177
218
  const deps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
178
219
  await maybeTriggerUsageTopup(deps, "t1");
@@ -1,6 +1,6 @@
1
- import type { ICreditLedger } from "./credit-ledger.js";
1
+ import type { ILedger } from "./ledger.js";
2
2
  export interface CreditExpiryCronConfig {
3
- ledger: ICreditLedger;
3
+ ledger: ILedger;
4
4
  /** Current time as ISO-8601 string. */
5
5
  now: string;
6
6
  }
@@ -1,5 +1,5 @@
1
1
  import { logger } from "../config/logger.js";
2
- import { InsufficientBalanceError } from "./credit-ledger.js";
2
+ import { InsufficientBalanceError } from "./ledger.js";
3
3
  /**
4
4
  * Sweep expired credit grants and debit the original grant amount
5
5
  * (or remaining balance if partially consumed).
@@ -23,7 +23,10 @@ export async function runCreditExpiryCron(cfg) {
23
23
  }
24
24
  // Debit the lesser of the original grant amount or current balance
25
25
  const debitAmount = balance.lessThan(grant.amount) ? balance : grant.amount;
26
- await cfg.ledger.debit(grant.tenantId, debitAmount, "credit_expiry", `Expired credit grant reclaimed: ${grant.id}`, `expiry:${grant.id}`);
26
+ await cfg.ledger.debit(grant.tenantId, debitAmount, "credit_expiry", {
27
+ description: `Expired credit grant reclaimed: ${grant.entryId}`,
28
+ referenceId: `expiry:${grant.entryId}`,
29
+ });
27
30
  result.processed++;
28
31
  if (!result.expired.includes(grant.tenantId)) {
29
32
  result.expired.push(grant.tenantId);
@@ -35,8 +38,8 @@ export async function runCreditExpiryCron(cfg) {
35
38
  }
36
39
  else {
37
40
  const msg = err instanceof Error ? err.message : String(err);
38
- logger.error("Credit expiry failed", { tenantId: grant.tenantId, txnId: grant.id, error: msg });
39
- result.errors.push(`${grant.tenantId}:${grant.id}: ${msg}`);
41
+ logger.error("Credit expiry failed", { tenantId: grant.tenantId, entryId: grant.entryId, error: msg });
42
+ result.errors.push(`${grant.tenantId}:${grant.entryId}: ${msg}`);
40
43
  }
41
44
  }
42
45
  }
@@ -2,20 +2,21 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
2
2
  import { createTestDb, truncateAllTables } from "../test/db.js";
3
3
  import { Credit } from "./credit.js";
4
4
  import { runCreditExpiryCron } from "./credit-expiry-cron.js";
5
- import { DrizzleCreditLedger } from "./credit-ledger.js";
5
+ import { DrizzleLedger } from "./ledger.js";
6
6
  describe("runCreditExpiryCron", () => {
7
7
  let pool;
8
8
  let ledger;
9
9
  beforeAll(async () => {
10
10
  const { db, pool: p } = await createTestDb();
11
11
  pool = p;
12
- ledger = new DrizzleCreditLedger(db);
12
+ ledger = new DrizzleLedger(db);
13
13
  });
14
14
  afterAll(async () => {
15
15
  await pool.close();
16
16
  });
17
17
  beforeEach(async () => {
18
18
  await truncateAllTables(pool);
19
+ await ledger.seedSystemAccounts();
19
20
  });
20
21
  // All tests pass an explicit `now` parameter — hardcoded dates are time-independent
21
22
  // because runCreditExpiryCron never reads the system clock.
@@ -26,7 +27,11 @@ describe("runCreditExpiryCron", () => {
26
27
  expect(result.errors).toEqual([]);
27
28
  });
28
29
  it("debits expired promotional credit grant", async () => {
29
- await ledger.credit("tenant-1", Credit.fromCents(500), "promo", "New user bonus", "promo:tenant-1", undefined, undefined, "2026-01-10T00:00:00Z");
30
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", {
31
+ description: "New user bonus",
32
+ referenceId: "promo:tenant-1",
33
+ expiresAt: "2026-01-10T00:00:00Z",
34
+ });
30
35
  const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
31
36
  expect(result.processed).toBe(1);
32
37
  expect(result.expired).toContain("tenant-1");
@@ -34,29 +39,41 @@ describe("runCreditExpiryCron", () => {
34
39
  expect(balance.toCents()).toBe(0);
35
40
  });
36
41
  it("does not debit non-expired credits", async () => {
37
- await ledger.credit("tenant-1", Credit.fromCents(500), "promo", "Future bonus", "promo:tenant-1-future", undefined, undefined, "2026-02-01T00:00:00Z");
42
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", {
43
+ description: "Future bonus",
44
+ referenceId: "promo:tenant-1-future",
45
+ expiresAt: "2026-02-01T00:00:00Z",
46
+ });
38
47
  const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
39
48
  expect(result.processed).toBe(0);
40
49
  const balance = await ledger.balance("tenant-1");
41
50
  expect(balance.toCents()).toBe(500);
42
51
  });
43
52
  it("does not debit credits without expires_at", async () => {
44
- await ledger.credit("tenant-1", Credit.fromCents(500), "purchase", "Top-up");
53
+ await ledger.credit("tenant-1", Credit.fromCents(500), "purchase", { description: "Top-up" });
45
54
  const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
46
55
  expect(result.processed).toBe(0);
47
56
  const balance = await ledger.balance("tenant-1");
48
57
  expect(balance.toCents()).toBe(500);
49
58
  });
50
59
  it("only debits up to available balance when partially consumed", async () => {
51
- await ledger.credit("tenant-1", Credit.fromCents(500), "promo", "Promo", "promo:partial", undefined, undefined, "2026-01-10T00:00:00Z");
52
- await ledger.debit("tenant-1", Credit.fromCents(300), "bot_runtime", "Runtime");
60
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", {
61
+ description: "Promo",
62
+ referenceId: "promo:partial",
63
+ expiresAt: "2026-01-10T00:00:00Z",
64
+ });
65
+ await ledger.debit("tenant-1", Credit.fromCents(300), "bot_runtime", { description: "Runtime" });
53
66
  const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
54
67
  expect(result.processed).toBe(1);
55
68
  const balance = await ledger.balance("tenant-1");
56
69
  expect(balance.toCents()).toBe(0);
57
70
  });
58
71
  it("is idempotent -- does not double-debit on second run", async () => {
59
- await ledger.credit("tenant-1", Credit.fromCents(500), "promo", "Promo", "promo:idemp", undefined, undefined, "2026-01-10T00:00:00Z");
72
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", {
73
+ description: "Promo",
74
+ referenceId: "promo:idemp",
75
+ expiresAt: "2026-01-10T00:00:00Z",
76
+ });
60
77
  await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
61
78
  const balanceAfterFirst = await ledger.balance("tenant-1");
62
79
  const result2 = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
@@ -56,7 +56,7 @@ export declare class InsufficientBalanceError extends Error {
56
56
  requestedAmount: Credit;
57
57
  constructor(currentBalance: Credit, requestedAmount: Credit);
58
58
  }
59
- export interface ICreditLedger {
59
+ export interface ILedger {
60
60
  credit(tenantId: string, amount: Credit, type: CreditType, description?: string, referenceId?: string, fundingSource?: string, attributedUserId?: string, expiresAt?: string): Promise<CreditTransaction>;
61
61
  expiredCredits(now: string): Promise<Array<{
62
62
  id: string;
@@ -82,7 +82,7 @@ export interface ICreditLedger {
82
82
  * creditBalances row is always consistent with the sum of creditTransactions.
83
83
  * Zero raw SQL in application code.
84
84
  */
85
- export declare class DrizzleCreditLedger implements ICreditLedger {
85
+ export declare class DrizzleCreditLedger implements ILedger {
86
86
  private readonly db;
87
87
  constructor(db: PlatformDb);
88
88
  /**
@@ -289,5 +289,5 @@ export class DrizzleCreditLedger {
289
289
  .where(sql `${creditBalances.balance} > 0`);
290
290
  }
291
291
  }
292
- // Backward-compat alias — callers using 'new CreditLedger(db)' continue to work.
292
+ // Backward-compat alias — callers using 'new DrizzleLedger(db)' continue to work.
293
293
  export { DrizzleCreditLedger as CreditLedger };
@@ -1,9 +1,7 @@
1
1
  import { Credit } from "./credit.js";
2
- import type { ICreditLedger } from "./credit-ledger.js";
3
- import type { ICreditTransactionRepository } from "./credit-transaction-repository.js";
2
+ import type { ILedger } from "./ledger.js";
4
3
  export interface DividendCronConfig {
5
- creditTransactionRepo: ICreditTransactionRepository;
6
- ledger: ICreditLedger;
4
+ ledger: ILedger;
7
5
  /** Fraction of daily purchases matched as dividend pool. Default 1.0 (100%). */
8
6
  matchRate: number;
9
7
  /** The date to compute dividend for, as YYYY-MM-DD string. Typically yesterday. */
@@ -21,8 +19,8 @@ export interface DividendCronResult {
21
19
  * Compute and distribute the community dividend for a given day.
22
20
  *
23
21
  * 1. Check idempotency — skip if already run for this date.
24
- * 2. Sum all 'purchase' transactions for the target date.
25
- * 3. Find all tenants with a 'purchase' transaction in the last 7 days.
22
+ * 2. Sum all 'purchase' entries for the target date.
23
+ * 3. Find all tenants with a 'purchase' entry in the last 7 days.
26
24
  * 4. Compute pool = sum × matchRate, per-user share = floor(pool / activeCount).
27
25
  * 5. Credit each active tenant with their share.
28
26
  */