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,22 @@
|
|
|
1
|
+
import "dotenv/config";
|
|
2
|
+
declare class BillingWizard {
|
|
3
|
+
private state;
|
|
4
|
+
private generateSecret;
|
|
5
|
+
run(): Promise<void>;
|
|
6
|
+
private getAvailableApiKeys;
|
|
7
|
+
private stepApiKey;
|
|
8
|
+
private stepStoreSelection;
|
|
9
|
+
private stepProductSelection;
|
|
10
|
+
private stepWebhookSetup;
|
|
11
|
+
private stepConfiguration;
|
|
12
|
+
private stepGenerateFiles;
|
|
13
|
+
private generateFiles;
|
|
14
|
+
private runValidationTests;
|
|
15
|
+
private generateConfigContent;
|
|
16
|
+
private offerRealCycleFlow;
|
|
17
|
+
private runRealCycleFlow;
|
|
18
|
+
private generateExampleContent;
|
|
19
|
+
}
|
|
20
|
+
export declare function runWizard(): Promise<void>;
|
|
21
|
+
export { BillingWizard };
|
|
22
|
+
//# sourceMappingURL=wizard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wizard.d.ts","sourceRoot":"","sources":["../../src/wizard.ts"],"names":[],"mappings":"AAuBA,OAAO,eAAe,CAAC;AA+DvB,cAAM,aAAa;IACjB,OAAO,CAAC,KAAK,CAUX;IAEF,OAAO,CAAC,cAAc;IAIhB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB1B,OAAO,CAAC,mBAAmB;YAqBb,UAAU;YAsEV,kBAAkB;YAiClB,oBAAoB;YAoDpB,gBAAgB;YAiEhB,iBAAiB;YA6CjB,iBAAiB;YA8BjB,aAAa;YA2Bb,kBAAkB;IAoDhC,OAAO,CAAC,qBAAqB;YAgEf,kBAAkB;YAmClB,gBAAgB;IAyD9B,OAAO,CAAC,sBAAsB;CAoD/B;AAQD,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAG/C;AAED,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
import prompts from "prompts";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { createWriteStream } from "node:fs";
|
|
4
|
+
import { resolve } from "node:path";
|
|
5
|
+
import { createBilling } from "@core/index.js";
|
|
6
|
+
import { fetchProducts, flattenPlans } from "@core/plans.js";
|
|
7
|
+
import "dotenv/config";
|
|
8
|
+
// ASCII Art Banner (no emojis)
|
|
9
|
+
const banner = `
|
|
10
|
+
${chalk.bold("Lemon Squeezy Billing Setup")}
|
|
11
|
+
${"─".repeat(35)}
|
|
12
|
+
Quick setup for your billing integration
|
|
13
|
+
`;
|
|
14
|
+
// Animation helpers - 9 dots snake animation
|
|
15
|
+
class LoadingAnimation {
|
|
16
|
+
interval = null;
|
|
17
|
+
frame = 0;
|
|
18
|
+
dots = 9;
|
|
19
|
+
chars = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇'];
|
|
20
|
+
start(message = "Loading") {
|
|
21
|
+
this.stop();
|
|
22
|
+
process.stdout.write(`\n${message} `);
|
|
23
|
+
this.interval = setInterval(() => {
|
|
24
|
+
const char = this.chars[this.frame % this.dots];
|
|
25
|
+
const dots = '.'.repeat(this.frame % this.dots);
|
|
26
|
+
const spaces = ' '.repeat(this.dots - (this.frame % this.dots) - 1);
|
|
27
|
+
process.stdout.write(`\r${message} ${char}${dots}${spaces} `);
|
|
28
|
+
this.frame++;
|
|
29
|
+
}, 80);
|
|
30
|
+
}
|
|
31
|
+
stop(message) {
|
|
32
|
+
if (this.interval) {
|
|
33
|
+
clearInterval(this.interval);
|
|
34
|
+
this.interval = null;
|
|
35
|
+
}
|
|
36
|
+
if (message) {
|
|
37
|
+
process.stdout.write(`\r${message}\n`);
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
process.stdout.write('\n');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
const loading = new LoadingAnimation();
|
|
45
|
+
class BillingWizard {
|
|
46
|
+
state = {
|
|
47
|
+
apiKey: "",
|
|
48
|
+
stores: [],
|
|
49
|
+
selectedStoreIds: [],
|
|
50
|
+
products: [],
|
|
51
|
+
selectedProductIds: [],
|
|
52
|
+
webhookEvents: [],
|
|
53
|
+
cachePath: "./billing-cache.json",
|
|
54
|
+
webhookSecret: this.generateSecret(),
|
|
55
|
+
loggerPath: "./billing.log",
|
|
56
|
+
};
|
|
57
|
+
generateSecret() {
|
|
58
|
+
return "ls_".concat(Math.random().toString(36).slice(2, 15));
|
|
59
|
+
}
|
|
60
|
+
async run() {
|
|
61
|
+
console.log(banner);
|
|
62
|
+
try {
|
|
63
|
+
await this.stepApiKey();
|
|
64
|
+
await this.stepStoreSelection();
|
|
65
|
+
await this.stepProductSelection();
|
|
66
|
+
await this.stepWebhookSetup();
|
|
67
|
+
await this.stepConfiguration();
|
|
68
|
+
await this.stepGenerateFiles();
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
if (error instanceof Error && error.message === 'Aborted') {
|
|
72
|
+
console.log("\n[x] Wizard cancelled");
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
console.error("\n[x] Error:", error);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
getAvailableApiKeys() {
|
|
80
|
+
const keys = [];
|
|
81
|
+
if (process.env.LS_TEST_API_KEY) {
|
|
82
|
+
keys.push({
|
|
83
|
+
name: "LS_TEST_API_KEY",
|
|
84
|
+
value: process.env.LS_TEST_API_KEY,
|
|
85
|
+
description: "Test mode API key (sandbox)"
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
if (process.env.LS_LIVE_API_KEY) {
|
|
89
|
+
keys.push({
|
|
90
|
+
name: "LS_LIVE_API_KEY",
|
|
91
|
+
value: process.env.LS_LIVE_API_KEY,
|
|
92
|
+
description: "Live mode API key (production)"
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return keys;
|
|
96
|
+
}
|
|
97
|
+
async stepApiKey() {
|
|
98
|
+
console.log("\n" + chalk.dim("Navigation: ENTER to proceed, ESC to go back/exit"));
|
|
99
|
+
console.log(chalk.dim("Tip: Set LS_TEST_API_KEY, LS_LIVE_API_KEY, or LS_WEBHOOK_SECRET in .env"));
|
|
100
|
+
const availableKeys = this.getAvailableApiKeys();
|
|
101
|
+
let apiKey = null;
|
|
102
|
+
if (availableKeys.length > 0) {
|
|
103
|
+
if (availableKeys.length === 1) {
|
|
104
|
+
console.log(`\n[+] Found ${availableKeys[0].name} in environment`);
|
|
105
|
+
apiKey = availableKeys[0].value;
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.log("\n" + chalk.dim("Select API keys (use SPACE to select, ENTER to submit):"));
|
|
109
|
+
const selected = await prompts({
|
|
110
|
+
type: "multiselect",
|
|
111
|
+
name: "apiKeys",
|
|
112
|
+
message: "Choose API keys:",
|
|
113
|
+
choices: availableKeys.map(k => ({
|
|
114
|
+
title: `${k.name} (${k.description})`,
|
|
115
|
+
value: k.value
|
|
116
|
+
})),
|
|
117
|
+
instructions: false,
|
|
118
|
+
hint: "Space to select, Enter to submit",
|
|
119
|
+
onState: (state) => {
|
|
120
|
+
if (state.aborted)
|
|
121
|
+
throw new Error('Aborted');
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
// Use the first selected key, or fallback to the first available if none selected
|
|
125
|
+
apiKey = selected.apiKeys.length > 0 ? selected.apiKeys[0] : availableKeys[0].value;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
if (!apiKey) {
|
|
129
|
+
const response = await prompts({
|
|
130
|
+
type: "text",
|
|
131
|
+
name: "apiKey",
|
|
132
|
+
message: "Enter your Lemon Squeezy API key:",
|
|
133
|
+
validate: (value) => value.length > 0 || "API key is required",
|
|
134
|
+
onState: (state) => {
|
|
135
|
+
if (state.aborted)
|
|
136
|
+
throw new Error('Aborted');
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
apiKey = response.apiKey;
|
|
140
|
+
}
|
|
141
|
+
if (!apiKey) {
|
|
142
|
+
console.log("[x] API key is required");
|
|
143
|
+
return this.stepApiKey();
|
|
144
|
+
}
|
|
145
|
+
loading.start("Validating API key");
|
|
146
|
+
try {
|
|
147
|
+
// Test the API key by creating a billing instance
|
|
148
|
+
const billing = await createBilling({
|
|
149
|
+
apiKey: apiKey,
|
|
150
|
+
callbacks: { onPurchase: async () => { } }
|
|
151
|
+
});
|
|
152
|
+
loading.stop(`[+] Found ${billing.stores.length} store(s)`);
|
|
153
|
+
this.state.apiKey = apiKey;
|
|
154
|
+
this.state.stores = billing.stores;
|
|
155
|
+
}
|
|
156
|
+
catch (error) {
|
|
157
|
+
loading.stop("[x] Invalid API key. Please try again.");
|
|
158
|
+
return this.stepApiKey();
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
async stepStoreSelection() {
|
|
162
|
+
console.log("\n" + chalk.dim("Navigation: ENTER to proceed, ESC to go back"));
|
|
163
|
+
if (this.state.stores.length === 0) {
|
|
164
|
+
console.log("[x] No stores found. Please check your API key.");
|
|
165
|
+
return this.stepApiKey();
|
|
166
|
+
}
|
|
167
|
+
console.log("\n" + chalk.dim("Select stores (use SPACE to select, ENTER to submit):"));
|
|
168
|
+
const response = await prompts({
|
|
169
|
+
type: "multiselect",
|
|
170
|
+
name: "stores",
|
|
171
|
+
message: "Select stores to use:",
|
|
172
|
+
choices: this.state.stores.map(store => ({
|
|
173
|
+
title: `${store.name} (${store.id})`,
|
|
174
|
+
value: store.id
|
|
175
|
+
})),
|
|
176
|
+
instructions: false,
|
|
177
|
+
hint: "Space to select, Enter to submit",
|
|
178
|
+
onState: (state) => {
|
|
179
|
+
if (state.aborted)
|
|
180
|
+
throw new Error('Aborted');
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
this.state.selectedStoreIds = response.stores;
|
|
184
|
+
if (this.state.selectedStoreIds.length === 0) {
|
|
185
|
+
console.log("[x] Please select at least one store");
|
|
186
|
+
return this.stepStoreSelection();
|
|
187
|
+
}
|
|
188
|
+
console.log(`[+] Selected ${this.state.selectedStoreIds.length} store(s)`);
|
|
189
|
+
}
|
|
190
|
+
async stepProductSelection() {
|
|
191
|
+
console.log("\n" + chalk.dim("Navigation: ENTER to proceed, ESC to go back"));
|
|
192
|
+
loading.start("Fetching products");
|
|
193
|
+
const allProducts = [];
|
|
194
|
+
for (const storeId of this.state.selectedStoreIds) {
|
|
195
|
+
try {
|
|
196
|
+
const products = await fetchProducts(storeId);
|
|
197
|
+
allProducts.push(...products);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
loading.stop(`[x] Failed to fetch products for store ${storeId}`);
|
|
201
|
+
return this.stepStoreSelection();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
loading.stop("");
|
|
205
|
+
if (allProducts.length === 0) {
|
|
206
|
+
console.log("[x] No products found. Please create products in your Lemon Squeezy dashboard.");
|
|
207
|
+
return this.stepStoreSelection();
|
|
208
|
+
}
|
|
209
|
+
this.state.products = allProducts;
|
|
210
|
+
const plans = flattenPlans(allProducts);
|
|
211
|
+
console.log("\n" + chalk.dim("Select products (use SPACE to select, ENTER to submit):"));
|
|
212
|
+
const response = await prompts({
|
|
213
|
+
type: "multiselect",
|
|
214
|
+
name: "products",
|
|
215
|
+
message: "Select products to include:",
|
|
216
|
+
choices: plans.map(plan => ({
|
|
217
|
+
title: `${plan.name} - ${plan.variantName} (${plan.priceFormatted})`,
|
|
218
|
+
value: plan.variantId
|
|
219
|
+
})),
|
|
220
|
+
instructions: false,
|
|
221
|
+
hint: "Space to select, Enter to submit",
|
|
222
|
+
onState: (state) => {
|
|
223
|
+
if (state.aborted)
|
|
224
|
+
throw new Error('Aborted');
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
this.state.selectedProductIds = response.products;
|
|
228
|
+
if (this.state.selectedProductIds.length === 0) {
|
|
229
|
+
console.log("[x] Please select at least one product");
|
|
230
|
+
return this.stepProductSelection();
|
|
231
|
+
}
|
|
232
|
+
console.log(`[+] Selected ${this.state.selectedProductIds.length} product(s)`);
|
|
233
|
+
}
|
|
234
|
+
async stepWebhookSetup() {
|
|
235
|
+
console.log("\n" + chalk.dim("Navigation: ENTER to proceed, ESC to skip"));
|
|
236
|
+
console.log("\n" + chalk.dim("Create webhook endpoint? (use SPACE to select, ENTER to submit):"));
|
|
237
|
+
const response = await prompts({
|
|
238
|
+
type: "multiselect",
|
|
239
|
+
name: "webhook",
|
|
240
|
+
message: "Create webhook endpoint:",
|
|
241
|
+
choices: [
|
|
242
|
+
{ title: "Yes, create webhook", value: "yes" },
|
|
243
|
+
{ title: "No, skip webhook setup", value: "no" }
|
|
244
|
+
],
|
|
245
|
+
instructions: false,
|
|
246
|
+
hint: "Space to select, Enter to submit",
|
|
247
|
+
onState: (state) => {
|
|
248
|
+
if (state.aborted)
|
|
249
|
+
throw new Error('Aborted');
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
if (!response.webhook.includes("yes")) {
|
|
253
|
+
console.log("[-] Skipping webhook setup");
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
console.log("\n" + chalk.dim("Enter webhook URL:"));
|
|
257
|
+
const urlResponse = await prompts({
|
|
258
|
+
type: "text",
|
|
259
|
+
name: "url",
|
|
260
|
+
message: "Webhook URL:",
|
|
261
|
+
initial: "https://your-domain.com/webhook",
|
|
262
|
+
validate: (value) => value.length > 0 || "URL is required",
|
|
263
|
+
onState: (state) => {
|
|
264
|
+
if (state.aborted)
|
|
265
|
+
throw new Error('Aborted');
|
|
266
|
+
}
|
|
267
|
+
});
|
|
268
|
+
this.state.webhookUrl = urlResponse.url;
|
|
269
|
+
console.log("\n" + chalk.dim("Select webhook events (use SPACE to select, ENTER to submit):"));
|
|
270
|
+
const eventsResponse = await prompts({
|
|
271
|
+
type: "multiselect",
|
|
272
|
+
name: "events",
|
|
273
|
+
message: "Select webhook events:",
|
|
274
|
+
choices: [
|
|
275
|
+
{ title: "Order Created", value: "order_created" },
|
|
276
|
+
{ title: "Order Refunded", value: "order_refunded" },
|
|
277
|
+
{ title: "Subscription Created", value: "subscription_created" },
|
|
278
|
+
{ title: "Subscription Updated", value: "subscription_updated" },
|
|
279
|
+
{ title: "Subscription Cancelled", value: "subscription_cancelled" },
|
|
280
|
+
{ title: "Payment Failed", value: "subscription_payment_failed" },
|
|
281
|
+
{ title: "License Key Created", value: "license_key_created" },
|
|
282
|
+
{ title: "License Key Updated", value: "license_key_updated" }
|
|
283
|
+
],
|
|
284
|
+
instructions: false,
|
|
285
|
+
hint: "Space to select, Enter to submit",
|
|
286
|
+
onState: (state) => {
|
|
287
|
+
if (state.aborted)
|
|
288
|
+
throw new Error('Aborted');
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
this.state.webhookEvents = eventsResponse.events;
|
|
292
|
+
console.log("[+] Webhook configuration saved");
|
|
293
|
+
}
|
|
294
|
+
async stepConfiguration() {
|
|
295
|
+
console.log("\n" + chalk.dim("Navigation: ENTER to proceed, ESC to go back"));
|
|
296
|
+
console.log("\n" + chalk.dim("Enter cache file path:"));
|
|
297
|
+
const cacheResponse = await prompts({
|
|
298
|
+
type: "text",
|
|
299
|
+
name: "cachePath",
|
|
300
|
+
message: "Cache file path:",
|
|
301
|
+
initial: this.state.cachePath,
|
|
302
|
+
onState: (state) => {
|
|
303
|
+
if (state.aborted)
|
|
304
|
+
throw new Error('Aborted');
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
this.state.cachePath = cacheResponse.cachePath || this.state.cachePath;
|
|
308
|
+
console.log("\n" + chalk.dim("Enter the webhook secret you want:"));
|
|
309
|
+
const secretResponse = await prompts({
|
|
310
|
+
type: "text",
|
|
311
|
+
name: "webhookSecret",
|
|
312
|
+
message: "Webhook secret:",
|
|
313
|
+
initial: this.state.webhookSecret,
|
|
314
|
+
onState: (state) => {
|
|
315
|
+
if (state.aborted)
|
|
316
|
+
throw new Error('Aborted');
|
|
317
|
+
}
|
|
318
|
+
});
|
|
319
|
+
this.state.webhookSecret = secretResponse.webhookSecret || this.state.webhookSecret;
|
|
320
|
+
console.log("\n" + chalk.dim("Enter logger file path:"));
|
|
321
|
+
const loggerResponse = await prompts({
|
|
322
|
+
type: "text",
|
|
323
|
+
name: "loggerPath",
|
|
324
|
+
message: "Logger file path:",
|
|
325
|
+
initial: this.state.loggerPath,
|
|
326
|
+
onState: (state) => {
|
|
327
|
+
if (state.aborted)
|
|
328
|
+
throw new Error('Aborted');
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
this.state.loggerPath = loggerResponse.loggerPath || this.state.loggerPath;
|
|
332
|
+
console.log("[+] Configuration saved");
|
|
333
|
+
}
|
|
334
|
+
async stepGenerateFiles() {
|
|
335
|
+
console.log("\n" + chalk.dim("Navigation: ENTER to generate, ESC to exit"));
|
|
336
|
+
console.log("\n" + chalk.dim("Generate configuration files? (use SPACE to select, ENTER to submit):"));
|
|
337
|
+
const response = await prompts({
|
|
338
|
+
type: "multiselect",
|
|
339
|
+
name: "generate",
|
|
340
|
+
message: "Generate configuration files:",
|
|
341
|
+
choices: [
|
|
342
|
+
{ title: "Yes, generate files", value: "yes" },
|
|
343
|
+
{ title: "No, exit without generating", value: "no" }
|
|
344
|
+
],
|
|
345
|
+
instructions: false,
|
|
346
|
+
hint: "Space to select, Enter to submit",
|
|
347
|
+
onState: (state) => {
|
|
348
|
+
if (state.aborted)
|
|
349
|
+
throw new Error('Aborted');
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
if (!response.generate.includes("yes")) {
|
|
353
|
+
console.log("[-] Exiting without generating files");
|
|
354
|
+
process.exit(0);
|
|
355
|
+
}
|
|
356
|
+
await this.generateFiles();
|
|
357
|
+
// Run validation tests after file generation
|
|
358
|
+
await this.runValidationTests();
|
|
359
|
+
}
|
|
360
|
+
async generateFiles() {
|
|
361
|
+
const configContent = this.generateConfigContent();
|
|
362
|
+
const exampleContent = this.generateExampleContent();
|
|
363
|
+
const configPath = resolve(process.cwd(), "billing-config.ts");
|
|
364
|
+
const examplePath = resolve(process.cwd(), "example.ts");
|
|
365
|
+
// Write config file
|
|
366
|
+
const configStream = createWriteStream(configPath);
|
|
367
|
+
configStream.write(configContent);
|
|
368
|
+
configStream.end();
|
|
369
|
+
// Write example file
|
|
370
|
+
const exampleStream = createWriteStream(examplePath);
|
|
371
|
+
exampleStream.write(exampleContent);
|
|
372
|
+
exampleStream.end();
|
|
373
|
+
console.log("\n[+] Files generated successfully!");
|
|
374
|
+
console.log(`📁 ${configPath}`);
|
|
375
|
+
console.log(`📁 ${examplePath}`);
|
|
376
|
+
console.log("\n🚀 Next steps:");
|
|
377
|
+
console.log("1. Review the generated files");
|
|
378
|
+
console.log("2. Run: node example.ts");
|
|
379
|
+
console.log("3. Start building your billing integration!");
|
|
380
|
+
}
|
|
381
|
+
async runValidationTests() {
|
|
382
|
+
console.log("\n" + chalk.dim("Running validation tests..."));
|
|
383
|
+
try {
|
|
384
|
+
const { execSync } = await import('child_process');
|
|
385
|
+
const fs = await import('node:fs');
|
|
386
|
+
const path = await import('node:path');
|
|
387
|
+
loading.start("Testing TypeScript compilation");
|
|
388
|
+
execSync('pnpm typecheck', { stdio: 'pipe' });
|
|
389
|
+
loading.stop("[+] TypeScript compilation passed");
|
|
390
|
+
loading.start("Testing build process");
|
|
391
|
+
execSync('pnpm build', { stdio: 'pipe' });
|
|
392
|
+
loading.stop("[+] Build process passed");
|
|
393
|
+
loading.start("Testing example file syntax");
|
|
394
|
+
execSync('node --check example.ts', { stdio: 'pipe' });
|
|
395
|
+
loading.stop("[+] Example file syntax valid");
|
|
396
|
+
loading.start("Testing billing configuration");
|
|
397
|
+
const configPath = path.resolve(process.cwd(), 'billing-config.ts');
|
|
398
|
+
if (!fs.existsSync(configPath)) {
|
|
399
|
+
throw new Error("billing-config.ts file not found");
|
|
400
|
+
}
|
|
401
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
402
|
+
if (!configContent.includes('export const billingConfig')) {
|
|
403
|
+
throw new Error("billing-config.ts does not export billingConfig");
|
|
404
|
+
}
|
|
405
|
+
loading.stop("[+] Billing configuration valid");
|
|
406
|
+
console.log("\n[+] All validation tests passed! ✅");
|
|
407
|
+
console.log("Your billing integration is ready to use.");
|
|
408
|
+
// Check if sandbox API key and offer real cycle flow
|
|
409
|
+
await this.offerRealCycleFlow();
|
|
410
|
+
}
|
|
411
|
+
catch (error) {
|
|
412
|
+
console.log("\n[x] Validation tests failed:");
|
|
413
|
+
console.error("Error:", error instanceof Error ? error.message : error);
|
|
414
|
+
console.log("\nPlease review the generated files and fix any issues.");
|
|
415
|
+
console.log("You can run the following commands to debug:");
|
|
416
|
+
console.log(" pnpm typecheck");
|
|
417
|
+
console.log(" pnpm build");
|
|
418
|
+
console.log(" node --check example.ts");
|
|
419
|
+
process.exit(1);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
generateConfigContent() {
|
|
423
|
+
const lines = [];
|
|
424
|
+
lines.push(`import type { BillingConfig, PurchaseEvent, RefundEvent, SubscriptionEvent, PaymentFailedEvent, LicenseKeyEvent, SubscriptionPausedEvent, SubscriptionResumedEvent, SubscriptionPaymentSuccessEvent, SubscriptionPaymentRecoveredEvent } from "./types";`);
|
|
425
|
+
lines.push(``);
|
|
426
|
+
lines.push(`export const billingConfig: BillingConfig = {`);
|
|
427
|
+
lines.push(` apiKey: process.env.LEMON_SQUEEZY_API_KEY ?? (() => { throw new Error("LEMON_SQUEEZY_API_KEY is required"); })(),`);
|
|
428
|
+
lines.push(` storeId: "${this.state.selectedStoreIds[0]}",`);
|
|
429
|
+
lines.push(` webhookSecret: "${this.state.webhookSecret}",`);
|
|
430
|
+
lines.push(` cachePath: "${this.state.cachePath}",`);
|
|
431
|
+
lines.push(` logger: { filePath: "${this.state.loggerPath}" },`);
|
|
432
|
+
lines.push(` callbacks: {`);
|
|
433
|
+
lines.push(` onPurchase: async (event: PurchaseEvent) => {`);
|
|
434
|
+
lines.push(` console.log("Purchase:", event);`);
|
|
435
|
+
lines.push(` },`);
|
|
436
|
+
lines.push(` onRefund: async (event: RefundEvent) => {`);
|
|
437
|
+
lines.push(` console.log("Refund:", event);`);
|
|
438
|
+
lines.push(` },`);
|
|
439
|
+
lines.push(` onSubscriptionCreated: async (event: SubscriptionEvent) => {`);
|
|
440
|
+
lines.push(` console.log("Subscription created:", event);`);
|
|
441
|
+
lines.push(` },`);
|
|
442
|
+
lines.push(` onSubscriptionUpdated: async (event: SubscriptionEvent) => {`);
|
|
443
|
+
lines.push(` console.log("Subscription updated:", event);`);
|
|
444
|
+
lines.push(` },`);
|
|
445
|
+
lines.push(` onSubscriptionCancelled: async (event: SubscriptionEvent) => {`);
|
|
446
|
+
lines.push(` console.log("Subscription cancelled:", event);`);
|
|
447
|
+
lines.push(` },`);
|
|
448
|
+
lines.push(` onPaymentFailed: async (event: PaymentFailedEvent) => {`);
|
|
449
|
+
lines.push(` console.log("Payment failed:", event);`);
|
|
450
|
+
lines.push(` },`);
|
|
451
|
+
lines.push(` onSubscriptionPaused: async (event: SubscriptionPausedEvent) => {`);
|
|
452
|
+
lines.push(` console.log("Subscription paused:", event);`);
|
|
453
|
+
lines.push(` },`);
|
|
454
|
+
lines.push(` onSubscriptionResumed: async (event: SubscriptionResumedEvent) => {`);
|
|
455
|
+
lines.push(` console.log("Subscription resumed:", event);`);
|
|
456
|
+
lines.push(` },`);
|
|
457
|
+
lines.push(` onSubscriptionPaymentSuccess: async (event: SubscriptionPaymentSuccessEvent) => {`);
|
|
458
|
+
lines.push(` console.log("Subscription payment success:", event);`);
|
|
459
|
+
lines.push(` },`);
|
|
460
|
+
lines.push(` onSubscriptionPaymentRecovered: async (event: SubscriptionPaymentRecoveredEvent) => {`);
|
|
461
|
+
lines.push(` console.log("Subscription payment recovered:", event);`);
|
|
462
|
+
lines.push(` },`);
|
|
463
|
+
lines.push(` onLicenseKeyCreated: async (event: LicenseKeyEvent) => {`);
|
|
464
|
+
lines.push(` console.log("License key created:", event);`);
|
|
465
|
+
lines.push(` },`);
|
|
466
|
+
lines.push(` onLicenseKeyUpdated: async (event: LicenseKeyEvent) => {`);
|
|
467
|
+
lines.push(` console.log("License key updated:", event);`);
|
|
468
|
+
lines.push(` }`);
|
|
469
|
+
lines.push(` }`);
|
|
470
|
+
lines.push(`};`);
|
|
471
|
+
if (this.state.webhookUrl) {
|
|
472
|
+
lines.push(``);
|
|
473
|
+
lines.push(`export const webhookConfig = {`);
|
|
474
|
+
lines.push(` url: "${this.state.webhookUrl}",`);
|
|
475
|
+
const eventsStr = this.state.webhookEvents.map(e => `"${e}"`).join(", ");
|
|
476
|
+
lines.push(` events: [${eventsStr}],`);
|
|
477
|
+
lines.push(` secret: "${this.state.webhookSecret.slice(0, 8)}...${this.state.webhookSecret.slice(-4)}"`);
|
|
478
|
+
lines.push(`};`);
|
|
479
|
+
}
|
|
480
|
+
return lines.join("\n");
|
|
481
|
+
}
|
|
482
|
+
async offerRealCycleFlow() {
|
|
483
|
+
console.log("\n" + chalk.dim("Checking API key type..."));
|
|
484
|
+
const isSandboxKey = this.state.apiKey.includes('test_') ||
|
|
485
|
+
this.state.apiKey.includes('sandbox_') ||
|
|
486
|
+
process.env.LS_TEST_API_KEY === this.state.apiKey;
|
|
487
|
+
const message = isSandboxKey
|
|
488
|
+
? "Run real cycle flow test (sandbox)?"
|
|
489
|
+
: "Run live tests (production API)?";
|
|
490
|
+
console.log("\n" + chalk.dim("Would you like to run tests? (use SPACE to select, ENTER to submit):"));
|
|
491
|
+
const response = await prompts({
|
|
492
|
+
type: "multiselect",
|
|
493
|
+
name: "runTest",
|
|
494
|
+
message,
|
|
495
|
+
choices: [
|
|
496
|
+
{ title: isSandboxKey ? "Yes, run sandbox test" : "Yes, run live tests", value: "yes" },
|
|
497
|
+
{ title: "No, skip tests", value: "no" }
|
|
498
|
+
],
|
|
499
|
+
instructions: false,
|
|
500
|
+
hint: "Space to select, Enter to submit",
|
|
501
|
+
onState: (state) => {
|
|
502
|
+
if (state.aborted)
|
|
503
|
+
throw new Error('Aborted');
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
if (!response.runTest.includes("yes")) {
|
|
507
|
+
console.log("[-] Skipping tests");
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
await this.runRealCycleFlow();
|
|
511
|
+
}
|
|
512
|
+
async runRealCycleFlow() {
|
|
513
|
+
console.log("\n" + chalk.dim("Running tests..."));
|
|
514
|
+
try {
|
|
515
|
+
const path = await import('node:path');
|
|
516
|
+
const { pathToFileURL } = await import('node:url');
|
|
517
|
+
const configPath = path.resolve(process.cwd(), 'billing-config.ts');
|
|
518
|
+
const billingConfig = await import(pathToFileURL(configPath).href);
|
|
519
|
+
const billing = await createBilling(billingConfig.billingConfig);
|
|
520
|
+
if (billing.plans.length === 0) {
|
|
521
|
+
console.log("[x] No products available for checkout test");
|
|
522
|
+
}
|
|
523
|
+
else {
|
|
524
|
+
loading.start("Testing checkout URL creation");
|
|
525
|
+
const testVariantId = billing.plans[0].variantId;
|
|
526
|
+
const checkoutUrl = await billing.createCheckout({
|
|
527
|
+
variantId: testVariantId,
|
|
528
|
+
email: "test@example.com",
|
|
529
|
+
userId: "test-user-123"
|
|
530
|
+
});
|
|
531
|
+
loading.stop(`[+] Checkout URL created: ${checkoutUrl.slice(0, 60)}...`);
|
|
532
|
+
console.log(chalk.dim(` Using variant: ${testVariantId}`));
|
|
533
|
+
}
|
|
534
|
+
loading.start("Testing product listing");
|
|
535
|
+
loading.stop(`[+] Found ${billing.plans.length} products`);
|
|
536
|
+
for (const plan of billing.plans.slice(0, 3)) {
|
|
537
|
+
console.log(chalk.dim(` - ${plan.name} / ${plan.variantName}: ${plan.priceFormatted} (${plan.variantId})`));
|
|
538
|
+
}
|
|
539
|
+
loading.start("Testing store listing");
|
|
540
|
+
loading.stop(`[+] Found ${billing.stores.length} stores`);
|
|
541
|
+
for (const store of billing.stores) {
|
|
542
|
+
console.log(chalk.dim(` - ${store.name} (${store.id})`));
|
|
543
|
+
}
|
|
544
|
+
loading.start("Testing customer portal URL");
|
|
545
|
+
loading.stop("[+] Customer portal functionality available");
|
|
546
|
+
const isSandbox = this.state.apiKey.includes('test_') || this.state.apiKey.includes('sandbox_');
|
|
547
|
+
console.log("\n[+] Tests completed successfully! ✅");
|
|
548
|
+
console.log(`All API operations are working correctly with your ${isSandbox ? 'sandbox' : 'live'} environment.`);
|
|
549
|
+
}
|
|
550
|
+
catch (error) {
|
|
551
|
+
console.log("\n[x] Real cycle flow test failed:");
|
|
552
|
+
if (error instanceof Error) {
|
|
553
|
+
console.error("Error:", error.message);
|
|
554
|
+
}
|
|
555
|
+
else if (error && typeof error === 'object') {
|
|
556
|
+
console.error("Error:", JSON.stringify(error, null, 2));
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
console.error("Error:", String(error));
|
|
560
|
+
}
|
|
561
|
+
console.log("\nThis is normal if your environment doesn't have products configured.");
|
|
562
|
+
console.log("Your billing integration is still ready to use.");
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
generateExampleContent() {
|
|
566
|
+
return `import express from "express";
|
|
567
|
+
import { createBilling } from "@yosefhayim/fresh-squeezy";
|
|
568
|
+
import { billingConfig } from "./billing-config";
|
|
569
|
+
|
|
570
|
+
const app = express();
|
|
571
|
+
const port = process.env.PORT || 3000;
|
|
572
|
+
|
|
573
|
+
async function setupBilling() {
|
|
574
|
+
try {
|
|
575
|
+
const billing = await createBilling(billingConfig);
|
|
576
|
+
|
|
577
|
+
console.log("[+] Billing setup complete!");
|
|
578
|
+
console.log("Available stores:", billing.stores.map(s => s.name));
|
|
579
|
+
console.log("Available plans:", billing.plans.length);
|
|
580
|
+
|
|
581
|
+
// Example: Create a checkout
|
|
582
|
+
const checkoutUrl = await billing.createCheckout({
|
|
583
|
+
variantId: "${this.state.selectedProductIds[0] || "your-variant-id"}",
|
|
584
|
+
email: "user@example.com",
|
|
585
|
+
userId: "user-123"
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
console.log("Checkout URL:", checkoutUrl);
|
|
589
|
+
|
|
590
|
+
// Webhook endpoint
|
|
591
|
+
app.post("/webhook", express.raw({ type: "application/json" }), async (req, res) => {
|
|
592
|
+
const signature = req.headers["x-signature"] as string;
|
|
593
|
+
const rawBody = req.body.toString();
|
|
594
|
+
|
|
595
|
+
if (!billing.verifyWebhook(rawBody, signature)) {
|
|
596
|
+
return res.status(401).send("Invalid signature");
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
await billing.handleWebhook(JSON.parse(rawBody));
|
|
600
|
+
res.json({ received: true });
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
app.listen(port, () => {
|
|
604
|
+
console.log(\`🚀 Server running on port \${port}\`);
|
|
605
|
+
console.log("💡 Test your webhook with: curl -X POST http://localhost:\${port}/webhook");
|
|
606
|
+
});
|
|
607
|
+
|
|
608
|
+
} catch (error) {
|
|
609
|
+
console.error("[x] Setup failed:", error);
|
|
610
|
+
process.exit(1);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
setupBilling();
|
|
615
|
+
`;
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
// Run the wizard
|
|
619
|
+
const isMain = globalThis.process?.argv[1]?.endsWith('wizard.ts');
|
|
620
|
+
if (isMain) {
|
|
621
|
+
runWizard().catch(console.error);
|
|
622
|
+
}
|
|
623
|
+
export async function runWizard() {
|
|
624
|
+
const wizard = new BillingWizard();
|
|
625
|
+
await wizard.run();
|
|
626
|
+
}
|
|
627
|
+
export { BillingWizard };
|
|
628
|
+
//# sourceMappingURL=wizard.js.map
|