@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,4 +1,4 @@
1
- import type { CreditLedger } from "@wopr-network/platform-core/credits";
1
+ import type { ILedger } from "@wopr-network/platform-core/credits";
2
2
  import { Credit } from "@wopr-network/platform-core/credits";
3
3
  import { describe, expect, it, vi } from "vitest";
4
4
  import {
@@ -48,8 +48,8 @@ function makeManager(overrides: Record<string, unknown> = {}): SnapshotManager {
48
48
  } as unknown as SnapshotManager;
49
49
  }
50
50
 
51
- function makeLedger(balanceCents = 100): CreditLedger {
52
- return { balance: vi.fn().mockResolvedValue(Credit.fromCents(balanceCents)) } as unknown as CreditLedger;
51
+ function makeLedger(balanceCents = 100): ILedger {
52
+ return { balance: vi.fn().mockResolvedValue(Credit.fromCents(balanceCents)) } as unknown as ILedger;
53
53
  }
54
54
 
55
55
  function makeService(managerOverrides: Record<string, unknown> = {}, balanceCents = 100) {
@@ -1,11 +1,11 @@
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
  import { SNAPSHOT_TIER_POLICIES, STORAGE_CHARGE_PER_GB_MONTH, STORAGE_COST_PER_GB_MONTH } from "./types.js";
5
5
 
6
6
  export interface OnDemandSnapshotServiceConfig {
7
7
  manager: SnapshotManager;
8
- ledger: CreditLedger;
8
+ ledger: ILedger;
9
9
  }
10
10
 
11
11
  export interface CreateSnapshotParams {
@@ -25,7 +25,7 @@ export interface CreateSnapshotResult {
25
25
 
26
26
  export class OnDemandSnapshotService {
27
27
  private readonly manager: SnapshotManager;
28
- private readonly ledger: CreditLedger;
28
+ private readonly ledger: ILedger;
29
29
 
30
30
  constructor(cfg: OnDemandSnapshotServiceConfig) {
31
31
  this.manager = cfg.manager;
@@ -7,7 +7,7 @@
7
7
 
8
8
  import type { PGlite } from "@electric-sql/pglite";
9
9
  import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
10
- import { CreditLedger } from "../../credits/credit-ledger.js";
10
+ import { DrizzleLedger } from "../../credits/ledger.js";
11
11
  import type { PlatformDb } from "../../db/index.js";
12
12
  import { createTestDb, truncateAllTables } from "../../test/db.js";
13
13
  import { DrizzleWebhookSeenRepository } from "../drizzle-webhook-seen-repository.js";
@@ -41,13 +41,15 @@ afterAll(async () => {
41
41
 
42
42
  describe("handlePayRamWebhook", () => {
43
43
  let chargeStore: PayRamChargeRepository;
44
- let creditLedger: CreditLedger;
44
+ let creditLedger: DrizzleLedger;
45
45
  let deps: PayRamWebhookDeps;
46
46
 
47
47
  beforeEach(async () => {
48
48
  await truncateAllTables(pool);
49
49
  chargeStore = new PayRamChargeRepository(db);
50
- creditLedger = new CreditLedger(db);
50
+ creditLedger = new DrizzleLedger(db);
51
+
52
+ await creditLedger.seedSystemAccounts();
51
53
  deps = { chargeStore, creditLedger, replayGuard: noOpReplayGuard };
52
54
 
53
55
  // Create a default test charge
@@ -77,14 +79,14 @@ describe("handlePayRamWebhook", () => {
77
79
  const history = await creditLedger.history("tenant-a");
78
80
  expect(history).toHaveLength(1);
79
81
  expect(history[0].referenceId).toBe("payram:ref-test-001");
80
- expect(history[0].type).toBe("purchase");
82
+ expect(history[0].entryType).toBe("purchase");
81
83
  });
82
84
 
83
85
  it("records fundingSource as payram", async () => {
84
86
  await handlePayRamWebhook(deps, makePayload({ status: "FILLED" }));
85
87
 
86
88
  const history = await creditLedger.history("tenant-a");
87
- expect(history[0].fundingSource).toBe("payram");
89
+ expect(history[0].metadata?.fundingSource).toBe("payram");
88
90
  });
89
91
 
90
92
  it("marks the charge as credited after FILLED", async () => {
@@ -1,15 +1,15 @@
1
1
  import { Credit } from "../../credits/credit.js";
2
- import type { ICreditLedger } from "../../credits/credit-ledger.js";
2
+ import type { ILedger } from "../../credits/ledger.js";
3
3
  import type { IWebhookSeenRepository } from "../webhook-seen-repository.js";
4
4
  import type { PayRamChargeRepository } from "./charge-store.js";
5
5
  import type { PayRamWebhookPayload, PayRamWebhookResult } from "./types.js";
6
6
 
7
7
  export interface PayRamWebhookDeps {
8
8
  chargeStore: PayRamChargeRepository;
9
- creditLedger: ICreditLedger;
9
+ creditLedger: ILedger;
10
10
  replayGuard: IWebhookSeenRepository;
11
11
  /** Called after credits are purchased — consumer can reactivate suspended resources. Returns reactivated resource IDs. */
12
- onCreditsPurchased?: (tenantId: string, ledger: ICreditLedger) => Promise<string[]>;
12
+ onCreditsPurchased?: (tenantId: string, ledger: ILedger) => Promise<string[]>;
13
13
  }
14
14
 
15
15
  /**
@@ -57,14 +57,11 @@ export async function handlePayRamWebhook(
57
57
  // overpayment stays in the PayRam wallet as a buffer.
58
58
  const creditCents = charge.amountUsdCents;
59
59
 
60
- await creditLedger.credit(
61
- charge.tenantId,
62
- Credit.fromCents(creditCents),
63
- "purchase",
64
- `Crypto credit purchase via PayRam (ref: ${payload.reference_id}, ${payload.currency ?? "crypto"})`,
65
- `payram:${payload.reference_id}`,
66
- "payram",
67
- );
60
+ await creditLedger.credit(charge.tenantId, Credit.fromCents(creditCents), "purchase", {
61
+ description: `Crypto credit purchase via PayRam (ref: ${payload.reference_id}, ${payload.currency ?? "crypto"})`,
62
+ referenceId: `payram:${payload.reference_id}`,
63
+ fundingSource: "payram",
64
+ });
68
65
 
69
66
  await chargeStore.markCredited(payload.reference_id);
70
67
 
@@ -2,7 +2,7 @@ import type Stripe from "stripe";
2
2
  import { beforeEach, describe, expect, it, vi } from "vitest";
3
3
  import type { IAutoTopupEventLogRepository } from "../../credits/auto-topup-event-log-repository.js";
4
4
  import { Credit } from "../../credits/credit.js";
5
- import type { CreditTransaction, ICreditLedger } from "../../credits/credit-ledger.js";
5
+ import type { ILedger, JournalEntry } from "../../credits/ledger.js";
6
6
  import { PaymentMethodOwnershipError } from "../payment-processor.js";
7
7
  import type { CreditPriceMap } from "./credit-prices.js";
8
8
  import { StripePaymentProcessor } from "./stripe-payment-processor.js";
@@ -58,7 +58,8 @@ function createMocks() {
58
58
  buildCustomerIdMap: vi.fn(),
59
59
  };
60
60
 
61
- const creditLedger: ICreditLedger = {
61
+ const creditLedger: ILedger = {
62
+ post: vi.fn(),
62
63
  credit: vi.fn(),
63
64
  debit: vi.fn(),
64
65
  balance: vi.fn(),
@@ -69,6 +70,12 @@ function createMocks() {
69
70
  expiredCredits: vi.fn(),
70
71
  lifetimeSpend: vi.fn(),
71
72
  lifetimeSpendBatch: vi.fn().mockResolvedValue(new Map()),
73
+ trialBalance: vi.fn(),
74
+ accountBalance: vi.fn(),
75
+ seedSystemAccounts: vi.fn(),
76
+ existsByReferenceIdLike: vi.fn(),
77
+ sumPurchasesForPeriod: vi.fn(),
78
+ getActiveTenantIdsInWindow: vi.fn(),
72
79
  };
73
80
 
74
81
  const autoTopupEventLog: IAutoTopupEventLogRepository = {
@@ -330,7 +337,7 @@ describe("StripePaymentProcessor", () => {
330
337
  status: "succeeded",
331
338
  } as unknown as Stripe.Response<Stripe.PaymentIntent>);
332
339
  vi.mocked(mocks.creditLedger.hasReferenceId).mockResolvedValue(false);
333
- vi.mocked(mocks.creditLedger.credit).mockResolvedValue({} as unknown as CreditTransaction);
340
+ vi.mocked(mocks.creditLedger.credit).mockResolvedValue({} as unknown as JournalEntry);
334
341
  vi.mocked(mocks.autoTopupEventLog.writeEvent).mockResolvedValue(undefined);
335
342
 
336
343
  const result = await processor.charge({
@@ -2,7 +2,7 @@ import type Stripe from "stripe";
2
2
  import { chargeAutoTopup } from "../../credits/auto-topup-charge.js";
3
3
  import type { IAutoTopupEventLogRepository } from "../../credits/auto-topup-event-log-repository.js";
4
4
  import { Credit } from "../../credits/credit.js";
5
- import type { ICreditLedger } from "../../credits/credit-ledger.js";
5
+ import type { ILedger } from "../../credits/ledger.js";
6
6
  import {
7
7
  type ChargeOpts,
8
8
  type ChargeResult,
@@ -37,7 +37,7 @@ export interface StripePaymentProcessorDeps {
37
37
  tenantRepo: ITenantCustomerRepository;
38
38
  webhookSecret: string;
39
39
  priceMap?: CreditPriceMap;
40
- creditLedger: ICreditLedger;
40
+ creditLedger: ILedger;
41
41
  autoTopupEventLog?: IAutoTopupEventLogRepository;
42
42
  /** Consumer-supplied webhook handler (handles domain-specific event processing). */
43
43
  webhookHandler?: (event: Stripe.Event) => Promise<StripeWebhookHandlerResult>;
@@ -50,7 +50,7 @@ export class StripePaymentProcessor implements IPaymentProcessor {
50
50
  private readonly tenantRepo: ITenantCustomerRepository;
51
51
  private readonly webhookSecret: string;
52
52
  private readonly priceMap: CreditPriceMap;
53
- private readonly creditLedger: ICreditLedger;
53
+ private readonly creditLedger: ILedger;
54
54
  private readonly autoTopupEventLog?: IAutoTopupEventLogRepository;
55
55
  private readonly webhookHandler?: (event: Stripe.Event) => Promise<StripeWebhookHandlerResult>;
56
56
 
@@ -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 class DrizzleTenantCustomerRepository implements ITenantCustomerRepository {
29
29
  constructor(private readonly db: PlatformDb) {}
@@ -8,7 +8,7 @@ import { createTestDb, truncateAllTables } from "../test/db.js";
8
8
  import { type AutoTopupChargeDeps, chargeAutoTopup, MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
9
9
  import { DrizzleAutoTopupEventLogRepository } from "./auto-topup-event-log-repository.js";
10
10
  import { Credit } from "./credit.js";
11
- import { CreditLedger } from "./credit-ledger.js";
11
+ import { DrizzleLedger } from "./ledger.js";
12
12
  import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
13
13
 
14
14
  function mockStripe(overrides?: {
@@ -49,7 +49,7 @@ function mockTenantStore(stripeCustomerId = "cus_123") {
49
49
  describe("chargeAutoTopup", () => {
50
50
  let pool: PGlite;
51
51
  let db: PlatformDb;
52
- let ledger: CreditLedger;
52
+ let ledger: DrizzleLedger;
53
53
 
54
54
  beforeAll(async () => {
55
55
  ({ db, pool } = await createTestDb());
@@ -61,7 +61,9 @@ describe("chargeAutoTopup", () => {
61
61
 
62
62
  beforeEach(async () => {
63
63
  await truncateAllTables(pool);
64
- ledger = new CreditLedger(db);
64
+ ledger = new DrizzleLedger(db);
65
+
66
+ await ledger.seedSystemAccounts();
65
67
  });
66
68
 
67
69
  it("charges Stripe and credits ledger on success", async () => {
@@ -80,8 +82,8 @@ describe("chargeAutoTopup", () => {
80
82
  expect(result.paymentReference).toEqual(expect.any(String));
81
83
  expect((await ledger.balance("t1")).toCents()).toBe(500);
82
84
  const history = await ledger.history("t1");
83
- expect(history[0].type).toBe("purchase");
84
- expect(history[0].fundingSource).toBe("stripe");
85
+ expect(history[0].entryType).toBe("purchase");
86
+ expect(history[0].metadata?.fundingSource).toBe("stripe");
85
87
  });
86
88
 
87
89
  it("writes success event to credit_auto_topup log", async () => {
@@ -2,7 +2,7 @@ import Stripe from "stripe";
2
2
  import { logger } from "../config/logger.js";
3
3
  import type { IAutoTopupEventLogRepository } from "./auto-topup-event-log-repository.js";
4
4
  import type { Credit } from "./credit.js";
5
- import type { ICreditLedger } from "./credit-ledger.js";
5
+ import type { ILedger } from "./ledger.js";
6
6
  import type { ITenantCustomerRepository } from "./tenant-customer-repository.js";
7
7
 
8
8
  /** After this many consecutive Stripe failures, the auto-topup mode is disabled. */
@@ -11,7 +11,7 @@ export const MAX_CONSECUTIVE_FAILURES = 3;
11
11
  export interface AutoTopupChargeDeps {
12
12
  stripe: Stripe;
13
13
  tenantRepo: ITenantCustomerRepository;
14
- creditLedger: ICreditLedger;
14
+ creditLedger: ILedger;
15
15
  eventLogRepo: IAutoTopupEventLogRepository;
16
16
  }
17
17
 
@@ -137,14 +137,11 @@ export async function chargeAutoTopup(
137
137
  // 5. Credit the ledger (idempotent via referenceId = PI ID)
138
138
  try {
139
139
  if (!(await deps.creditLedger.hasReferenceId(paymentIntent.id))) {
140
- await deps.creditLedger.credit(
141
- tenantId,
142
- amount,
143
- "purchase",
144
- `Auto-topup (${source})`,
145
- paymentIntent.id,
146
- "stripe",
147
- );
140
+ await deps.creditLedger.credit(tenantId, amount, "purchase", {
141
+ description: `Auto-topup (${source})`,
142
+ referenceId: paymentIntent.id,
143
+ fundingSource: "stripe",
144
+ });
148
145
  }
149
146
  } catch (err) {
150
147
  const message = `Stripe charge ${paymentIntent.id} succeeded but credit grant failed: ${err instanceof Error ? err.message : String(err)}`;
@@ -5,12 +5,12 @@ import { createTestDb, truncateAllTables } from "../test/db.js";
5
5
  import { DrizzleAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
6
6
  import { maybeTriggerUsageTopup, type UsageTopupDeps } from "./auto-topup-usage.js";
7
7
  import { Credit } from "./credit.js";
8
- import { CreditLedger } from "./credit-ledger.js";
8
+ import { DrizzleLedger } from "./ledger.js";
9
9
 
10
10
  describe("maybeTriggerUsageTopup", () => {
11
11
  let pool: PGlite;
12
12
  let db: PlatformDb;
13
- let ledger: CreditLedger;
13
+ let ledger: DrizzleLedger;
14
14
  let settingsRepo: DrizzleAutoTopupSettingsRepository;
15
15
 
16
16
  beforeAll(async () => {
@@ -23,7 +23,9 @@ describe("maybeTriggerUsageTopup", () => {
23
23
 
24
24
  beforeEach(async () => {
25
25
  await truncateAllTables(pool);
26
- ledger = new CreditLedger(db);
26
+ ledger = new DrizzleLedger(db);
27
+
28
+ await ledger.seedSystemAccounts();
27
29
  settingsRepo = new DrizzleAutoTopupSettingsRepository(db);
28
30
  });
29
31
 
@@ -37,7 +39,11 @@ describe("maybeTriggerUsageTopup", () => {
37
39
 
38
40
  it("does nothing when usage_enabled is false", async () => {
39
41
  await settingsRepo.upsert("t1", { usageEnabled: false });
40
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
42
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
43
+ description: "buy",
44
+ referenceId: "ref-1",
45
+ fundingSource: "stripe",
46
+ });
41
47
  const mockCharge = vi.fn();
42
48
  const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
43
49
 
@@ -51,7 +57,11 @@ describe("maybeTriggerUsageTopup", () => {
51
57
  usageThreshold: Credit.fromCents(100),
52
58
  usageTopup: Credit.fromCents(500),
53
59
  });
54
- await ledger.credit("t1", Credit.fromCents(200), "purchase", "buy", "ref-1", "stripe");
60
+ await ledger.credit("t1", Credit.fromCents(200), "purchase", {
61
+ description: "buy",
62
+ referenceId: "ref-1",
63
+ fundingSource: "stripe",
64
+ });
55
65
  const mockCharge = vi.fn();
56
66
  const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
57
67
 
@@ -65,7 +75,11 @@ describe("maybeTriggerUsageTopup", () => {
65
75
  usageThreshold: Credit.fromCents(100),
66
76
  usageTopup: Credit.fromCents(500),
67
77
  });
68
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
78
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
79
+ description: "buy",
80
+ referenceId: "ref-1",
81
+ fundingSource: "stripe",
82
+ });
69
83
  const mockCharge = vi.fn().mockResolvedValue({ success: true, paymentReference: "pi_123" });
70
84
  const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
71
85
 
@@ -76,7 +90,11 @@ describe("maybeTriggerUsageTopup", () => {
76
90
  it("skips when charge is already in-flight", async () => {
77
91
  await settingsRepo.upsert("t1", { usageEnabled: true, usageThreshold: Credit.fromCents(100) });
78
92
  await settingsRepo.setUsageChargeInFlight("t1", true);
79
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
93
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
94
+ description: "buy",
95
+ referenceId: "ref-1",
96
+ fundingSource: "stripe",
97
+ });
80
98
  const mockCharge = vi.fn();
81
99
  const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
82
100
 
@@ -91,7 +109,11 @@ describe("maybeTriggerUsageTopup", () => {
91
109
  usageThreshold: Credit.fromCents(500),
92
110
  usageTopup: Credit.fromCents(2000),
93
111
  });
94
- await ledger.credit("t1", Credit.fromCents(100), "purchase", "buy", "ref-1", "stripe");
112
+ await ledger.credit("t1", Credit.fromCents(100), "purchase", {
113
+ description: "buy",
114
+ referenceId: "ref-1",
115
+ fundingSource: "stripe",
116
+ });
95
117
 
96
118
  const mockCharge = vi.fn().mockResolvedValue({ success: true, paymentReference: "pi_race" });
97
119
  const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
@@ -115,7 +137,11 @@ describe("maybeTriggerUsageTopup", () => {
115
137
  usageThreshold: Credit.fromCents(100),
116
138
  usageTopup: Credit.fromCents(500),
117
139
  });
118
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
140
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
141
+ description: "buy",
142
+ referenceId: "ref-1",
143
+ fundingSource: "stripe",
144
+ });
119
145
  const mockCharge = vi.fn().mockResolvedValue({ success: true, paymentReference: "pi_123" });
120
146
  const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
121
147
 
@@ -137,7 +163,11 @@ describe("maybeTriggerUsageTopup", () => {
137
163
  usageThreshold: Credit.fromCents(100),
138
164
  usageTopup: Credit.fromCents(500),
139
165
  });
140
- 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
+ });
141
171
  const mockCharge = vi
142
172
  .fn()
143
173
  .mockRejectedValueOnce(new Error("Stripe network error"))
@@ -164,7 +194,11 @@ describe("maybeTriggerUsageTopup", () => {
164
194
  });
165
195
  await settingsRepo.incrementUsageFailures("t1");
166
196
  await settingsRepo.incrementUsageFailures("t1");
167
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
197
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
198
+ description: "buy",
199
+ referenceId: "ref-1",
200
+ fundingSource: "stripe",
201
+ });
168
202
  const mockCharge = vi.fn().mockResolvedValue({ success: true });
169
203
  const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
170
204
 
@@ -178,7 +212,11 @@ describe("maybeTriggerUsageTopup", () => {
178
212
  usageThreshold: Credit.fromCents(100),
179
213
  usageTopup: Credit.fromCents(500),
180
214
  });
181
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
215
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
216
+ description: "buy",
217
+ referenceId: "ref-1",
218
+ fundingSource: "stripe",
219
+ });
182
220
  const mockCharge = vi.fn().mockResolvedValue({ success: false, error: "declined" });
183
221
  const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
184
222
 
@@ -210,7 +248,11 @@ describe("maybeTriggerUsageTopup", () => {
210
248
  });
211
249
  await settingsRepo.incrementUsageFailures("t1");
212
250
  await settingsRepo.incrementUsageFailures("t1");
213
- await ledger.credit("t1", Credit.fromCents(50), "purchase", "buy", "ref-1", "stripe");
251
+ await ledger.credit("t1", Credit.fromCents(50), "purchase", {
252
+ description: "buy",
253
+ referenceId: "ref-1",
254
+ fundingSource: "stripe",
255
+ });
214
256
  const mockCharge = vi.fn().mockResolvedValue({ success: false, error: "declined" });
215
257
  const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
216
258
 
@@ -3,11 +3,11 @@ import type { AutoTopupChargeResult } from "./auto-topup-charge.js";
3
3
  import { MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
4
4
  import type { IAutoTopupSettingsRepository } from "./auto-topup-settings-repository.js";
5
5
  import type { Credit } from "./credit.js";
6
- import type { ICreditLedger } from "./credit-ledger.js";
6
+ import type { ILedger } from "./ledger.js";
7
7
 
8
8
  export interface UsageTopupDeps {
9
9
  settingsRepo: IAutoTopupSettingsRepository;
10
- creditLedger: ICreditLedger;
10
+ creditLedger: ILedger;
11
11
  /** Injected charge function (allows mocking in tests). */
12
12
  chargeAutoTopup: (tenantId: string, amount: Credit, source: string) => Promise<AutoTopupChargeResult>;
13
13
  /** Optional tenant status check. If provided and returns non-null, skip the charge. */
@@ -3,16 +3,16 @@ import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
3
3
  import { createTestDb, truncateAllTables } from "../test/db.js";
4
4
  import { Credit } from "./credit.js";
5
5
  import { runCreditExpiryCron } from "./credit-expiry-cron.js";
6
- import { DrizzleCreditLedger } from "./credit-ledger.js";
6
+ import { DrizzleLedger } from "./ledger.js";
7
7
 
8
8
  describe("runCreditExpiryCron", () => {
9
9
  let pool: PGlite;
10
- let ledger: DrizzleCreditLedger;
10
+ let ledger: DrizzleLedger;
11
11
 
12
12
  beforeAll(async () => {
13
13
  const { db, pool: p } = await createTestDb();
14
14
  pool = p;
15
- ledger = new DrizzleCreditLedger(db);
15
+ ledger = new DrizzleLedger(db);
16
16
  });
17
17
 
18
18
  afterAll(async () => {
@@ -21,6 +21,7 @@ describe("runCreditExpiryCron", () => {
21
21
 
22
22
  beforeEach(async () => {
23
23
  await truncateAllTables(pool);
24
+ await ledger.seedSystemAccounts();
24
25
  });
25
26
 
26
27
  // All tests pass an explicit `now` parameter — hardcoded dates are time-independent
@@ -33,16 +34,11 @@ describe("runCreditExpiryCron", () => {
33
34
  });
34
35
 
35
36
  it("debits expired promotional credit grant", async () => {
36
- await ledger.credit(
37
- "tenant-1",
38
- Credit.fromCents(500),
39
- "promo",
40
- "New user bonus",
41
- "promo:tenant-1",
42
- undefined,
43
- undefined,
44
- "2026-01-10T00:00:00Z",
45
- );
37
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", {
38
+ description: "New user bonus",
39
+ referenceId: "promo:tenant-1",
40
+ expiresAt: "2026-01-10T00:00:00Z",
41
+ });
46
42
 
47
43
  const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
48
44
  expect(result.processed).toBe(1);
@@ -53,16 +49,11 @@ describe("runCreditExpiryCron", () => {
53
49
  });
54
50
 
55
51
  it("does not debit non-expired credits", async () => {
56
- await ledger.credit(
57
- "tenant-1",
58
- Credit.fromCents(500),
59
- "promo",
60
- "Future bonus",
61
- "promo:tenant-1-future",
62
- undefined,
63
- undefined,
64
- "2026-02-01T00:00:00Z",
65
- );
52
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", {
53
+ description: "Future bonus",
54
+ referenceId: "promo:tenant-1-future",
55
+ expiresAt: "2026-02-01T00:00:00Z",
56
+ });
66
57
 
67
58
  const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
68
59
  expect(result.processed).toBe(0);
@@ -72,7 +63,7 @@ describe("runCreditExpiryCron", () => {
72
63
  });
73
64
 
74
65
  it("does not debit credits without expires_at", async () => {
75
- await ledger.credit("tenant-1", Credit.fromCents(500), "purchase", "Top-up");
66
+ await ledger.credit("tenant-1", Credit.fromCents(500), "purchase", { description: "Top-up" });
76
67
 
77
68
  const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
78
69
  expect(result.processed).toBe(0);
@@ -82,17 +73,12 @@ describe("runCreditExpiryCron", () => {
82
73
  });
83
74
 
84
75
  it("only debits up to available balance when partially consumed", async () => {
85
- await ledger.credit(
86
- "tenant-1",
87
- Credit.fromCents(500),
88
- "promo",
89
- "Promo",
90
- "promo:partial",
91
- undefined,
92
- undefined,
93
- "2026-01-10T00:00:00Z",
94
- );
95
- await ledger.debit("tenant-1", Credit.fromCents(300), "bot_runtime", "Runtime");
76
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", {
77
+ description: "Promo",
78
+ referenceId: "promo:partial",
79
+ expiresAt: "2026-01-10T00:00:00Z",
80
+ });
81
+ await ledger.debit("tenant-1", Credit.fromCents(300), "bot_runtime", { description: "Runtime" });
96
82
 
97
83
  const result = await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
98
84
  expect(result.processed).toBe(1);
@@ -102,16 +88,11 @@ describe("runCreditExpiryCron", () => {
102
88
  });
103
89
 
104
90
  it("is idempotent -- does not double-debit on second run", async () => {
105
- await ledger.credit(
106
- "tenant-1",
107
- Credit.fromCents(500),
108
- "promo",
109
- "Promo",
110
- "promo:idemp",
111
- undefined,
112
- undefined,
113
- "2026-01-10T00:00:00Z",
114
- );
91
+ await ledger.credit("tenant-1", Credit.fromCents(500), "promo", {
92
+ description: "Promo",
93
+ referenceId: "promo:idemp",
94
+ expiresAt: "2026-01-10T00:00:00Z",
95
+ });
115
96
 
116
97
  await runCreditExpiryCron({ ledger, now: "2026-01-15T00:00:00Z" });
117
98
  const balanceAfterFirst = await ledger.balance("tenant-1");
@@ -1,9 +1,9 @@
1
1
  import { logger } from "../config/logger.js";
2
- import type { ICreditLedger } from "./credit-ledger.js";
3
- import { InsufficientBalanceError } from "./credit-ledger.js";
2
+ import type { ILedger } from "./ledger.js";
3
+ import { InsufficientBalanceError } from "./ledger.js";
4
4
 
5
5
  export interface CreditExpiryCronConfig {
6
- ledger: ICreditLedger;
6
+ ledger: ILedger;
7
7
  /** Current time as ISO-8601 string. */
8
8
  now: string;
9
9
  }
@@ -42,13 +42,10 @@ export async function runCreditExpiryCron(cfg: CreditExpiryCronConfig): Promise<
42
42
  // Debit the lesser of the original grant amount or current balance
43
43
  const debitAmount = balance.lessThan(grant.amount) ? balance : grant.amount;
44
44
 
45
- await cfg.ledger.debit(
46
- grant.tenantId,
47
- debitAmount,
48
- "credit_expiry",
49
- `Expired credit grant reclaimed: ${grant.id}`,
50
- `expiry:${grant.id}`,
51
- );
45
+ await cfg.ledger.debit(grant.tenantId, debitAmount, "credit_expiry", {
46
+ description: `Expired credit grant reclaimed: ${grant.entryId}`,
47
+ referenceId: `expiry:${grant.entryId}`,
48
+ });
52
49
 
53
50
  result.processed++;
54
51
  if (!result.expired.includes(grant.tenantId)) {
@@ -59,8 +56,8 @@ export async function runCreditExpiryCron(cfg: CreditExpiryCronConfig): Promise<
59
56
  result.skippedZeroBalance++;
60
57
  } else {
61
58
  const msg = err instanceof Error ? err.message : String(err);
62
- logger.error("Credit expiry failed", { tenantId: grant.tenantId, txnId: grant.id, error: msg });
63
- result.errors.push(`${grant.tenantId}:${grant.id}: ${msg}`);
59
+ logger.error("Credit expiry failed", { tenantId: grant.tenantId, entryId: grant.entryId, error: msg });
60
+ result.errors.push(`${grant.tenantId}:${grant.entryId}: ${msg}`);
64
61
  }
65
62
  }
66
63
  }
@@ -95,7 +95,7 @@ export class InsufficientBalanceError extends Error {
95
95
  }
96
96
  }
97
97
 
98
- export interface ICreditLedger {
98
+ export interface ILedger {
99
99
  credit(
100
100
  tenantId: string,
101
101
  amount: Credit,
@@ -135,7 +135,7 @@ export interface ICreditLedger {
135
135
  * creditBalances row is always consistent with the sum of creditTransactions.
136
136
  * Zero raw SQL in application code.
137
137
  */
138
- export class DrizzleCreditLedger implements ICreditLedger {
138
+ export class DrizzleCreditLedger implements ILedger {
139
139
  constructor(private readonly db: PlatformDb) {}
140
140
 
141
141
  /**
@@ -446,5 +446,5 @@ export class DrizzleCreditLedger implements ICreditLedger {
446
446
  }
447
447
  }
448
448
 
449
- // Backward-compat alias — callers using 'new CreditLedger(db)' continue to work.
449
+ // Backward-compat alias — callers using 'new DrizzleLedger(db)' continue to work.
450
450
  export { DrizzleCreditLedger as CreditLedger };