@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.
- package/dist/api/routes/admin-credits.d.ts +2 -2
- package/dist/api/routes/admin-credits.js +9 -4
- package/dist/api/routes/quota.d.ts +2 -2
- package/dist/api/routes/verify-email.d.ts +3 -3
- package/dist/backup/on-demand-snapshot-service.d.ts +2 -2
- package/dist/billing/payram/webhook.d.ts +3 -3
- package/dist/billing/payram/webhook.js +5 -1
- package/dist/billing/payram/webhook.test.js +5 -4
- package/dist/billing/stripe/stripe-payment-processor.d.ts +2 -2
- package/dist/billing/stripe/stripe-payment-processor.test.js +7 -0
- package/dist/billing/stripe/tenant-store.d.ts +1 -1
- package/dist/billing/stripe/tenant-store.js +1 -1
- package/dist/credits/auto-topup-charge.d.ts +2 -2
- package/dist/credits/auto-topup-charge.js +5 -1
- package/dist/credits/auto-topup-charge.test.js +5 -4
- package/dist/credits/auto-topup-usage.d.ts +2 -2
- package/dist/credits/auto-topup-usage.test.js +53 -12
- package/dist/credits/credit-expiry-cron.d.ts +2 -2
- package/dist/credits/credit-expiry-cron.js +7 -4
- package/dist/credits/credit-expiry-cron.test.js +25 -8
- package/dist/credits/credit-ledger.d.ts +2 -2
- package/dist/credits/credit-ledger.js +1 -1
- package/dist/credits/dividend-cron.d.ts +4 -6
- package/dist/credits/dividend-cron.js +10 -16
- package/dist/credits/dividend-cron.test.js +31 -44
- package/dist/credits/dividend-repository.js +19 -22
- package/dist/credits/dividend-repository.test.js +4 -3
- package/dist/credits/index.d.ts +4 -2
- package/dist/credits/index.js +2 -1
- package/dist/credits/ledger.d.ts +195 -0
- package/dist/credits/ledger.js +561 -0
- package/dist/credits/ledger.test.js +418 -0
- package/dist/credits/signup-grant.d.ts +2 -2
- package/dist/credits/signup-grant.js +4 -4
- package/dist/credits/signup-grant.test.js +5 -3
- package/dist/credits/trial-balance-cron.d.ts +19 -0
- package/dist/credits/trial-balance-cron.js +30 -0
- package/dist/credits/trial-balance-cron.test.js +55 -0
- package/dist/db/schema/gateway-service-keys.d.ts +109 -0
- package/dist/db/schema/gateway-service-keys.js +18 -0
- package/dist/db/schema/index.d.ts +2 -0
- package/dist/db/schema/index.js +2 -0
- package/dist/db/schema/ledger.d.ts +442 -0
- package/dist/db/schema/ledger.js +76 -0
- package/dist/gateway/credit-gate.d.ts +2 -2
- package/dist/gateway/credit-gate.js +5 -1
- package/dist/gateway/credit-gate.test.js +35 -33
- package/dist/gateway/gateway-routes.test.js +1 -1
- package/dist/gateway/index.d.ts +2 -0
- package/dist/gateway/index.js +1 -0
- package/dist/gateway/protocol/anthropic.js +1 -1
- package/dist/gateway/protocol/deps.d.ts +5 -5
- package/dist/gateway/protocol/openai.js +1 -1
- package/dist/gateway/proxy.d.ts +4 -4
- package/dist/gateway/route-mounting.test.js +1 -1
- package/dist/gateway/service-key-auth.d.ts +1 -1
- package/dist/gateway/service-key-auth.js +1 -1
- package/dist/gateway/service-key-repository.d.ts +27 -0
- package/dist/gateway/service-key-repository.js +64 -0
- package/dist/gateway/types.d.ts +5 -5
- package/dist/metering/reconciliation-cron.test.js +9 -8
- package/dist/metering/reconciliation-repository.js +12 -10
- package/dist/metering/reconciliation-repository.test.js +9 -8
- package/dist/monetization/affiliate/affiliate-admin-repository.js +10 -8
- package/dist/monetization/affiliate/affiliate-admin-repository.test.js +32 -13
- package/dist/monetization/affiliate/credit-match.d.ts +2 -2
- package/dist/monetization/affiliate/credit-match.js +4 -1
- package/dist/monetization/affiliate/credit-match.test.js +58 -13
- package/dist/monetization/affiliate/new-user-bonus.d.ts +2 -2
- package/dist/monetization/affiliate/new-user-bonus.js +4 -1
- package/dist/monetization/affiliate/new-user-bonus.test.js +4 -3
- package/dist/monetization/credits/auto-topup-charge.d.ts +2 -2
- package/dist/monetization/credits/auto-topup-charge.js +5 -1
- package/dist/monetization/credits/auto-topup-charge.test.js +5 -4
- package/dist/monetization/credits/auto-topup-usage.d.ts +2 -2
- package/dist/monetization/credits/auto-topup-usage.test.js +53 -12
- package/dist/monetization/credits/bot-billing.d.ts +3 -3
- package/dist/monetization/credits/bot-billing.test.js +18 -5
- package/dist/monetization/credits/credit-expiry-cron.test.js +25 -8
- package/dist/monetization/credits/dividend-cron.d.ts +2 -4
- package/dist/monetization/credits/dividend-cron.js +7 -4
- package/dist/monetization/credits/dividend-cron.test.js +26 -46
- package/dist/monetization/credits/dividend-repository.js +15 -24
- package/dist/monetization/credits/dividend-repository.test.js +4 -3
- package/dist/monetization/credits/index.d.ts +2 -2
- package/dist/monetization/credits/index.js +1 -1
- package/dist/monetization/credits/member-usage.test.js +23 -10
- package/dist/monetization/credits/phone-billing.d.ts +2 -2
- package/dist/monetization/credits/phone-billing.js +5 -1
- package/dist/monetization/credits/phone-billing.test.js +9 -12
- package/dist/monetization/credits/runtime-cron.d.ts +2 -2
- package/dist/monetization/credits/runtime-cron.js +32 -8
- package/dist/monetization/credits/runtime-cron.test.js +28 -27
- package/dist/monetization/credits/runtime-scheduler.d.ts +2 -2
- package/dist/monetization/credits/runtime-scheduler.test.js +1 -1
- package/dist/monetization/credits/signup-grant.test.js +5 -3
- package/dist/monetization/credits/storage-tier-cron.test.js +3 -2
- package/dist/monetization/credits/trial-balance-cron.test.js +42 -0
- package/dist/monetization/feature-gate.d.ts +3 -3
- package/dist/monetization/index.d.ts +3 -3
- package/dist/monetization/index.js +1 -1
- package/dist/monetization/metering/reconciliation-cron.test.js +9 -8
- package/dist/monetization/metering/reconciliation-repository.js +11 -10
- package/dist/monetization/metering/reconciliation-repository.test.js +9 -8
- package/dist/monetization/payram/webhook.d.ts +2 -2
- package/dist/monetization/payram/webhook.js +5 -1
- package/dist/monetization/payram/webhook.test.js +5 -4
- package/dist/monetization/promotions/engine.d.ts +2 -2
- package/dist/monetization/promotions/engine.js +4 -1
- package/dist/monetization/promotions/engine.test.js +3 -1
- package/dist/monetization/repository-types.d.ts +1 -1
- package/dist/monetization/socket/socket.d.ts +3 -3
- package/dist/monetization/stripe/stripe-payment-processor.d.ts +2 -2
- package/dist/monetization/stripe/stripe-payment-processor.test.js +7 -0
- package/dist/monetization/stripe/webhook.d.ts +2 -2
- package/dist/monetization/stripe/webhook.js +70 -6
- package/dist/monetization/stripe/webhook.test.js +20 -15
- package/dist/onboarding/onboarding-service.d.ts +2 -2
- package/dist/onboarding/onboarding-service.js +6 -2
- package/drizzle/migrations/0002_gateway_service_keys.sql +14 -0
- package/drizzle/migrations/0003_double_entry_ledger.sql +82 -0
- package/drizzle/migrations/meta/_journal.json +14 -0
- package/package.json +1 -1
- package/src/api/routes/admin-credits.ts +11 -14
- package/src/api/routes/quota.ts +2 -2
- package/src/api/routes/verify-email.ts +4 -4
- package/src/backup/on-demand-snapshot-service.test.ts +3 -3
- package/src/backup/on-demand-snapshot-service.ts +3 -3
- package/src/billing/payram/webhook.test.ts +7 -5
- package/src/billing/payram/webhook.ts +8 -11
- package/src/billing/stripe/stripe-payment-processor.test.ts +10 -3
- package/src/billing/stripe/stripe-payment-processor.ts +3 -3
- package/src/billing/stripe/tenant-store.ts +1 -1
- package/src/credits/auto-topup-charge.test.ts +7 -5
- package/src/credits/auto-topup-charge.ts +7 -10
- package/src/credits/auto-topup-usage.test.ts +55 -13
- package/src/credits/auto-topup-usage.ts +2 -2
- package/src/credits/credit-expiry-cron.test.ts +26 -45
- package/src/credits/credit-expiry-cron.ts +9 -12
- package/src/credits/credit-ledger.ts +3 -3
- package/src/credits/dividend-cron.test.ts +38 -45
- package/src/credits/dividend-cron.ts +12 -26
- package/src/credits/dividend-repository.test.ts +4 -3
- package/src/credits/dividend-repository.ts +21 -23
- package/src/credits/index.ts +23 -4
- package/src/credits/ledger.test.ts +514 -0
- package/src/credits/ledger.ts +851 -0
- package/src/credits/signup-grant.test.ts +7 -4
- package/src/credits/signup-grant.ts +6 -12
- package/src/credits/trial-balance-cron.test.ts +68 -0
- package/src/credits/trial-balance-cron.ts +46 -0
- package/src/db/schema/gateway-service-keys.ts +23 -0
- package/src/db/schema/index.ts +2 -0
- package/src/db/schema/ledger.ts +94 -0
- package/src/gateway/credit-gate-wiring.test.ts +3 -3
- package/src/gateway/credit-gate.test.ts +35 -33
- package/src/gateway/credit-gate.ts +6 -10
- package/src/gateway/gateway-routes.test.ts +6 -6
- package/src/gateway/index.ts +2 -0
- package/src/gateway/protocol/anthropic.ts +2 -2
- package/src/gateway/protocol/deps.ts +5 -5
- package/src/gateway/protocol/openai.ts +2 -2
- package/src/gateway/proxy.ts +4 -4
- package/src/gateway/route-mounting.test.ts +3 -3
- package/src/gateway/service-key-auth.ts +4 -2
- package/src/gateway/service-key-repository.ts +87 -0
- package/src/gateway/types.ts +5 -5
- package/src/metering/reconciliation-cron.test.ts +10 -9
- package/src/metering/reconciliation-repository.test.ts +10 -9
- package/src/metering/reconciliation-repository.ts +14 -11
- package/src/monetization/affiliate/affiliate-admin-repository.test.ts +32 -19
- package/src/monetization/affiliate/affiliate-admin-repository.ts +16 -8
- package/src/monetization/affiliate/credit-match.test.ts +60 -14
- package/src/monetization/affiliate/credit-match.ts +6 -9
- package/src/monetization/affiliate/new-user-bonus.test.ts +6 -4
- package/src/monetization/affiliate/new-user-bonus.ts +6 -9
- package/src/monetization/credits/auto-topup-charge.test.ts +7 -5
- package/src/monetization/credits/auto-topup-charge.ts +7 -10
- package/src/monetization/credits/auto-topup-usage.test.ts +55 -13
- package/src/monetization/credits/auto-topup-usage.ts +2 -2
- package/src/monetization/credits/bot-billing.test.ts +20 -6
- package/src/monetization/credits/bot-billing.ts +3 -3
- package/src/monetization/credits/credit-expiry-cron.test.ts +26 -45
- package/src/monetization/credits/dividend-cron.test.ts +34 -48
- package/src/monetization/credits/dividend-cron.ts +9 -14
- package/src/monetization/credits/dividend-repository.test.ts +4 -3
- package/src/monetization/credits/dividend-repository.ts +19 -25
- package/src/monetization/credits/index.ts +4 -4
- package/src/monetization/credits/member-usage.test.ts +25 -11
- package/src/monetization/credits/phone-billing.test.ts +18 -26
- package/src/monetization/credits/phone-billing.ts +7 -10
- package/src/monetization/credits/runtime-cron.test.ts +29 -28
- package/src/monetization/credits/runtime-cron.ts +34 -58
- package/src/monetization/credits/runtime-scheduler.test.ts +1 -1
- package/src/monetization/credits/runtime-scheduler.ts +2 -2
- package/src/monetization/credits/signup-grant.test.ts +7 -4
- package/src/monetization/credits/storage-tier-cron.test.ts +5 -3
- package/src/monetization/credits/trial-balance-cron.test.ts +52 -0
- package/src/monetization/feature-gate.ts +3 -3
- package/src/monetization/index.ts +4 -4
- package/src/monetization/metering/reconciliation-cron.test.ts +10 -9
- package/src/monetization/metering/reconciliation-repository.test.ts +11 -9
- package/src/monetization/metering/reconciliation-repository.ts +13 -11
- package/src/monetization/payram/webhook.test.ts +7 -5
- package/src/monetization/payram/webhook.ts +7 -10
- package/src/monetization/promotions/engine.test.ts +6 -5
- package/src/monetization/promotions/engine.ts +6 -3
- package/src/monetization/repository-types.ts +1 -1
- package/src/monetization/socket/socket.ts +4 -4
- package/src/monetization/stripe/stripe-payment-processor.test.ts +10 -3
- package/src/monetization/stripe/stripe-payment-processor.ts +3 -3
- package/src/monetization/stripe/webhook.test.ts +22 -16
- package/src/monetization/stripe/webhook.ts +75 -50
- package/src/onboarding/onboarding-service.ts +8 -11
- package/dist/credits/credit-ledger-extra.test.js +0 -40
- package/dist/credits/credit-ledger.bench.js +0 -33
- package/dist/credits/credit-ledger.test.d.ts +0 -4
- package/dist/credits/credit-ledger.test.js +0 -203
- package/dist/credits/credit-transaction-repository.test.js +0 -232
- package/dist/monetization/credits/credit-ledger-extra.test.d.ts +0 -1
- package/dist/monetization/credits/credit-ledger-extra.test.js +0 -39
- package/dist/monetization/credits/credit-ledger.bench.d.ts +0 -1
- package/dist/monetization/credits/credit-ledger.bench.js +0 -32
- package/dist/monetization/credits/credit-ledger.test.d.ts +0 -4
- package/dist/monetization/credits/credit-ledger.test.js +0 -202
- package/dist/monetization/credits/credit-transaction-repository.test.d.ts +0 -1
- package/dist/monetization/credits/credit-transaction-repository.test.js +0 -232
- package/src/credits/credit-ledger-extra.test.ts +0 -57
- package/src/credits/credit-ledger.bench.ts +0 -56
- package/src/credits/credit-ledger.test.ts +0 -276
- package/src/credits/credit-transaction-repository.test.ts +0 -274
- package/src/monetization/credits/credit-ledger-extra.test.ts +0 -56
- package/src/monetization/credits/credit-ledger.bench.ts +0 -55
- package/src/monetization/credits/credit-ledger.test.ts +0 -275
- package/src/monetization/credits/credit-transaction-repository.test.ts +0 -274
- /package/dist/credits/{credit-ledger-extra.test.d.ts → ledger.test.d.ts} +0 -0
- /package/dist/credits/{credit-ledger.bench.d.ts → trial-balance-cron.test.d.ts} +0 -0
- /package/dist/{credits/credit-transaction-repository.test.d.ts → monetization/credits/trial-balance-cron.test.d.ts} +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PGlite } from "@electric-sql/pglite";
|
|
2
|
-
import { Credit,
|
|
2
|
+
import { Credit, DrizzleLedger } from "@wopr-network/platform-core/credits";
|
|
3
3
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
4
4
|
import type { DrizzleDb } from "../../db/index.js";
|
|
5
5
|
import { createTestDb, truncateAllTables } from "../../test/db.js";
|
|
@@ -10,7 +10,7 @@ import { DrizzleAffiliateRepository } from "./drizzle-affiliate-repository.js";
|
|
|
10
10
|
describe("processAffiliateCreditMatch", () => {
|
|
11
11
|
let pool: PGlite;
|
|
12
12
|
let db: DrizzleDb;
|
|
13
|
-
let ledger:
|
|
13
|
+
let ledger: DrizzleLedger;
|
|
14
14
|
let affiliateRepo: DrizzleAffiliateRepository;
|
|
15
15
|
let fraudRepo: DrizzleAffiliateFraudRepository;
|
|
16
16
|
|
|
@@ -24,13 +24,19 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
24
24
|
|
|
25
25
|
beforeEach(async () => {
|
|
26
26
|
await truncateAllTables(pool);
|
|
27
|
-
ledger = new
|
|
27
|
+
ledger = new DrizzleLedger(db);
|
|
28
|
+
|
|
29
|
+
await ledger.seedSystemAccounts();
|
|
28
30
|
affiliateRepo = new DrizzleAffiliateRepository(db);
|
|
29
31
|
fraudRepo = new DrizzleAffiliateFraudRepository(db);
|
|
30
32
|
});
|
|
31
33
|
|
|
32
34
|
it("does nothing when tenant has no referral", async () => {
|
|
33
|
-
await ledger.credit("buyer", Credit.fromCents(1000), "purchase",
|
|
35
|
+
await ledger.credit("buyer", Credit.fromCents(1000), "purchase", {
|
|
36
|
+
description: "first buy",
|
|
37
|
+
referenceId: "session-1",
|
|
38
|
+
fundingSource: "stripe",
|
|
39
|
+
});
|
|
34
40
|
|
|
35
41
|
const result = await processAffiliateCreditMatch({
|
|
36
42
|
tenantId: "buyer",
|
|
@@ -45,8 +51,16 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
45
51
|
it("does nothing when tenant already has prior purchases", async () => {
|
|
46
52
|
await affiliateRepo.recordReferral("referrer", "buyer", "abc123");
|
|
47
53
|
|
|
48
|
-
await ledger.credit("buyer", Credit.fromCents(500), "purchase",
|
|
49
|
-
|
|
54
|
+
await ledger.credit("buyer", Credit.fromCents(500), "purchase", {
|
|
55
|
+
description: "old buy",
|
|
56
|
+
referenceId: "session-0",
|
|
57
|
+
fundingSource: "stripe",
|
|
58
|
+
});
|
|
59
|
+
await ledger.credit("buyer", Credit.fromCents(1000), "purchase", {
|
|
60
|
+
description: "new buy",
|
|
61
|
+
referenceId: "session-1",
|
|
62
|
+
fundingSource: "stripe",
|
|
63
|
+
});
|
|
50
64
|
|
|
51
65
|
const result = await processAffiliateCreditMatch({
|
|
52
66
|
tenantId: "buyer",
|
|
@@ -60,7 +74,11 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
60
74
|
|
|
61
75
|
it("credits referrer on first purchase with 100% match", async () => {
|
|
62
76
|
await affiliateRepo.recordReferral("referrer", "buyer", "abc123");
|
|
63
|
-
await ledger.credit("buyer", Credit.fromCents(2000), "purchase",
|
|
77
|
+
await ledger.credit("buyer", Credit.fromCents(2000), "purchase", {
|
|
78
|
+
description: "first buy",
|
|
79
|
+
referenceId: "session-1",
|
|
80
|
+
fundingSource: "stripe",
|
|
81
|
+
});
|
|
64
82
|
|
|
65
83
|
const result = await processAffiliateCreditMatch({
|
|
66
84
|
tenantId: "buyer",
|
|
@@ -83,7 +101,11 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
83
101
|
|
|
84
102
|
it("respects custom match rate", async () => {
|
|
85
103
|
await affiliateRepo.recordReferral("referrer", "buyer", "abc123");
|
|
86
|
-
await ledger.credit("buyer", Credit.fromCents(2000), "purchase",
|
|
104
|
+
await ledger.credit("buyer", Credit.fromCents(2000), "purchase", {
|
|
105
|
+
description: "first buy",
|
|
106
|
+
referenceId: "session-1",
|
|
107
|
+
fundingSource: "stripe",
|
|
108
|
+
});
|
|
87
109
|
|
|
88
110
|
const result = await processAffiliateCreditMatch({
|
|
89
111
|
tenantId: "buyer",
|
|
@@ -99,7 +121,11 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
99
121
|
|
|
100
122
|
it("is idempotent — second call returns null", async () => {
|
|
101
123
|
await affiliateRepo.recordReferral("referrer", "buyer", "abc123");
|
|
102
|
-
await ledger.credit("buyer", Credit.fromCents(1000), "purchase",
|
|
124
|
+
await ledger.credit("buyer", Credit.fromCents(1000), "purchase", {
|
|
125
|
+
description: "first buy",
|
|
126
|
+
referenceId: "session-1",
|
|
127
|
+
fundingSource: "stripe",
|
|
128
|
+
});
|
|
103
129
|
|
|
104
130
|
const first = await processAffiliateCreditMatch({
|
|
105
131
|
tenantId: "buyer",
|
|
@@ -123,7 +149,11 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
123
149
|
signupIp: "1.2.3.4",
|
|
124
150
|
signupEmail: "alice+ref@gmail.com",
|
|
125
151
|
});
|
|
126
|
-
await ledger.credit("buyer", Credit.fromCents(2000), "purchase",
|
|
152
|
+
await ledger.credit("buyer", Credit.fromCents(2000), "purchase", {
|
|
153
|
+
description: "first buy",
|
|
154
|
+
referenceId: "session-1",
|
|
155
|
+
fundingSource: "stripe",
|
|
156
|
+
});
|
|
127
157
|
|
|
128
158
|
const result = await processAffiliateCreditMatch({
|
|
129
159
|
tenantId: "buyer",
|
|
@@ -156,7 +186,11 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
156
186
|
|
|
157
187
|
// New referral
|
|
158
188
|
await affiliateRepo.recordReferral("referrer", "buyer", "abc123");
|
|
159
|
-
await ledger.credit("buyer", Credit.fromCents(1000), "purchase",
|
|
189
|
+
await ledger.credit("buyer", Credit.fromCents(1000), "purchase", {
|
|
190
|
+
description: "first buy",
|
|
191
|
+
referenceId: "session-1",
|
|
192
|
+
fundingSource: "stripe",
|
|
193
|
+
});
|
|
160
194
|
|
|
161
195
|
const result = await processAffiliateCreditMatch({
|
|
162
196
|
tenantId: "buyer",
|
|
@@ -184,7 +218,11 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
184
218
|
|
|
185
219
|
// New referral
|
|
186
220
|
await affiliateRepo.recordReferral("referrer", "buyer", "abc123");
|
|
187
|
-
await ledger.credit("buyer", Credit.fromCents(1000), "purchase",
|
|
221
|
+
await ledger.credit("buyer", Credit.fromCents(1000), "purchase", {
|
|
222
|
+
description: "first buy",
|
|
223
|
+
referenceId: "session-1",
|
|
224
|
+
fundingSource: "stripe",
|
|
225
|
+
});
|
|
188
226
|
|
|
189
227
|
const result = await processAffiliateCreditMatch({
|
|
190
228
|
tenantId: "buyer",
|
|
@@ -203,7 +241,11 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
203
241
|
|
|
204
242
|
it("allows payout when under both caps", async () => {
|
|
205
243
|
await affiliateRepo.recordReferral("referrer", "buyer", "abc123");
|
|
206
|
-
await ledger.credit("buyer", Credit.fromCents(2000), "purchase",
|
|
244
|
+
await ledger.credit("buyer", Credit.fromCents(2000), "purchase", {
|
|
245
|
+
description: "first buy",
|
|
246
|
+
referenceId: "session-1",
|
|
247
|
+
fundingSource: "stripe",
|
|
248
|
+
});
|
|
207
249
|
|
|
208
250
|
const result = await processAffiliateCreditMatch({
|
|
209
251
|
tenantId: "buyer",
|
|
@@ -223,7 +265,11 @@ describe("processAffiliateCreditMatch", () => {
|
|
|
223
265
|
await affiliateRepo.recordReferral("referrer", "buyer", "abc123", {
|
|
224
266
|
signupIp: "1.2.3.4",
|
|
225
267
|
});
|
|
226
|
-
await ledger.credit("buyer", Credit.fromCents(2000), "purchase",
|
|
268
|
+
await ledger.credit("buyer", Credit.fromCents(2000), "purchase", {
|
|
269
|
+
description: "first buy",
|
|
270
|
+
referenceId: "session-1",
|
|
271
|
+
fundingSource: "stripe",
|
|
272
|
+
});
|
|
227
273
|
|
|
228
274
|
const result = await processAffiliateCreditMatch({
|
|
229
275
|
tenantId: "buyer",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Credit,
|
|
1
|
+
import type { Credit, ILedger } from "@wopr-network/platform-core/credits";
|
|
2
2
|
import { config } from "../../config/index.js";
|
|
3
3
|
import type { IAffiliateFraudRepository } from "./affiliate-fraud-repository.js";
|
|
4
4
|
import type { IAffiliateRepository } from "./drizzle-affiliate-repository.js";
|
|
@@ -11,7 +11,7 @@ const DEFAULT_MAX_MATCH_CREDITS_30D = config.billing.affiliateMaxMatchCredits30d
|
|
|
11
11
|
export interface AffiliateCreditMatchDeps {
|
|
12
12
|
tenantId: string;
|
|
13
13
|
purchaseAmount: Credit;
|
|
14
|
-
ledger:
|
|
14
|
+
ledger: ILedger;
|
|
15
15
|
affiliateRepo: IAffiliateRepository;
|
|
16
16
|
matchRate?: number;
|
|
17
17
|
fraudRepo?: IAffiliateFraudRepository;
|
|
@@ -122,13 +122,10 @@ export async function processAffiliateCreditMatch(
|
|
|
122
122
|
if (matchAmount.isZero() || matchAmount.isNegative()) return null;
|
|
123
123
|
|
|
124
124
|
// 6. Credit the referrer
|
|
125
|
-
await ledger.credit(
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
`Affiliate match for referred tenant ${tenantId}`,
|
|
130
|
-
refId,
|
|
131
|
-
);
|
|
125
|
+
await ledger.credit(referral.referrerTenantId, matchAmount, "affiliate_match", {
|
|
126
|
+
description: `Affiliate match for referred tenant ${tenantId}`,
|
|
127
|
+
referenceId: refId,
|
|
128
|
+
});
|
|
132
129
|
|
|
133
130
|
// 7. Update referral record
|
|
134
131
|
await affiliateRepo.markFirstPurchase(tenantId);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PGlite } from "@electric-sql/pglite";
|
|
2
|
-
import { Credit,
|
|
2
|
+
import { Credit, DrizzleLedger } from "@wopr-network/platform-core/credits";
|
|
3
3
|
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
|
4
4
|
import type { DrizzleDb } from "../../db/index.js";
|
|
5
5
|
import { createTestDb, truncateAllTables } from "../../test/db.js";
|
|
@@ -9,7 +9,7 @@ import { DEFAULT_BONUS_RATE, grantNewUserBonus } from "./new-user-bonus.js";
|
|
|
9
9
|
describe("grantNewUserBonus", () => {
|
|
10
10
|
let pool: PGlite;
|
|
11
11
|
let db: DrizzleDb;
|
|
12
|
-
let ledger:
|
|
12
|
+
let ledger: DrizzleLedger;
|
|
13
13
|
let affiliateRepo: DrizzleAffiliateRepository;
|
|
14
14
|
|
|
15
15
|
beforeAll(async () => {
|
|
@@ -22,7 +22,9 @@ describe("grantNewUserBonus", () => {
|
|
|
22
22
|
|
|
23
23
|
beforeEach(async () => {
|
|
24
24
|
await truncateAllTables(pool);
|
|
25
|
-
ledger = new
|
|
25
|
+
ledger = new DrizzleLedger(db);
|
|
26
|
+
|
|
27
|
+
await ledger.seedSystemAccounts();
|
|
26
28
|
affiliateRepo = new DrizzleAffiliateRepository(db);
|
|
27
29
|
});
|
|
28
30
|
|
|
@@ -48,7 +50,7 @@ describe("grantNewUserBonus", () => {
|
|
|
48
50
|
|
|
49
51
|
const txns = await ledger.history("referred-1");
|
|
50
52
|
expect(txns).toHaveLength(1);
|
|
51
|
-
expect(txns[0].
|
|
53
|
+
expect(txns[0].entryType).toBe("affiliate_bonus");
|
|
52
54
|
expect(txns[0].referenceId).toBe("affiliate-bonus:referred-1");
|
|
53
55
|
expect(txns[0].description).toContain("first-purchase bonus");
|
|
54
56
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ILedger } from "@wopr-network/platform-core/credits";
|
|
2
2
|
import { Credit } from "@wopr-network/platform-core/credits";
|
|
3
3
|
import { config } from "../../config/index.js";
|
|
4
4
|
import type { IAffiliateRepository } from "./drizzle-affiliate-repository.js";
|
|
@@ -7,7 +7,7 @@ import type { IAffiliateRepository } from "./drizzle-affiliate-repository.js";
|
|
|
7
7
|
export const DEFAULT_BONUS_RATE = config.billing.affiliateNewUserBonusRate;
|
|
8
8
|
|
|
9
9
|
export interface NewUserBonusParams {
|
|
10
|
-
ledger:
|
|
10
|
+
ledger: ILedger;
|
|
11
11
|
affiliateRepo: IAffiliateRepository;
|
|
12
12
|
referredTenantId: string;
|
|
13
13
|
purchaseAmount: Credit;
|
|
@@ -57,13 +57,10 @@ export async function grantNewUserBonus(params: NewUserBonusParams): Promise<New
|
|
|
57
57
|
await affiliateRepo.markFirstPurchase(referredTenantId);
|
|
58
58
|
|
|
59
59
|
// 6. Credit the bonus
|
|
60
|
-
await ledger.credit(
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
`New user first-purchase bonus (${Math.round(rate * 100)}%)`,
|
|
65
|
-
refId,
|
|
66
|
-
);
|
|
60
|
+
await ledger.credit(referredTenantId, bonus, "affiliate_bonus", {
|
|
61
|
+
description: `New user first-purchase bonus (${Math.round(rate * 100)}%)`,
|
|
62
|
+
referenceId: refId,
|
|
63
|
+
});
|
|
67
64
|
|
|
68
65
|
return { granted: true, bonus };
|
|
69
66
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import crypto from "node:crypto";
|
|
2
2
|
import type { PGlite } from "@electric-sql/pglite";
|
|
3
3
|
import type { ITenantCustomerRepository } from "@wopr-network/platform-core/billing";
|
|
4
|
-
import { Credit,
|
|
4
|
+
import { Credit, DrizzleLedger } from "@wopr-network/platform-core/credits";
|
|
5
5
|
import Stripe from "stripe";
|
|
6
6
|
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
7
7
|
import type { DrizzleDb } from "../../db/index.js";
|
|
@@ -48,7 +48,7 @@ function mockTenantStore(stripeCustomerId = "cus_123") {
|
|
|
48
48
|
describe("chargeAutoTopup", () => {
|
|
49
49
|
let pool: PGlite;
|
|
50
50
|
let db: DrizzleDb;
|
|
51
|
-
let ledger:
|
|
51
|
+
let ledger: DrizzleLedger;
|
|
52
52
|
|
|
53
53
|
beforeAll(async () => {
|
|
54
54
|
({ db, pool } = await createTestDb());
|
|
@@ -60,7 +60,9 @@ describe("chargeAutoTopup", () => {
|
|
|
60
60
|
|
|
61
61
|
beforeEach(async () => {
|
|
62
62
|
await truncateAllTables(pool);
|
|
63
|
-
ledger = new
|
|
63
|
+
ledger = new DrizzleLedger(db);
|
|
64
|
+
|
|
65
|
+
await ledger.seedSystemAccounts();
|
|
64
66
|
});
|
|
65
67
|
|
|
66
68
|
it("charges Stripe and credits ledger on success", async () => {
|
|
@@ -79,8 +81,8 @@ describe("chargeAutoTopup", () => {
|
|
|
79
81
|
expect(result.paymentReference).toEqual(expect.any(String));
|
|
80
82
|
expect((await ledger.balance("t1")).toCents()).toBe(500);
|
|
81
83
|
const history = await ledger.history("t1");
|
|
82
|
-
expect(history[0].
|
|
83
|
-
expect(history[0].fundingSource).toBe("stripe");
|
|
84
|
+
expect(history[0].entryType).toBe("purchase");
|
|
85
|
+
expect(history[0].metadata?.fundingSource).toBe("stripe");
|
|
84
86
|
});
|
|
85
87
|
|
|
86
88
|
it("writes success event to credit_auto_topup log", async () => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ITenantCustomerRepository } from "@wopr-network/platform-core/billing";
|
|
2
|
-
import type { Credit,
|
|
2
|
+
import type { Credit, ILedger } from "@wopr-network/platform-core/credits";
|
|
3
3
|
import Stripe from "stripe";
|
|
4
4
|
import { logger } from "../../config/logger.js";
|
|
5
5
|
import type { IAutoTopupEventLogRepository } from "./auto-topup-event-log-repository.js";
|
|
@@ -10,7 +10,7 @@ export const MAX_CONSECUTIVE_FAILURES = 3;
|
|
|
10
10
|
export interface AutoTopupChargeDeps {
|
|
11
11
|
stripe: Stripe;
|
|
12
12
|
tenantRepo: ITenantCustomerRepository;
|
|
13
|
-
creditLedger:
|
|
13
|
+
creditLedger: ILedger;
|
|
14
14
|
eventLogRepo: IAutoTopupEventLogRepository;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -136,14 +136,11 @@ export async function chargeAutoTopup(
|
|
|
136
136
|
// 5. Credit the ledger (idempotent via referenceId = PI ID)
|
|
137
137
|
try {
|
|
138
138
|
if (!(await deps.creditLedger.hasReferenceId(paymentIntent.id))) {
|
|
139
|
-
await deps.creditLedger.credit(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
"
|
|
143
|
-
|
|
144
|
-
paymentIntent.id,
|
|
145
|
-
"stripe",
|
|
146
|
-
);
|
|
139
|
+
await deps.creditLedger.credit(tenantId, amount, "purchase", {
|
|
140
|
+
description: `Auto-topup (${source})`,
|
|
141
|
+
referenceId: paymentIntent.id,
|
|
142
|
+
fundingSource: "stripe",
|
|
143
|
+
});
|
|
147
144
|
}
|
|
148
145
|
} catch (err) {
|
|
149
146
|
const message = `Stripe charge ${paymentIntent.id} succeeded but credit grant failed: ${err instanceof Error ? err.message : String(err)}`;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PGlite } from "@electric-sql/pglite";
|
|
2
|
-
import { Credit,
|
|
2
|
+
import { Credit, DrizzleAutoTopupSettingsRepository, DrizzleLedger } from "@wopr-network/platform-core/credits";
|
|
3
3
|
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
4
|
import type { DrizzleDb } from "../../db/index.js";
|
|
5
5
|
import { createTestDb, truncateAllTables } from "../../test/db.js";
|
|
@@ -8,7 +8,7 @@ import { maybeTriggerUsageTopup, type UsageTopupDeps } from "./auto-topup-usage.
|
|
|
8
8
|
describe("maybeTriggerUsageTopup", () => {
|
|
9
9
|
let pool: PGlite;
|
|
10
10
|
let db: DrizzleDb;
|
|
11
|
-
let ledger:
|
|
11
|
+
let ledger: DrizzleLedger;
|
|
12
12
|
let settingsRepo: DrizzleAutoTopupSettingsRepository;
|
|
13
13
|
|
|
14
14
|
beforeAll(async () => {
|
|
@@ -21,7 +21,9 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
21
21
|
|
|
22
22
|
beforeEach(async () => {
|
|
23
23
|
await truncateAllTables(pool);
|
|
24
|
-
ledger = new
|
|
24
|
+
ledger = new DrizzleLedger(db);
|
|
25
|
+
|
|
26
|
+
await ledger.seedSystemAccounts();
|
|
25
27
|
settingsRepo = new DrizzleAutoTopupSettingsRepository(db);
|
|
26
28
|
});
|
|
27
29
|
|
|
@@ -35,7 +37,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
35
37
|
|
|
36
38
|
it("does nothing when usage_enabled is false", async () => {
|
|
37
39
|
await settingsRepo.upsert("t1", { usageEnabled: false });
|
|
38
|
-
await ledger.credit("t1", Credit.fromCents(50), "purchase",
|
|
40
|
+
await ledger.credit("t1", Credit.fromCents(50), "purchase", {
|
|
41
|
+
description: "buy",
|
|
42
|
+
referenceId: "ref-1",
|
|
43
|
+
fundingSource: "stripe",
|
|
44
|
+
});
|
|
39
45
|
const mockCharge = vi.fn();
|
|
40
46
|
const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
|
|
41
47
|
|
|
@@ -49,7 +55,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
49
55
|
usageThreshold: Credit.fromCents(100),
|
|
50
56
|
usageTopup: Credit.fromCents(500),
|
|
51
57
|
});
|
|
52
|
-
await ledger.credit("t1", Credit.fromCents(200), "purchase",
|
|
58
|
+
await ledger.credit("t1", Credit.fromCents(200), "purchase", {
|
|
59
|
+
description: "buy",
|
|
60
|
+
referenceId: "ref-1",
|
|
61
|
+
fundingSource: "stripe",
|
|
62
|
+
});
|
|
53
63
|
const mockCharge = vi.fn();
|
|
54
64
|
const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
|
|
55
65
|
|
|
@@ -63,7 +73,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
63
73
|
usageThreshold: Credit.fromCents(100),
|
|
64
74
|
usageTopup: Credit.fromCents(500),
|
|
65
75
|
});
|
|
66
|
-
await ledger.credit("t1", Credit.fromCents(50), "purchase",
|
|
76
|
+
await ledger.credit("t1", Credit.fromCents(50), "purchase", {
|
|
77
|
+
description: "buy",
|
|
78
|
+
referenceId: "ref-1",
|
|
79
|
+
fundingSource: "stripe",
|
|
80
|
+
});
|
|
67
81
|
const mockCharge = vi.fn().mockResolvedValue({ success: true, paymentReference: "pi_123" });
|
|
68
82
|
const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
|
|
69
83
|
|
|
@@ -74,7 +88,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
74
88
|
it("skips when charge is already in-flight", async () => {
|
|
75
89
|
await settingsRepo.upsert("t1", { usageEnabled: true, usageThreshold: Credit.fromCents(100) });
|
|
76
90
|
await settingsRepo.setUsageChargeInFlight("t1", true);
|
|
77
|
-
await ledger.credit("t1", Credit.fromCents(50), "purchase",
|
|
91
|
+
await ledger.credit("t1", Credit.fromCents(50), "purchase", {
|
|
92
|
+
description: "buy",
|
|
93
|
+
referenceId: "ref-1",
|
|
94
|
+
fundingSource: "stripe",
|
|
95
|
+
});
|
|
78
96
|
const mockCharge = vi.fn();
|
|
79
97
|
const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
|
|
80
98
|
|
|
@@ -89,7 +107,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
89
107
|
usageThreshold: Credit.fromCents(500),
|
|
90
108
|
usageTopup: Credit.fromCents(2000),
|
|
91
109
|
});
|
|
92
|
-
await ledger.credit("t1", Credit.fromCents(100), "purchase",
|
|
110
|
+
await ledger.credit("t1", Credit.fromCents(100), "purchase", {
|
|
111
|
+
description: "buy",
|
|
112
|
+
referenceId: "ref-1",
|
|
113
|
+
fundingSource: "stripe",
|
|
114
|
+
});
|
|
93
115
|
|
|
94
116
|
const mockCharge = vi.fn().mockResolvedValue({ success: true, paymentReference: "pi_race" });
|
|
95
117
|
const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
|
|
@@ -113,7 +135,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
113
135
|
usageThreshold: Credit.fromCents(100),
|
|
114
136
|
usageTopup: Credit.fromCents(500),
|
|
115
137
|
});
|
|
116
|
-
await ledger.credit("t1", Credit.fromCents(50), "purchase",
|
|
138
|
+
await ledger.credit("t1", Credit.fromCents(50), "purchase", {
|
|
139
|
+
description: "buy",
|
|
140
|
+
referenceId: "ref-1",
|
|
141
|
+
fundingSource: "stripe",
|
|
142
|
+
});
|
|
117
143
|
const mockCharge = vi.fn().mockResolvedValue({ success: true, paymentReference: "pi_123" });
|
|
118
144
|
const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
|
|
119
145
|
|
|
@@ -135,7 +161,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
135
161
|
usageThreshold: Credit.fromCents(100),
|
|
136
162
|
usageTopup: Credit.fromCents(500),
|
|
137
163
|
});
|
|
138
|
-
await ledger.credit("t1", Credit.fromCents(50), "purchase",
|
|
164
|
+
await ledger.credit("t1", Credit.fromCents(50), "purchase", {
|
|
165
|
+
description: "buy",
|
|
166
|
+
referenceId: "ref-1",
|
|
167
|
+
fundingSource: "stripe",
|
|
168
|
+
});
|
|
139
169
|
const mockCharge = vi
|
|
140
170
|
.fn()
|
|
141
171
|
.mockRejectedValueOnce(new Error("Stripe network error"))
|
|
@@ -162,7 +192,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
162
192
|
});
|
|
163
193
|
await settingsRepo.incrementUsageFailures("t1");
|
|
164
194
|
await settingsRepo.incrementUsageFailures("t1");
|
|
165
|
-
await ledger.credit("t1", Credit.fromCents(50), "purchase",
|
|
195
|
+
await ledger.credit("t1", Credit.fromCents(50), "purchase", {
|
|
196
|
+
description: "buy",
|
|
197
|
+
referenceId: "ref-1",
|
|
198
|
+
fundingSource: "stripe",
|
|
199
|
+
});
|
|
166
200
|
const mockCharge = vi.fn().mockResolvedValue({ success: true });
|
|
167
201
|
const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
|
|
168
202
|
|
|
@@ -176,7 +210,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
176
210
|
usageThreshold: Credit.fromCents(100),
|
|
177
211
|
usageTopup: Credit.fromCents(500),
|
|
178
212
|
});
|
|
179
|
-
await ledger.credit("t1", Credit.fromCents(50), "purchase",
|
|
213
|
+
await ledger.credit("t1", Credit.fromCents(50), "purchase", {
|
|
214
|
+
description: "buy",
|
|
215
|
+
referenceId: "ref-1",
|
|
216
|
+
fundingSource: "stripe",
|
|
217
|
+
});
|
|
180
218
|
const mockCharge = vi.fn().mockResolvedValue({ success: false, error: "declined" });
|
|
181
219
|
const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
|
|
182
220
|
|
|
@@ -208,7 +246,11 @@ describe("maybeTriggerUsageTopup", () => {
|
|
|
208
246
|
});
|
|
209
247
|
await settingsRepo.incrementUsageFailures("t1");
|
|
210
248
|
await settingsRepo.incrementUsageFailures("t1");
|
|
211
|
-
await ledger.credit("t1", Credit.fromCents(50), "purchase",
|
|
249
|
+
await ledger.credit("t1", Credit.fromCents(50), "purchase", {
|
|
250
|
+
description: "buy",
|
|
251
|
+
referenceId: "ref-1",
|
|
252
|
+
fundingSource: "stripe",
|
|
253
|
+
});
|
|
212
254
|
const mockCharge = vi.fn().mockResolvedValue({ success: false, error: "declined" });
|
|
213
255
|
const deps: UsageTopupDeps = { settingsRepo, creditLedger: ledger, chargeAutoTopup: mockCharge };
|
|
214
256
|
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { Credit, IAutoTopupSettingsRepository,
|
|
1
|
+
import type { Credit, IAutoTopupSettingsRepository, ILedger } from "@wopr-network/platform-core/credits";
|
|
2
2
|
import { logger } from "../../config/logger.js";
|
|
3
3
|
import type { AutoTopupChargeResult } from "./auto-topup-charge.js";
|
|
4
4
|
import { MAX_CONSECUTIVE_FAILURES } from "./auto-topup-charge.js";
|
|
5
5
|
|
|
6
6
|
export interface UsageTopupDeps {
|
|
7
7
|
settingsRepo: IAutoTopupSettingsRepository;
|
|
8
|
-
creditLedger:
|
|
8
|
+
creditLedger: ILedger;
|
|
9
9
|
/** Injected charge function (allows mocking in tests). */
|
|
10
10
|
chargeAutoTopup: (tenantId: string, amount: Credit, source: string) => Promise<AutoTopupChargeResult>;
|
|
11
11
|
/** Optional tenant status check. If provided and returns non-null, skip the charge. */
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { PGlite } from "@electric-sql/pglite";
|
|
2
|
-
import { Credit,
|
|
2
|
+
import { Credit, DrizzleLedger } from "@wopr-network/platform-core/credits";
|
|
3
3
|
import { sql } from "drizzle-orm";
|
|
4
4
|
import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from "vitest";
|
|
5
5
|
import type { DrizzleDb } from "../../db/index.js";
|
|
@@ -57,7 +57,7 @@ describe("BotBilling", () => {
|
|
|
57
57
|
let pool: PGlite;
|
|
58
58
|
let db: DrizzleDb;
|
|
59
59
|
let billing: BotBilling;
|
|
60
|
-
let ledger:
|
|
60
|
+
let ledger: DrizzleLedger;
|
|
61
61
|
|
|
62
62
|
beforeAll(async () => {
|
|
63
63
|
({ db, pool } = await createTestDb());
|
|
@@ -70,7 +70,9 @@ describe("BotBilling", () => {
|
|
|
70
70
|
beforeEach(async () => {
|
|
71
71
|
await truncateAllTables(pool);
|
|
72
72
|
billing = new BotBilling(new DrizzleBotInstanceRepository(db));
|
|
73
|
-
ledger = new
|
|
73
|
+
ledger = new DrizzleLedger(db);
|
|
74
|
+
|
|
75
|
+
await ledger.seedSystemAccounts();
|
|
74
76
|
});
|
|
75
77
|
|
|
76
78
|
describe("registerBot", () => {
|
|
@@ -211,7 +213,11 @@ describe("BotBilling", () => {
|
|
|
211
213
|
await billing.suspendBot("bot-1");
|
|
212
214
|
await billing.suspendBot("bot-2");
|
|
213
215
|
|
|
214
|
-
await ledger.credit("tenant-1", Credit.fromCents(500), "purchase",
|
|
216
|
+
await ledger.credit("tenant-1", Credit.fromCents(500), "purchase", {
|
|
217
|
+
description: "test credit",
|
|
218
|
+
referenceId: "ref-1",
|
|
219
|
+
fundingSource: "stripe",
|
|
220
|
+
});
|
|
215
221
|
const reactivated = await billing.checkReactivation("tenant-1", ledger);
|
|
216
222
|
|
|
217
223
|
expect(reactivated.sort()).toEqual(["bot-1", "bot-2"]);
|
|
@@ -231,14 +237,22 @@ describe("BotBilling", () => {
|
|
|
231
237
|
await billing.registerBot("bot-1", "tenant-1", "bot-a");
|
|
232
238
|
await billing.destroyBot("bot-1");
|
|
233
239
|
|
|
234
|
-
await ledger.credit("tenant-1", Credit.fromCents(500), "purchase",
|
|
240
|
+
await ledger.credit("tenant-1", Credit.fromCents(500), "purchase", {
|
|
241
|
+
description: "test credit",
|
|
242
|
+
referenceId: "ref-1",
|
|
243
|
+
fundingSource: "stripe",
|
|
244
|
+
});
|
|
235
245
|
const reactivated = await billing.checkReactivation("tenant-1", ledger);
|
|
236
246
|
|
|
237
247
|
expect(reactivated).toEqual([]);
|
|
238
248
|
});
|
|
239
249
|
|
|
240
250
|
it("returns empty array for tenant with no bots", async () => {
|
|
241
|
-
await ledger.credit("tenant-1", Credit.fromCents(500), "purchase",
|
|
251
|
+
await ledger.credit("tenant-1", Credit.fromCents(500), "purchase", {
|
|
252
|
+
description: "test credit",
|
|
253
|
+
referenceId: "ref-1",
|
|
254
|
+
fundingSource: "stripe",
|
|
255
|
+
});
|
|
242
256
|
const reactivated = await billing.checkReactivation("tenant-1", ledger);
|
|
243
257
|
expect(reactivated).toEqual([]);
|
|
244
258
|
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ILedger } from "@wopr-network/platform-core/credits";
|
|
2
2
|
import { Credit } from "@wopr-network/platform-core/credits";
|
|
3
3
|
import { logger } from "../../config/logger.js";
|
|
4
4
|
import type { IBotInstanceRepository } from "../../fleet/bot-instance-repository.js";
|
|
@@ -16,7 +16,7 @@ export interface IBotBilling {
|
|
|
16
16
|
suspendBot(botId: string): Promise<void>;
|
|
17
17
|
suspendAllForTenant(tenantId: string): Promise<string[]>;
|
|
18
18
|
reactivateBot(botId: string): Promise<void>;
|
|
19
|
-
checkReactivation(tenantId: string, ledger:
|
|
19
|
+
checkReactivation(tenantId: string, ledger: ILedger): Promise<string[]>;
|
|
20
20
|
destroyBot(botId: string): Promise<void>;
|
|
21
21
|
destroyExpiredBots(): Promise<string[]>;
|
|
22
22
|
getBotBilling(botId: string): Promise<unknown>;
|
|
@@ -98,7 +98,7 @@ export class DrizzleBotBilling implements IBotBilling {
|
|
|
98
98
|
*
|
|
99
99
|
* @returns IDs of reactivated bots.
|
|
100
100
|
*/
|
|
101
|
-
async checkReactivation(tenantId: string, ledger:
|
|
101
|
+
async checkReactivation(tenantId: string, ledger: ILedger): Promise<string[]> {
|
|
102
102
|
const balance = await ledger.balance(tenantId);
|
|
103
103
|
if (balance.isNegative() || balance.isZero()) return [];
|
|
104
104
|
|