fresh-squeezy 1.0.20
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.
Potentially problematic release.
This version of fresh-squeezy might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/README.md +114 -0
- package/dist/.cursor/skills/wizard/utils/validation.d.ts +3 -0
- package/dist/.cursor/skills/wizard/utils/validation.d.ts.map +1 -0
- package/dist/.cursor/skills/wizard/utils/validation.js +113 -0
- package/dist/.cursor/skills/wizard/utils/validation.js.map +1 -0
- package/dist/billing-config.d.ts +3 -0
- package/dist/billing-config.d.ts.map +1 -0
- package/dist/billing-config.js +37 -0
- package/dist/billing-config.js.map +1 -0
- package/dist/bootstrap.d.ts +8 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +42 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/cache.d.ts +5 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +27 -0
- package/dist/cache.js.map +1 -0
- package/dist/checkout.d.ts +3 -0
- package/dist/checkout.d.ts.map +1 -0
- package/dist/checkout.js +42 -0
- package/dist/checkout.js.map +1 -0
- package/dist/cli-validate.d.ts +2 -0
- package/dist/cli-validate.d.ts.map +1 -0
- package/dist/cli-validate.js +206 -0
- package/dist/cli-validate.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +40 -0
- package/dist/cli.js.map +1 -0
- package/dist/createBilling.d.ts +3 -0
- package/dist/createBilling.d.ts.map +1 -0
- package/dist/createBilling.js +82 -0
- package/dist/createBilling.js.map +1 -0
- package/dist/customers.d.ts +3 -0
- package/dist/customers.d.ts.map +1 -0
- package/dist/customers.js +57 -0
- package/dist/customers.js.map +1 -0
- package/dist/dedup.d.ts +60 -0
- package/dist/dedup.d.ts.map +1 -0
- package/dist/dedup.js +74 -0
- package/dist/dedup.js.map +1 -0
- package/dist/express.d.ts +13 -0
- package/dist/express.d.ts.map +1 -0
- package/dist/express.js +70 -0
- package/dist/express.js.map +1 -0
- package/dist/health.d.ts +3 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +38 -0
- package/dist/health.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/licenses.d.ts +3 -0
- package/dist/licenses.d.ts.map +1 -0
- package/dist/licenses.js +70 -0
- package/dist/licenses.js.map +1 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +75 -0
- package/dist/logger.js.map +1 -0
- package/dist/plans.d.ts +5 -0
- package/dist/plans.d.ts.map +1 -0
- package/dist/plans.js +72 -0
- package/dist/plans.js.map +1 -0
- package/dist/portal.d.ts +2 -0
- package/dist/portal.d.ts.map +1 -0
- package/dist/portal.js +15 -0
- package/dist/portal.js.map +1 -0
- package/dist/retry.d.ts +2 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +35 -0
- package/dist/retry.js.map +1 -0
- package/dist/src/billing-config.d.ts +3 -0
- package/dist/src/billing-config.d.ts.map +1 -0
- package/dist/src/billing-config.js +34 -0
- package/dist/src/billing-config.js.map +1 -0
- package/dist/src/cli/cli-validate.d.ts +2 -0
- package/dist/src/cli/cli-validate.d.ts.map +1 -0
- package/dist/src/cli/cli-validate.js +206 -0
- package/dist/src/cli/cli-validate.js.map +1 -0
- package/dist/src/cli/cli.d.ts +3 -0
- package/dist/src/cli/cli.d.ts.map +1 -0
- package/dist/src/cli/cli.js +40 -0
- package/dist/src/cli/cli.js.map +1 -0
- package/dist/src/core/bootstrap.d.ts +8 -0
- package/dist/src/core/bootstrap.d.ts.map +1 -0
- package/dist/src/core/bootstrap.js +42 -0
- package/dist/src/core/bootstrap.js.map +1 -0
- package/dist/src/core/cache.d.ts +5 -0
- package/dist/src/core/cache.d.ts.map +1 -0
- package/dist/src/core/cache.js +27 -0
- package/dist/src/core/cache.js.map +1 -0
- package/dist/src/core/checkout.d.ts +3 -0
- package/dist/src/core/checkout.d.ts.map +1 -0
- package/dist/src/core/checkout.js +42 -0
- package/dist/src/core/checkout.js.map +1 -0
- package/dist/src/core/createBilling.d.ts +3 -0
- package/dist/src/core/createBilling.d.ts.map +1 -0
- package/dist/src/core/createBilling.js +82 -0
- package/dist/src/core/createBilling.js.map +1 -0
- package/dist/src/core/customers.d.ts +3 -0
- package/dist/src/core/customers.d.ts.map +1 -0
- package/dist/src/core/customers.js +57 -0
- package/dist/src/core/customers.js.map +1 -0
- package/dist/src/core/dedup.d.ts +60 -0
- package/dist/src/core/dedup.d.ts.map +1 -0
- package/dist/src/core/dedup.js +74 -0
- package/dist/src/core/dedup.js.map +1 -0
- package/dist/src/core/express.d.ts +13 -0
- package/dist/src/core/express.d.ts.map +1 -0
- package/dist/src/core/express.js +70 -0
- package/dist/src/core/express.js.map +1 -0
- package/dist/src/core/health.d.ts +3 -0
- package/dist/src/core/health.d.ts.map +1 -0
- package/dist/src/core/health.js +38 -0
- package/dist/src/core/health.js.map +1 -0
- package/dist/src/core/index.d.ts +9 -0
- package/dist/src/core/index.d.ts.map +1 -0
- package/dist/src/core/index.js +9 -0
- package/dist/src/core/index.js.map +1 -0
- package/dist/src/core/licenses.d.ts +3 -0
- package/dist/src/core/licenses.d.ts.map +1 -0
- package/dist/src/core/licenses.js +70 -0
- package/dist/src/core/licenses.js.map +1 -0
- package/dist/src/core/logger.d.ts +4 -0
- package/dist/src/core/logger.d.ts.map +1 -0
- package/dist/src/core/logger.js +150 -0
- package/dist/src/core/logger.js.map +1 -0
- package/dist/src/core/plans.d.ts +5 -0
- package/dist/src/core/plans.d.ts.map +1 -0
- package/dist/src/core/plans.js +72 -0
- package/dist/src/core/plans.js.map +1 -0
- package/dist/src/core/portal.d.ts +2 -0
- package/dist/src/core/portal.d.ts.map +1 -0
- package/dist/src/core/portal.js +15 -0
- package/dist/src/core/portal.js.map +1 -0
- package/dist/src/core/retry.d.ts +2 -0
- package/dist/src/core/retry.d.ts.map +1 -0
- package/dist/src/core/retry.js +35 -0
- package/dist/src/core/retry.js.map +1 -0
- package/dist/src/core/subscriptions.d.ts +3 -0
- package/dist/src/core/subscriptions.d.ts.map +1 -0
- package/dist/src/core/subscriptions.js +46 -0
- package/dist/src/core/subscriptions.js.map +1 -0
- package/dist/src/core/webhook.d.ts +7 -0
- package/dist/src/core/webhook.d.ts.map +1 -0
- package/dist/src/core/webhook.js +233 -0
- package/dist/src/core/webhook.js.map +1 -0
- package/dist/src/core/webhooks.d.ts +3 -0
- package/dist/src/core/webhooks.d.ts.map +1 -0
- package/dist/src/core/webhooks.js +28 -0
- package/dist/src/core/webhooks.js.map +1 -0
- package/dist/src/types/billing/billing.d.ts +36 -0
- package/dist/src/types/billing/billing.d.ts.map +1 -0
- package/dist/src/types/billing/billing.js +2 -0
- package/dist/src/types/billing/billing.js.map +1 -0
- package/dist/src/types/billing/index.d.ts +2 -0
- package/dist/src/types/billing/index.d.ts.map +1 -0
- package/dist/src/types/billing/index.js +2 -0
- package/dist/src/types/billing/index.js.map +1 -0
- package/dist/src/types/billing/types.d.ts +50 -0
- package/dist/src/types/billing/types.d.ts.map +1 -0
- package/dist/src/types/billing/types.js +2 -0
- package/dist/src/types/billing/types.js.map +1 -0
- package/dist/src/types/cache/index.d.ts +2 -0
- package/dist/src/types/cache/index.d.ts.map +1 -0
- package/dist/src/types/cache/index.js +2 -0
- package/dist/src/types/cache/index.js.map +1 -0
- package/dist/src/types/cache/types.d.ts +62 -0
- package/dist/src/types/cache/types.d.ts.map +1 -0
- package/dist/src/types/cache/types.js +2 -0
- package/dist/src/types/cache/types.js.map +1 -0
- package/dist/src/types/config/index.d.ts +2 -0
- package/dist/src/types/config/index.d.ts.map +1 -0
- package/dist/src/types/config/index.js +2 -0
- package/dist/src/types/config/index.js.map +1 -0
- package/dist/src/types/config/types.d.ts +138 -0
- package/dist/src/types/config/types.d.ts.map +1 -0
- package/dist/src/types/config/types.js +2 -0
- package/dist/src/types/config/types.js.map +1 -0
- package/dist/src/types/index.d.ts +9 -0
- package/dist/src/types/index.d.ts.map +1 -0
- package/dist/src/types/index.js +9 -0
- package/dist/src/types/index.js.map +1 -0
- package/dist/src/types/license/index.d.ts +2 -0
- package/dist/src/types/license/index.d.ts.map +1 -0
- package/dist/src/types/license/index.js +2 -0
- package/dist/src/types/license/index.js.map +1 -0
- package/dist/src/types/license/types.d.ts +32 -0
- package/dist/src/types/license/types.d.ts.map +1 -0
- package/dist/src/types/license/types.js +2 -0
- package/dist/src/types/license/types.js.map +1 -0
- package/dist/src/types/management/index.d.ts +2 -0
- package/dist/src/types/management/index.d.ts.map +1 -0
- package/dist/src/types/management/index.js +2 -0
- package/dist/src/types/management/index.js.map +1 -0
- package/dist/src/types/management/types.d.ts +37 -0
- package/dist/src/types/management/types.d.ts.map +1 -0
- package/dist/src/types/management/types.js +2 -0
- package/dist/src/types/management/types.js.map +1 -0
- package/dist/src/types/subscription/index.d.ts +2 -0
- package/dist/src/types/subscription/index.d.ts.map +1 -0
- package/dist/src/types/subscription/index.js +2 -0
- package/dist/src/types/subscription/index.js.map +1 -0
- package/dist/src/types/subscription/types.d.ts +46 -0
- package/dist/src/types/subscription/types.d.ts.map +1 -0
- package/dist/src/types/subscription/types.js +2 -0
- package/dist/src/types/subscription/types.js.map +1 -0
- package/dist/src/types/types.d.ts +2 -0
- package/dist/src/types/types.d.ts.map +1 -0
- package/dist/src/types/types.js +2 -0
- package/dist/src/types/types.js.map +1 -0
- package/dist/src/types/webhook/index.d.ts +2 -0
- package/dist/src/types/webhook/index.d.ts.map +1 -0
- package/dist/src/types/webhook/index.js +2 -0
- package/dist/src/types/webhook/index.js.map +1 -0
- package/dist/src/types/webhook/types.d.ts +14 -0
- package/dist/src/types/webhook/types.d.ts.map +1 -0
- package/dist/src/types/webhook/types.js +2 -0
- package/dist/src/types/webhook/types.js.map +1 -0
- package/dist/src/wizard.d.ts +22 -0
- package/dist/src/wizard.d.ts.map +1 -0
- package/dist/src/wizard.js +628 -0
- package/dist/src/wizard.js.map +1 -0
- package/dist/subscriptions.d.ts +3 -0
- package/dist/subscriptions.d.ts.map +1 -0
- package/dist/subscriptions.js +46 -0
- package/dist/subscriptions.js.map +1 -0
- package/dist/types.d.ts +286 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/dist/webhook.d.ts +7 -0
- package/dist/webhook.d.ts.map +1 -0
- package/dist/webhook.js +227 -0
- package/dist/webhook.js.map +1 -0
- package/dist/webhooks.d.ts +3 -0
- package/dist/webhooks.d.ts.map +1 -0
- package/dist/webhooks.js +28 -0
- package/dist/webhooks.js.map +1 -0
- package/dist/wizard.d.ts +22 -0
- package/dist/wizard.d.ts.map +1 -0
- package/dist/wizard.js +615 -0
- package/dist/wizard.js.map +1 -0
- package/package.json +88 -0
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { createBilling } from "./index.js";
|
|
2
|
+
import { createHmac } from "node:crypto";
|
|
3
|
+
import { readFileSync, unlinkSync } from "node:fs";
|
|
4
|
+
const WEBHOOK_SECRET = process.env.LS_WEBHOOK_SECRET ?? "test-webhook-secret";
|
|
5
|
+
function pass(label) {
|
|
6
|
+
console.log(` \u2705 ${label}`);
|
|
7
|
+
}
|
|
8
|
+
function fail(label, error) {
|
|
9
|
+
console.log(` \u274c ${label}: ${error}`);
|
|
10
|
+
}
|
|
11
|
+
function cleanup(cachePath, logPath) {
|
|
12
|
+
try {
|
|
13
|
+
unlinkSync(cachePath);
|
|
14
|
+
}
|
|
15
|
+
catch { }
|
|
16
|
+
try {
|
|
17
|
+
unlinkSync(logPath);
|
|
18
|
+
}
|
|
19
|
+
catch { }
|
|
20
|
+
}
|
|
21
|
+
export async function runValidate() {
|
|
22
|
+
const CACHE_PATH = "/tmp/lemonsqueezy-billing-validate-cache.json";
|
|
23
|
+
const LOG_PATH = "/tmp/lemonsqueezy-billing-validate.log";
|
|
24
|
+
const API_KEY = process.env.LS_API_KEY;
|
|
25
|
+
console.log("\ud83c\udf4b lemonsqueezy-billing \u2014 Validation Suite\n");
|
|
26
|
+
console.log("=".repeat(50));
|
|
27
|
+
await validateOffline(CACHE_PATH, LOG_PATH);
|
|
28
|
+
await validateLive(API_KEY, CACHE_PATH, LOG_PATH);
|
|
29
|
+
console.log("\n" + "=".repeat(50));
|
|
30
|
+
console.log("\n\u2728 Validation complete\n");
|
|
31
|
+
console.log('\n📁 Validation files location:');
|
|
32
|
+
console.log(` Cache file: ${CACHE_PATH}`);
|
|
33
|
+
console.log(` Log file: ${LOG_PATH}`);
|
|
34
|
+
}
|
|
35
|
+
async function validateOffline(cachePath, logPath) {
|
|
36
|
+
console.log("\n\ud83d\udce6 Offline Validation (no API key needed)\n");
|
|
37
|
+
const { readCache, writeCache, isCacheValid } = await import("./cache.js");
|
|
38
|
+
const { createWebhookVerifier, createWebhookHandler } = await import("./webhook.js");
|
|
39
|
+
const { createLogger, withLogger } = await import("./logger.js");
|
|
40
|
+
const { withRetry } = await import("./retry.js");
|
|
41
|
+
const testCache = {
|
|
42
|
+
generatedAt: new Date().toISOString(),
|
|
43
|
+
store: { id: "test-store", name: "Test Store", slug: "test", currency: "USD" },
|
|
44
|
+
products: [{
|
|
45
|
+
id: "prod-1",
|
|
46
|
+
name: "Test Product",
|
|
47
|
+
description: "Test",
|
|
48
|
+
status: "published",
|
|
49
|
+
variants: [{
|
|
50
|
+
id: "var-1",
|
|
51
|
+
name: "Default",
|
|
52
|
+
price: 499,
|
|
53
|
+
priceFormatted: "$4.99",
|
|
54
|
+
isSubscription: false,
|
|
55
|
+
status: "published",
|
|
56
|
+
}],
|
|
57
|
+
}],
|
|
58
|
+
};
|
|
59
|
+
writeCache(testCache, cachePath);
|
|
60
|
+
const loaded = readCache(cachePath);
|
|
61
|
+
if (loaded?.store.name === "Test Store")
|
|
62
|
+
pass("Cache write + read");
|
|
63
|
+
else
|
|
64
|
+
fail("Cache write + read", "Data mismatch");
|
|
65
|
+
if (isCacheValid(loaded, 3_600_000))
|
|
66
|
+
pass("Cache TTL (fresh)");
|
|
67
|
+
else
|
|
68
|
+
fail("Cache TTL", "Fresh cache rejected");
|
|
69
|
+
const oldCache = { ...testCache, generatedAt: new Date(Date.now() - 7_200_000).toISOString() };
|
|
70
|
+
writeCache(oldCache, cachePath);
|
|
71
|
+
if (!isCacheValid(readCache(cachePath), 3_600_000))
|
|
72
|
+
pass("Cache TTL (expired rejected)");
|
|
73
|
+
else
|
|
74
|
+
fail("Cache TTL", "Expired cache accepted");
|
|
75
|
+
const verifier = createWebhookVerifier(WEBHOOK_SECRET);
|
|
76
|
+
const body = '{"test":true}';
|
|
77
|
+
const validSig = createHmac("sha256", WEBHOOK_SECRET).update(body).digest("hex");
|
|
78
|
+
if (verifier(body, validSig))
|
|
79
|
+
pass("Webhook signature (valid accepted)");
|
|
80
|
+
else
|
|
81
|
+
fail("Webhook signature", "Valid sig rejected");
|
|
82
|
+
if (!verifier(body, "invalid-signature"))
|
|
83
|
+
pass("Webhook signature (invalid rejected)");
|
|
84
|
+
else
|
|
85
|
+
fail("Webhook signature", "Invalid sig accepted");
|
|
86
|
+
if (!verifier(body + "tampered", validSig))
|
|
87
|
+
pass("Webhook signature (tampered rejected)");
|
|
88
|
+
else
|
|
89
|
+
fail("Webhook signature", "Tampered body accepted");
|
|
90
|
+
let purchaseCount = 0;
|
|
91
|
+
const handler = createWebhookHandler({
|
|
92
|
+
onPurchase: async () => { purchaseCount++; },
|
|
93
|
+
});
|
|
94
|
+
const orderPayload = {
|
|
95
|
+
meta: { event_name: "order_created", test_mode: false, custom_data: { user_id: "usr_1" } },
|
|
96
|
+
data: { id: "ord_1", type: "orders", attributes: { user_email: "test@test.com", customer_id: 1, variant_id: "v1", product_name: "Pro", total: 499 } },
|
|
97
|
+
};
|
|
98
|
+
await handler(orderPayload);
|
|
99
|
+
if (purchaseCount === 1)
|
|
100
|
+
pass("Webhook dispatch (order_created)");
|
|
101
|
+
else
|
|
102
|
+
fail("Webhook dispatch", `Expected 1 call, got ${purchaseCount}`);
|
|
103
|
+
const dup = await handler(orderPayload);
|
|
104
|
+
if (dup.skipped && purchaseCount === 1)
|
|
105
|
+
pass("Webhook deduplication");
|
|
106
|
+
else
|
|
107
|
+
fail("Webhook dedup", "Duplicate not skipped");
|
|
108
|
+
const logger = createLogger({ filePath: logPath });
|
|
109
|
+
if (logger)
|
|
110
|
+
pass("File logger created");
|
|
111
|
+
else
|
|
112
|
+
fail("File logger", "createLogger returned undefined");
|
|
113
|
+
if (logger) {
|
|
114
|
+
const wrapped = withLogger(logger, "testOp", async (args) => ({ ok: true }));
|
|
115
|
+
await wrapped({ email: "secret@test.com", apiKey: "ls_live_abcdef123456" });
|
|
116
|
+
const logContent = readFileSync(logPath, "utf-8").trim();
|
|
117
|
+
const logLines = logContent.split("\n").filter(line => line.trim());
|
|
118
|
+
if (logLines.length > 0) {
|
|
119
|
+
try {
|
|
120
|
+
const entry = JSON.parse(logLines[0]);
|
|
121
|
+
if (entry.op === "testOp" && entry.input.email.includes("*") && entry.input.apiKey.includes("...")) {
|
|
122
|
+
pass("Logger masking (email + apiKey)");
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
fail("Logger masking", "Sensitive fields not masked");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
catch {
|
|
129
|
+
fail("Logger masking", "Failed to parse log entry");
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
fail("Logger masking", "No log entries found");
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
let retryAttempts = 0;
|
|
137
|
+
try {
|
|
138
|
+
await withRetry(async () => {
|
|
139
|
+
retryAttempts++;
|
|
140
|
+
const err = new Error("client error");
|
|
141
|
+
err.status = 422;
|
|
142
|
+
throw err;
|
|
143
|
+
}, "retryTest", 3);
|
|
144
|
+
}
|
|
145
|
+
catch { }
|
|
146
|
+
if (retryAttempts === 1)
|
|
147
|
+
pass("Retry (4xx not retried)");
|
|
148
|
+
else
|
|
149
|
+
fail("Retry", `Expected 1 attempt, got ${retryAttempts}`);
|
|
150
|
+
writeCache(testCache, cachePath);
|
|
151
|
+
const billing = await createBilling({
|
|
152
|
+
apiKey: "dummy-key-cache-hit",
|
|
153
|
+
storeId: "test-store",
|
|
154
|
+
webhookSecret: WEBHOOK_SECRET,
|
|
155
|
+
cachePath: cachePath,
|
|
156
|
+
cacheTtlMs: 3_600_000,
|
|
157
|
+
callbacks: { onPurchase: async () => { } },
|
|
158
|
+
});
|
|
159
|
+
if (billing.stores.length > 0 && billing.plans.length === 1 && billing.plans[0].price === 499) {
|
|
160
|
+
pass("createBilling from cache");
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
fail("createBilling from cache", "Unexpected plans/stores");
|
|
164
|
+
}
|
|
165
|
+
if (billing.verifyWebhook(body, validSig))
|
|
166
|
+
pass("billing.verifyWebhook");
|
|
167
|
+
else
|
|
168
|
+
fail("billing.verifyWebhook", "Valid sig rejected");
|
|
169
|
+
}
|
|
170
|
+
async function validateLive(apiKey, cachePath, logPath) {
|
|
171
|
+
if (!apiKey) {
|
|
172
|
+
console.log("\n\ud83c\udf10 Live Validation\n");
|
|
173
|
+
console.log(" \u23ed\ufe0f Skipped \u2014 set LS_API_KEY env var to run live tests");
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
console.log("\n\ud83c\udf10 Live Validation (real API calls)\n");
|
|
177
|
+
cleanup(cachePath, logPath);
|
|
178
|
+
const liveCachePath = "/tmp/lemonsqueezy-billing-live-cache.json";
|
|
179
|
+
const billing = await createBilling({
|
|
180
|
+
apiKey: apiKey,
|
|
181
|
+
webhookSecret: WEBHOOK_SECRET,
|
|
182
|
+
cachePath: liveCachePath,
|
|
183
|
+
cacheTtlMs: 0,
|
|
184
|
+
logger: { filePath: logPath },
|
|
185
|
+
callbacks: {
|
|
186
|
+
onPurchase: async () => { },
|
|
187
|
+
onRefund: async () => { },
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
if (billing.stores.length > 0) {
|
|
191
|
+
pass(`Store discovered: ${billing.stores[0].name} (${billing.stores[0].id})`);
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
fail("Store discovery", "No stores found");
|
|
195
|
+
}
|
|
196
|
+
if (billing.plans.length > 0) {
|
|
197
|
+
for (const plan of billing.plans) {
|
|
198
|
+
pass(`Plan: ${plan.name} / ${plan.variantName} \u2014 ${plan.priceFormatted}`);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
fail("Plans", "No published products found");
|
|
203
|
+
}
|
|
204
|
+
cleanup(cachePath, logPath);
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=cli-validate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-validate.js","sourceRoot":"","sources":["../src/cli-validate.ts"],"names":[],"mappings":"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"}
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { runWizard } from "./wizard.js";
|
|
3
|
+
import { runValidate } from "./cli-validate.js";
|
|
4
|
+
function printHelp() {
|
|
5
|
+
console.log(`
|
|
6
|
+
Lemon Squeezy Billing CLI
|
|
7
|
+
|
|
8
|
+
Usage: lemonsqueezy-billing <command>
|
|
9
|
+
|
|
10
|
+
Commands:
|
|
11
|
+
wizard Run interactive setup wizard
|
|
12
|
+
validate Validate billing configuration
|
|
13
|
+
help Show this help message
|
|
14
|
+
|
|
15
|
+
Examples:
|
|
16
|
+
npx lemonsqueezy-billing wizard
|
|
17
|
+
npx lemonsqueezy-billing validate
|
|
18
|
+
`);
|
|
19
|
+
}
|
|
20
|
+
async function main() {
|
|
21
|
+
const args = process.argv.slice(2);
|
|
22
|
+
const command = args[0] || "help";
|
|
23
|
+
switch (command) {
|
|
24
|
+
case "wizard":
|
|
25
|
+
await runWizard();
|
|
26
|
+
break;
|
|
27
|
+
case "validate":
|
|
28
|
+
await runValidate();
|
|
29
|
+
break;
|
|
30
|
+
case "help":
|
|
31
|
+
default:
|
|
32
|
+
printHelp();
|
|
33
|
+
process.exit(command === "help" ? 0 : 1);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
main().catch((error) => {
|
|
37
|
+
console.error("Error:", error.message);
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
|
40
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAIhD,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;CAab,CAAC,CAAC;AACH,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAa,IAAI,CAAC,CAAC,CAAa,IAAI,MAAM,CAAC;IAExD,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,QAAQ;YACX,MAAM,SAAS,EAAE,CAAC;YAClB,MAAM;QACR,KAAK,UAAU;YACb,MAAM,WAAW,EAAE,CAAC;YACpB,MAAM;QACR,KAAK,MAAM,CAAC;QACZ;YACE,SAAS,EAAE,CAAC;YACZ,OAAO,CAAC,IAAI,CAAC,OAAO,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;IAC5B,OAAO,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;IACvC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createBilling.d.ts","sourceRoot":"","sources":["../src/createBilling.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,aAAa,EAAwD,MAAM,YAAY,CAAC;AAgB/G,wBAAsB,aAAa,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CA4G3E"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { bootstrap } from "./bootstrap.js";
|
|
2
|
+
import { createCheckoutFactory } from "./checkout.js";
|
|
3
|
+
import { createWebhookVerifier, createWebhookHandler } from "./webhook.js";
|
|
4
|
+
import { createLogger, withLogger } from "./logger.js";
|
|
5
|
+
import { createExpressRouter } from "./express.js";
|
|
6
|
+
import { getCustomerPortalUrl } from "./portal.js";
|
|
7
|
+
import { fetchProducts, flattenPlans } from "./plans.js";
|
|
8
|
+
import { writeCache } from "./cache.js";
|
|
9
|
+
import { createSubscriptionManagement } from "./subscriptions.js";
|
|
10
|
+
import { createLicenseKeyManagement } from "./licenses.js";
|
|
11
|
+
import { createCustomerManagement } from "./customers.js";
|
|
12
|
+
import { createWebhookManagement } from "./webhooks.js";
|
|
13
|
+
import { createDedupBackend } from "./dedup.js";
|
|
14
|
+
import { healthCheck } from "./health.js";
|
|
15
|
+
export async function createBilling(config) {
|
|
16
|
+
const logger = createLogger(config.logger);
|
|
17
|
+
const doBootstrap = async () => {
|
|
18
|
+
return bootstrap(config);
|
|
19
|
+
};
|
|
20
|
+
const loggedBootstrap = withLogger(logger, "bootstrap", async (_args) => doBootstrap());
|
|
21
|
+
const { stores, plans: initialPlans, cache } = await loggedBootstrap({});
|
|
22
|
+
let currentPlans = initialPlans;
|
|
23
|
+
const storeId = cache.store.id;
|
|
24
|
+
const rawCreateCheckout = createCheckoutFactory(storeId, config.checkoutExpiresInMs);
|
|
25
|
+
const loggedCheckout = withLogger(logger, "createCheckout", async (args) => rawCreateCheckout(args), (url) => ({ checkoutUrl: typeof url === "string" ? `${url.slice(0, 50)}...` : "" }));
|
|
26
|
+
const verifyWebhook = config.webhookSecret
|
|
27
|
+
? createWebhookVerifier(config.webhookSecret)
|
|
28
|
+
: () => { throw new Error("webhookSecret is required for webhook verification"); };
|
|
29
|
+
const rawHandleWebhook = createWebhookHandler(config.callbacks);
|
|
30
|
+
const loggedHandleWebhook = withLogger(logger, "handleWebhook", async (args) => rawHandleWebhook(args), (result) => ({ dispatched: result.dispatched, skipped: result.skipped }));
|
|
31
|
+
const refreshPlans = async () => {
|
|
32
|
+
const products = await fetchProducts(storeId);
|
|
33
|
+
currentPlans = flattenPlans(products);
|
|
34
|
+
writeCache({ generatedAt: new Date().toISOString(), store: cache.store, products }, config.cachePath);
|
|
35
|
+
};
|
|
36
|
+
const loggedGetPortal = withLogger(logger, "getCustomerPortal", async (args) => getCustomerPortalUrl(args.customerId), (url) => ({ portalUrl: typeof url === "string" ? `${url.slice(0, 50)}...` : "" }));
|
|
37
|
+
const subscriptionManagement = createSubscriptionManagement();
|
|
38
|
+
const licenseKeyManagement = createLicenseKeyManagement();
|
|
39
|
+
const customerManagement = createCustomerManagement();
|
|
40
|
+
const webhookManagement = createWebhookManagement(storeId);
|
|
41
|
+
const dedupBackend = createDedupBackend(config.dedup);
|
|
42
|
+
const billing = {
|
|
43
|
+
stores,
|
|
44
|
+
get plans() { return currentPlans; },
|
|
45
|
+
createCheckout: (params) => loggedCheckout(params),
|
|
46
|
+
verifyWebhook: (rawBody, signature) => verifyWebhook(rawBody, signature),
|
|
47
|
+
handleWebhook: async (payload) => {
|
|
48
|
+
await loggedHandleWebhook(payload);
|
|
49
|
+
},
|
|
50
|
+
refreshPlans,
|
|
51
|
+
getCustomerPortal: (customerId) => loggedGetPortal({ customerId }),
|
|
52
|
+
getExpressRouter: (options) => createExpressRouter({
|
|
53
|
+
plans: currentPlans,
|
|
54
|
+
createCheckout: rawCreateCheckout,
|
|
55
|
+
verifyWebhook,
|
|
56
|
+
handleWebhook: rawHandleWebhook,
|
|
57
|
+
}, options),
|
|
58
|
+
// Subscription Management
|
|
59
|
+
pauseSubscription: subscriptionManagement.pauseSubscription,
|
|
60
|
+
resumeSubscription: subscriptionManagement.resumeSubscription,
|
|
61
|
+
cancelSubscription: subscriptionManagement.cancelSubscription,
|
|
62
|
+
changeSubscriptionVariant: subscriptionManagement.changeSubscriptionVariant,
|
|
63
|
+
resumeCancelledSubscription: subscriptionManagement.resumeCancelledSubscription,
|
|
64
|
+
// License Key Management
|
|
65
|
+
validateLicense: licenseKeyManagement.validateLicense,
|
|
66
|
+
getLicenseDetails: licenseKeyManagement.getLicenseDetails,
|
|
67
|
+
activateLicense: licenseKeyManagement.activateLicense,
|
|
68
|
+
deactivateLicense: licenseKeyManagement.deactivateLicense,
|
|
69
|
+
// Customer Management
|
|
70
|
+
getCustomerByEmail: customerManagement.getCustomerByEmail,
|
|
71
|
+
getSubscriptionsForUser: customerManagement.getSubscriptionsForUser,
|
|
72
|
+
// Webhook Management
|
|
73
|
+
createWebhook: webhookManagement.createWebhook,
|
|
74
|
+
deleteWebhook: webhookManagement.deleteWebhook,
|
|
75
|
+
// Deduplication
|
|
76
|
+
dedupBackend,
|
|
77
|
+
// Health Check
|
|
78
|
+
healthCheck,
|
|
79
|
+
};
|
|
80
|
+
return billing;
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=createBilling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createBilling.js","sourceRoot":"","sources":["../src/createBilling.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customers.d.ts","sourceRoot":"","sources":["../src/customers.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAkB,MAAM,YAAY,CAAC;AAGrE,wBAAgB,wBAAwB,IAAI,kBAAkB,CAgF7D"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { listCustomers } from "@lemonsqueezy/lemonsqueezy.js";
|
|
2
|
+
import { withRetry } from "./retry.js";
|
|
3
|
+
export function createCustomerManagement() {
|
|
4
|
+
const getCustomerByEmail = async (email) => {
|
|
5
|
+
const response = await withRetry(() => listCustomers({ filter: { email } }), "listCustomers");
|
|
6
|
+
if (response.error || !response.data?.data || response.data.data.length === 0) {
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
const customer = response.data.data[0];
|
|
10
|
+
const attrs = customer.attributes;
|
|
11
|
+
// Get subscriptions for this customer
|
|
12
|
+
const subscriptionsResponse = await withRetry(() => listCustomers({ filter: { email }, include: ["subscriptions"] }), "listCustomersWithSubscriptions");
|
|
13
|
+
const subscriptions = subscriptionsResponse.data?.included
|
|
14
|
+
?.filter(item => item.type === "subscription")
|
|
15
|
+
.map(item => ({
|
|
16
|
+
id: item.id,
|
|
17
|
+
status: String(item.attributes.status ?? ""),
|
|
18
|
+
variantId: String(item.attributes.variant_id ?? ""),
|
|
19
|
+
productId: String(item.attributes.product_id ?? ""),
|
|
20
|
+
price: Number(item.attributes.renews_at_price ?? 0),
|
|
21
|
+
nextBillingDate: String(item.attributes.renews_at ?? ""),
|
|
22
|
+
endsAt: String(item.attributes.ends_at ?? ""),
|
|
23
|
+
})) || [];
|
|
24
|
+
const billingAddress = attrs.billing_address ? {
|
|
25
|
+
name: attrs.billing_address.name || attrs.name || "",
|
|
26
|
+
address1: attrs.billing_address.address || "",
|
|
27
|
+
address2: attrs.billing_address.address2 || undefined,
|
|
28
|
+
city: attrs.billing_address.city || "",
|
|
29
|
+
country: attrs.billing_address.country || "",
|
|
30
|
+
zip: attrs.billing_address.zip || "",
|
|
31
|
+
state: attrs.billing_address.state || undefined,
|
|
32
|
+
} : undefined;
|
|
33
|
+
return {
|
|
34
|
+
id: customer.id,
|
|
35
|
+
email: attrs.email,
|
|
36
|
+
name: attrs.name,
|
|
37
|
+
billingAddress,
|
|
38
|
+
subscriptions,
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const getSubscriptionsForUser = async (userId) => {
|
|
42
|
+
// This would typically require storing the userId in custom_data
|
|
43
|
+
// For now, we'll search by email if userId is an email
|
|
44
|
+
if (userId.includes('@')) {
|
|
45
|
+
const customer = await getCustomerByEmail(userId);
|
|
46
|
+
return customer?.subscriptions || [];
|
|
47
|
+
}
|
|
48
|
+
// If userId is not an email, you would need to implement
|
|
49
|
+
// a lookup mechanism based on your user database
|
|
50
|
+
return [];
|
|
51
|
+
};
|
|
52
|
+
return {
|
|
53
|
+
getCustomerByEmail,
|
|
54
|
+
getSubscriptionsForUser,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
//# sourceMappingURL=customers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"customers.js","sourceRoot":"","sources":["../src/customers.ts"],"names":[],"mappings":"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"}
|
package/dist/dedup.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { DedupBackend, DedupConfig } from "./types.js";
|
|
2
|
+
export interface RedisClient {
|
|
3
|
+
exists(key: string): Promise<number>;
|
|
4
|
+
setex(key: string, ttlSeconds: number, value: string): Promise<void>;
|
|
5
|
+
}
|
|
6
|
+
export interface DatabaseClient {
|
|
7
|
+
webhookDedup: {
|
|
8
|
+
findUnique: (args: {
|
|
9
|
+
where: {
|
|
10
|
+
key: string;
|
|
11
|
+
};
|
|
12
|
+
select: {
|
|
13
|
+
expiresAt: true;
|
|
14
|
+
};
|
|
15
|
+
}) => Promise<{
|
|
16
|
+
expiresAt: Date;
|
|
17
|
+
} | null>;
|
|
18
|
+
delete: (args: {
|
|
19
|
+
where: {
|
|
20
|
+
key: string;
|
|
21
|
+
};
|
|
22
|
+
}) => Promise<void>;
|
|
23
|
+
upsert: (args: {
|
|
24
|
+
where: {
|
|
25
|
+
key: string;
|
|
26
|
+
};
|
|
27
|
+
update: {
|
|
28
|
+
expiresAt: Date;
|
|
29
|
+
};
|
|
30
|
+
create: {
|
|
31
|
+
key: string;
|
|
32
|
+
expiresAt: Date;
|
|
33
|
+
};
|
|
34
|
+
}) => Promise<void>;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export declare class InMemoryDedupBackend implements DedupBackend {
|
|
38
|
+
private ttlMs;
|
|
39
|
+
private seen;
|
|
40
|
+
constructor(ttlMs?: number);
|
|
41
|
+
private cleanup;
|
|
42
|
+
isDuplicate(key: string): Promise<boolean>;
|
|
43
|
+
markDuplicate(key: string, _ttlMs: number): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
export declare class RedisDedupBackend implements DedupBackend {
|
|
46
|
+
private defaultTtlMs;
|
|
47
|
+
private redis;
|
|
48
|
+
constructor(redisClient: RedisClient, defaultTtlMs?: number);
|
|
49
|
+
isDuplicate(key: string): Promise<boolean>;
|
|
50
|
+
markDuplicate(key: string, ttlMs: number): Promise<void>;
|
|
51
|
+
}
|
|
52
|
+
export declare class DatabaseDedupBackend implements DedupBackend {
|
|
53
|
+
private defaultTtlMs;
|
|
54
|
+
private db;
|
|
55
|
+
constructor(databaseClient: DatabaseClient, defaultTtlMs?: number);
|
|
56
|
+
isDuplicate(key: string): Promise<boolean>;
|
|
57
|
+
markDuplicate(key: string, ttlMs: number): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
export declare function createDedupBackend(config?: DedupConfig): DedupBackend;
|
|
60
|
+
//# sourceMappingURL=dedup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup.d.ts","sourceRoot":"","sources":["../src/dedup.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,WAAW,WAAW;IAC1B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACrC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;AAED,MAAM,WAAW,cAAc;IAC7B,YAAY,EAAE;QACZ,UAAU,EAAE,CAAC,IAAI,EAAE;YAAE,KAAK,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,MAAM,EAAE;gBAAE,SAAS,EAAE,IAAI,CAAA;aAAE,CAAA;SAAE,KAAK,OAAO,CAAC;YAAE,SAAS,EAAE,IAAI,CAAA;SAAE,GAAG,IAAI,CAAC,CAAC;QACnH,MAAM,EAAE,CAAC,IAAI,EAAE;YAAE,KAAK,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;QAC5D,MAAM,EAAE,CAAC,IAAI,EAAE;YAAE,KAAK,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAA;aAAE,CAAC;YAAC,MAAM,EAAE;gBAAE,SAAS,EAAE,IAAI,CAAA;aAAE,CAAC;YAAC,MAAM,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAC;gBAAC,SAAS,EAAE,IAAI,CAAA;aAAE,CAAA;SAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;KACpI,CAAC;CACH;AAED,qBAAa,oBAAqB,YAAW,YAAY;IAG3C,OAAO,CAAC,KAAK;IAFzB,OAAO,CAAC,IAAI,CAA6B;gBAErB,KAAK,GAAE,MAAkB;IAE7C,OAAO,CAAC,OAAO;IAOT,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1C,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGhE;AAED,qBAAa,iBAAkB,YAAW,YAAY;IAGd,OAAO,CAAC,YAAY;IAF1D,OAAO,CAAC,KAAK,CAAc;gBAEf,WAAW,EAAE,WAAW,EAAU,YAAY,GAAE,MAAkB;IAIxE,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAK1C,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAI/D;AAED,qBAAa,oBAAqB,YAAW,YAAY;IAGX,OAAO,CAAC,YAAY;IAFhE,OAAO,CAAC,EAAE,CAAiB;gBAEf,cAAc,EAAE,cAAc,EAAU,YAAY,GAAE,MAAkB;IAI9E,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAgB1C,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAU/D;AAED,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,YAAY,CAMrE"}
|
package/dist/dedup.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export class InMemoryDedupBackend {
|
|
2
|
+
ttlMs;
|
|
3
|
+
seen = new Map();
|
|
4
|
+
constructor(ttlMs = 3_600_000) {
|
|
5
|
+
this.ttlMs = ttlMs;
|
|
6
|
+
}
|
|
7
|
+
cleanup() {
|
|
8
|
+
const now = Date.now();
|
|
9
|
+
for (const [key, timestamp] of this.seen) {
|
|
10
|
+
if (now - timestamp > this.ttlMs)
|
|
11
|
+
this.seen.delete(key);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
async isDuplicate(key) {
|
|
15
|
+
this.cleanup();
|
|
16
|
+
return this.seen.has(key);
|
|
17
|
+
}
|
|
18
|
+
async markDuplicate(key, _ttlMs) {
|
|
19
|
+
this.seen.set(key, Date.now());
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export class RedisDedupBackend {
|
|
23
|
+
defaultTtlMs;
|
|
24
|
+
redis;
|
|
25
|
+
constructor(redisClient, defaultTtlMs = 3_600_000) {
|
|
26
|
+
this.defaultTtlMs = defaultTtlMs;
|
|
27
|
+
this.redis = redisClient;
|
|
28
|
+
}
|
|
29
|
+
async isDuplicate(key) {
|
|
30
|
+
const exists = await this.redis.exists(key);
|
|
31
|
+
return exists === 1;
|
|
32
|
+
}
|
|
33
|
+
async markDuplicate(key, ttlMs) {
|
|
34
|
+
const ttlSeconds = Math.ceil((ttlMs || this.defaultTtlMs) / 1000);
|
|
35
|
+
await this.redis.setex(key, ttlSeconds, "1");
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export class DatabaseDedupBackend {
|
|
39
|
+
defaultTtlMs;
|
|
40
|
+
db;
|
|
41
|
+
constructor(databaseClient, defaultTtlMs = 3_600_000) {
|
|
42
|
+
this.defaultTtlMs = defaultTtlMs;
|
|
43
|
+
this.db = databaseClient;
|
|
44
|
+
}
|
|
45
|
+
async isDuplicate(key) {
|
|
46
|
+
const record = await this.db.webhookDedup.findUnique({
|
|
47
|
+
where: { key },
|
|
48
|
+
select: { expiresAt: true }
|
|
49
|
+
});
|
|
50
|
+
if (!record)
|
|
51
|
+
return false;
|
|
52
|
+
if (new Date() > record.expiresAt) {
|
|
53
|
+
await this.db.webhookDedup.delete({ where: { key } });
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
return true;
|
|
57
|
+
}
|
|
58
|
+
async markDuplicate(key, ttlMs) {
|
|
59
|
+
const ttl = ttlMs || this.defaultTtlMs;
|
|
60
|
+
const expiresAt = new Date(Date.now() + ttl);
|
|
61
|
+
await this.db.webhookDedup.upsert({
|
|
62
|
+
where: { key },
|
|
63
|
+
update: { expiresAt },
|
|
64
|
+
create: { key, expiresAt }
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function createDedupBackend(config) {
|
|
69
|
+
if (config?.backend) {
|
|
70
|
+
return config.backend;
|
|
71
|
+
}
|
|
72
|
+
return new InMemoryDedupBackend(config?.ttlMs);
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=dedup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dedup.js","sourceRoot":"","sources":["../src/dedup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ExpressRouterOptions, Plan, CheckoutParams, WebhookPayload } from "./types.js";
|
|
2
|
+
interface RouterDeps {
|
|
3
|
+
plans: Plan[];
|
|
4
|
+
createCheckout: (params: CheckoutParams) => Promise<string>;
|
|
5
|
+
verifyWebhook: (rawBody: string, signature: string) => boolean;
|
|
6
|
+
handleWebhook: (payload: WebhookPayload) => Promise<{
|
|
7
|
+
dispatched: string | null;
|
|
8
|
+
skipped: boolean;
|
|
9
|
+
}>;
|
|
10
|
+
}
|
|
11
|
+
export declare function createExpressRouter(deps: RouterDeps, options: ExpressRouterOptions): unknown;
|
|
12
|
+
export {};
|
|
13
|
+
//# sourceMappingURL=express.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"express.d.ts","sourceRoot":"","sources":["../src/express.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE7F,UAAU,UAAU;IAClB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,cAAc,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAC5D,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC;IAC/D,aAAa,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,OAAO,CAAC;QAAE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;CACtG;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CA8F5F"}
|