@victusvinceere/saas-payments 0.1.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.
@@ -0,0 +1,4 @@
1
+ export { CreateCheckoutOptions, LemonSqueezyConfig, cancelSubscriptionById, configureLemonSqueezy, createCheckoutUrl, getCustomerSubscriptions, getSubscriptionById, getSubscriptionStatusLabel, initLemonSqueezy, isSubscriptionActive, resumeSubscription } from './lemonsqueezy/index.mjs';
2
+ export { WebhookEvent, WebhookHandlers, createWebhookHandler } from './webhooks/index.mjs';
3
+ import '@lemonsqueezy/lemonsqueezy.js';
4
+ import 'next/server';
@@ -0,0 +1,4 @@
1
+ export { CreateCheckoutOptions, LemonSqueezyConfig, cancelSubscriptionById, configureLemonSqueezy, createCheckoutUrl, getCustomerSubscriptions, getSubscriptionById, getSubscriptionStatusLabel, initLemonSqueezy, isSubscriptionActive, resumeSubscription } from './lemonsqueezy/index.js';
2
+ export { WebhookEvent, WebhookHandlers, createWebhookHandler } from './webhooks/index.js';
3
+ import '@lemonsqueezy/lemonsqueezy.js';
4
+ import 'next/server';
package/dist/index.js ADDED
@@ -0,0 +1,246 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ cancelSubscriptionById: () => cancelSubscriptionById,
34
+ configureLemonSqueezy: () => configureLemonSqueezy,
35
+ createCheckoutUrl: () => createCheckoutUrl,
36
+ createWebhookHandler: () => createWebhookHandler,
37
+ getCustomerSubscriptions: () => getCustomerSubscriptions,
38
+ getSubscriptionById: () => getSubscriptionById,
39
+ getSubscriptionStatusLabel: () => getSubscriptionStatusLabel,
40
+ initLemonSqueezy: () => initLemonSqueezy,
41
+ isSubscriptionActive: () => isSubscriptionActive,
42
+ resumeSubscription: () => resumeSubscription
43
+ });
44
+ module.exports = __toCommonJS(src_exports);
45
+
46
+ // src/lemonsqueezy/index.ts
47
+ var import_lemonsqueezy = require("@lemonsqueezy/lemonsqueezy.js");
48
+ var config = {};
49
+ function configureLemonSqueezy(options) {
50
+ config = { ...config, ...options };
51
+ }
52
+ function getApiKey() {
53
+ const apiKey = config.apiKey || process.env.LEMONSQUEEZY_API_KEY;
54
+ if (!apiKey) {
55
+ throw new Error("LEMONSQUEEZY_API_KEY is not set");
56
+ }
57
+ return apiKey;
58
+ }
59
+ function getStoreId() {
60
+ const storeId = config.storeId || process.env.LEMONSQUEEZY_STORE_ID;
61
+ if (!storeId) {
62
+ throw new Error("LEMONSQUEEZY_STORE_ID is not set");
63
+ }
64
+ return storeId;
65
+ }
66
+ function getAppUrl() {
67
+ return config.appUrl || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";
68
+ }
69
+ function initLemonSqueezy() {
70
+ (0, import_lemonsqueezy.lemonSqueezySetup)({ apiKey: getApiKey() });
71
+ }
72
+ async function getSubscriptionById(subscriptionId) {
73
+ initLemonSqueezy();
74
+ const { data, error } = await (0, import_lemonsqueezy.getSubscription)(subscriptionId);
75
+ if (error) throw new Error(error.message);
76
+ return data;
77
+ }
78
+ async function cancelSubscriptionById(subscriptionId) {
79
+ initLemonSqueezy();
80
+ const { data, error } = await (0, import_lemonsqueezy.cancelSubscription)(subscriptionId);
81
+ if (error) throw new Error(error.message);
82
+ return data;
83
+ }
84
+ async function resumeSubscription(subscriptionId) {
85
+ initLemonSqueezy();
86
+ const { data, error } = await (0, import_lemonsqueezy.updateSubscription)(subscriptionId, {
87
+ cancelled: false
88
+ });
89
+ if (error) throw new Error(error.message);
90
+ return data;
91
+ }
92
+ async function createCheckoutUrl(options) {
93
+ initLemonSqueezy();
94
+ const { data, error } = await (0, import_lemonsqueezy.createCheckout)(getStoreId(), options.variantId, {
95
+ checkoutData: {
96
+ email: options.email,
97
+ name: options.name || void 0,
98
+ custom: {
99
+ user_id: options.userId
100
+ }
101
+ },
102
+ productOptions: {
103
+ redirectUrl: options.redirectUrl || `${getAppUrl()}/dashboard/settings/billing`
104
+ }
105
+ });
106
+ if (error) throw new Error(error.message);
107
+ return data?.data.attributes.url;
108
+ }
109
+ async function getCustomerSubscriptions(customerId) {
110
+ initLemonSqueezy();
111
+ const { data, error } = await (0, import_lemonsqueezy.listSubscriptions)({
112
+ filter: { userEmail: customerId }
113
+ });
114
+ if (error) throw new Error(error.message);
115
+ return data;
116
+ }
117
+ function isSubscriptionActive(status) {
118
+ return ["active", "on_trial", "paused"].includes(status);
119
+ }
120
+ function getSubscriptionStatusLabel(status) {
121
+ const labels = {
122
+ active: "Active",
123
+ on_trial: "Trial",
124
+ paused: "Paused",
125
+ past_due: "Past Due",
126
+ unpaid: "Unpaid",
127
+ cancelled: "Cancelled",
128
+ expired: "Expired"
129
+ };
130
+ return labels[status] || status;
131
+ }
132
+
133
+ // src/webhooks/index.ts
134
+ var import_server = require("next/server");
135
+ var import_crypto = __toESM(require("crypto"));
136
+ function verifyWebhookSignature(rawBody, signature, secret) {
137
+ const hmac = import_crypto.default.createHmac("sha256", secret);
138
+ const digest = hmac.update(rawBody).digest("hex");
139
+ return import_crypto.default.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
140
+ }
141
+ function createWebhookHandler(handlers, webhookSecret) {
142
+ return async function handleWebhook(request) {
143
+ const secret = webhookSecret || process.env.LEMONSQUEEZY_WEBHOOK_SECRET;
144
+ if (!secret) {
145
+ console.error("LEMONSQUEEZY_WEBHOOK_SECRET is not set");
146
+ return import_server.NextResponse.json(
147
+ { error: "Webhook secret not configured" },
148
+ { status: 500 }
149
+ );
150
+ }
151
+ const rawBody = await request.text();
152
+ const signature = request.headers.get("x-signature");
153
+ if (!signature) {
154
+ return import_server.NextResponse.json(
155
+ { error: "Missing signature" },
156
+ { status: 401 }
157
+ );
158
+ }
159
+ const isValid = verifyWebhookSignature(rawBody, signature, secret);
160
+ if (!isValid) {
161
+ return import_server.NextResponse.json(
162
+ { error: "Invalid signature" },
163
+ { status: 401 }
164
+ );
165
+ }
166
+ try {
167
+ const event = JSON.parse(rawBody);
168
+ const eventName = event.meta.event_name;
169
+ switch (eventName) {
170
+ case "subscription_created":
171
+ if (handlers.onSubscriptionCreated) {
172
+ await handlers.onSubscriptionCreated(event);
173
+ }
174
+ break;
175
+ case "subscription_updated":
176
+ if (handlers.onSubscriptionUpdated) {
177
+ await handlers.onSubscriptionUpdated(event);
178
+ }
179
+ break;
180
+ case "subscription_cancelled":
181
+ if (handlers.onSubscriptionCancelled) {
182
+ await handlers.onSubscriptionCancelled(event);
183
+ }
184
+ break;
185
+ case "subscription_resumed":
186
+ if (handlers.onSubscriptionResumed) {
187
+ await handlers.onSubscriptionResumed(event);
188
+ }
189
+ break;
190
+ case "subscription_expired":
191
+ if (handlers.onSubscriptionExpired) {
192
+ await handlers.onSubscriptionExpired(event);
193
+ }
194
+ break;
195
+ case "subscription_paused":
196
+ if (handlers.onSubscriptionPaused) {
197
+ await handlers.onSubscriptionPaused(event);
198
+ }
199
+ break;
200
+ case "subscription_unpaused":
201
+ if (handlers.onSubscriptionUnpaused) {
202
+ await handlers.onSubscriptionUnpaused(event);
203
+ }
204
+ break;
205
+ case "subscription_payment_success":
206
+ if (handlers.onSubscriptionPaymentSuccess) {
207
+ await handlers.onSubscriptionPaymentSuccess(event);
208
+ }
209
+ break;
210
+ case "subscription_payment_failed":
211
+ if (handlers.onSubscriptionPaymentFailed) {
212
+ await handlers.onSubscriptionPaymentFailed(event);
213
+ }
214
+ break;
215
+ case "order_created":
216
+ if (handlers.onOrderCreated) {
217
+ await handlers.onOrderCreated(event);
218
+ }
219
+ break;
220
+ default:
221
+ console.log(`Unhandled webhook event: ${eventName}`);
222
+ }
223
+ return import_server.NextResponse.json({ received: true });
224
+ } catch (error) {
225
+ console.error("Webhook processing error:", error);
226
+ return import_server.NextResponse.json(
227
+ { error: "Webhook processing failed" },
228
+ { status: 500 }
229
+ );
230
+ }
231
+ };
232
+ }
233
+ // Annotate the CommonJS export names for ESM import in node:
234
+ 0 && (module.exports = {
235
+ cancelSubscriptionById,
236
+ configureLemonSqueezy,
237
+ createCheckoutUrl,
238
+ createWebhookHandler,
239
+ getCustomerSubscriptions,
240
+ getSubscriptionById,
241
+ getSubscriptionStatusLabel,
242
+ initLemonSqueezy,
243
+ isSubscriptionActive,
244
+ resumeSubscription
245
+ });
246
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/lemonsqueezy/index.ts","../src/webhooks/index.ts"],"sourcesContent":["// Lemon Squeezy\nexport {\n initLemonSqueezy,\n configureLemonSqueezy,\n getSubscriptionById,\n cancelSubscriptionById,\n resumeSubscription,\n createCheckoutUrl,\n getCustomerSubscriptions,\n isSubscriptionActive,\n getSubscriptionStatusLabel,\n} from \"./lemonsqueezy\";\nexport type { LemonSqueezyConfig, CreateCheckoutOptions } from \"./lemonsqueezy\";\n\n// Webhooks\nexport { createWebhookHandler } from \"./webhooks\";\nexport type { WebhookEvent, WebhookHandlers } from \"./webhooks\";\n","import {\n lemonSqueezySetup,\n getSubscription,\n cancelSubscription,\n updateSubscription,\n createCheckout,\n listSubscriptions,\n} from \"@lemonsqueezy/lemonsqueezy.js\";\n\nexport interface LemonSqueezyConfig {\n apiKey?: string;\n storeId?: string;\n appUrl?: string;\n}\n\nlet config: LemonSqueezyConfig = {};\n\nexport function configureLemonSqueezy(options: LemonSqueezyConfig) {\n config = { ...config, ...options };\n}\n\nfunction getApiKey(): string {\n const apiKey = config.apiKey || process.env.LEMONSQUEEZY_API_KEY;\n if (!apiKey) {\n throw new Error(\"LEMONSQUEEZY_API_KEY is not set\");\n }\n return apiKey;\n}\n\nfunction getStoreId(): string {\n const storeId = config.storeId || process.env.LEMONSQUEEZY_STORE_ID;\n if (!storeId) {\n throw new Error(\"LEMONSQUEEZY_STORE_ID is not set\");\n }\n return storeId;\n}\n\nfunction getAppUrl(): string {\n return config.appUrl || process.env.NEXT_PUBLIC_APP_URL || \"http://localhost:3000\";\n}\n\nexport function initLemonSqueezy() {\n lemonSqueezySetup({ apiKey: getApiKey() });\n}\n\nexport async function getSubscriptionById(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await getSubscription(subscriptionId);\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport async function cancelSubscriptionById(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await cancelSubscription(subscriptionId);\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport async function resumeSubscription(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await updateSubscription(subscriptionId, {\n cancelled: false,\n });\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport interface CreateCheckoutOptions {\n variantId: string;\n email: string;\n name?: string;\n userId: string;\n redirectUrl?: string;\n}\n\nexport async function createCheckoutUrl(options: CreateCheckoutOptions) {\n initLemonSqueezy();\n\n const { data, error } = await createCheckout(getStoreId(), options.variantId, {\n checkoutData: {\n email: options.email,\n name: options.name || undefined,\n custom: {\n user_id: options.userId,\n },\n },\n productOptions: {\n redirectUrl: options.redirectUrl || `${getAppUrl()}/dashboard/settings/billing`,\n },\n });\n\n if (error) throw new Error(error.message);\n return data?.data.attributes.url;\n}\n\nexport async function getCustomerSubscriptions(customerId: string) {\n initLemonSqueezy();\n const { data, error } = await listSubscriptions({\n filter: { userEmail: customerId },\n });\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport function isSubscriptionActive(status: string) {\n return [\"active\", \"on_trial\", \"paused\"].includes(status);\n}\n\nexport function getSubscriptionStatusLabel(status: string) {\n const labels: Record<string, string> = {\n active: \"Active\",\n on_trial: \"Trial\",\n paused: \"Paused\",\n past_due: \"Past Due\",\n unpaid: \"Unpaid\",\n cancelled: \"Cancelled\",\n expired: \"Expired\",\n };\n return labels[status] || status;\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport crypto from \"crypto\";\n\nexport interface WebhookEvent {\n meta: {\n event_name: string;\n custom_data?: {\n user_id?: string;\n };\n };\n data: {\n id: string;\n type: string;\n attributes: {\n status: string;\n customer_id: number;\n product_id: number;\n variant_id: number;\n renews_at: string | null;\n ends_at: string | null;\n trial_ends_at: string | null;\n };\n };\n}\n\nexport interface WebhookHandlers {\n onSubscriptionCreated?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionUpdated?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionCancelled?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionResumed?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionExpired?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaused?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionUnpaused?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaymentSuccess?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaymentFailed?: (event: WebhookEvent) => Promise<void>;\n onOrderCreated?: (event: WebhookEvent) => Promise<void>;\n}\n\nfunction verifyWebhookSignature(\n rawBody: string,\n signature: string,\n secret: string\n): boolean {\n const hmac = crypto.createHmac(\"sha256\", secret);\n const digest = hmac.update(rawBody).digest(\"hex\");\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));\n}\n\nexport function createWebhookHandler(handlers: WebhookHandlers, webhookSecret?: string) {\n return async function handleWebhook(request: NextRequest): Promise<NextResponse> {\n const secret = webhookSecret || process.env.LEMONSQUEEZY_WEBHOOK_SECRET;\n\n if (!secret) {\n console.error(\"LEMONSQUEEZY_WEBHOOK_SECRET is not set\");\n return NextResponse.json(\n { error: \"Webhook secret not configured\" },\n { status: 500 }\n );\n }\n\n const rawBody = await request.text();\n const signature = request.headers.get(\"x-signature\");\n\n if (!signature) {\n return NextResponse.json(\n { error: \"Missing signature\" },\n { status: 401 }\n );\n }\n\n const isValid = verifyWebhookSignature(rawBody, signature, secret);\n\n if (!isValid) {\n return NextResponse.json(\n { error: \"Invalid signature\" },\n { status: 401 }\n );\n }\n\n try {\n const event: WebhookEvent = JSON.parse(rawBody);\n const eventName = event.meta.event_name;\n\n switch (eventName) {\n case \"subscription_created\":\n if (handlers.onSubscriptionCreated) {\n await handlers.onSubscriptionCreated(event);\n }\n break;\n case \"subscription_updated\":\n if (handlers.onSubscriptionUpdated) {\n await handlers.onSubscriptionUpdated(event);\n }\n break;\n case \"subscription_cancelled\":\n if (handlers.onSubscriptionCancelled) {\n await handlers.onSubscriptionCancelled(event);\n }\n break;\n case \"subscription_resumed\":\n if (handlers.onSubscriptionResumed) {\n await handlers.onSubscriptionResumed(event);\n }\n break;\n case \"subscription_expired\":\n if (handlers.onSubscriptionExpired) {\n await handlers.onSubscriptionExpired(event);\n }\n break;\n case \"subscription_paused\":\n if (handlers.onSubscriptionPaused) {\n await handlers.onSubscriptionPaused(event);\n }\n break;\n case \"subscription_unpaused\":\n if (handlers.onSubscriptionUnpaused) {\n await handlers.onSubscriptionUnpaused(event);\n }\n break;\n case \"subscription_payment_success\":\n if (handlers.onSubscriptionPaymentSuccess) {\n await handlers.onSubscriptionPaymentSuccess(event);\n }\n break;\n case \"subscription_payment_failed\":\n if (handlers.onSubscriptionPaymentFailed) {\n await handlers.onSubscriptionPaymentFailed(event);\n }\n break;\n case \"order_created\":\n if (handlers.onOrderCreated) {\n await handlers.onOrderCreated(event);\n }\n break;\n default:\n console.log(`Unhandled webhook event: ${eventName}`);\n }\n\n return NextResponse.json({ received: true });\n } catch (error) {\n console.error(\"Webhook processing error:\", error);\n return NextResponse.json(\n { error: \"Webhook processing failed\" },\n { status: 500 }\n );\n }\n };\n}\n"],"mappings":"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","names":["crypto"]}
package/dist/index.mjs ADDED
@@ -0,0 +1,207 @@
1
+ // src/lemonsqueezy/index.ts
2
+ import {
3
+ lemonSqueezySetup,
4
+ getSubscription,
5
+ cancelSubscription,
6
+ updateSubscription,
7
+ createCheckout,
8
+ listSubscriptions
9
+ } from "@lemonsqueezy/lemonsqueezy.js";
10
+ var config = {};
11
+ function configureLemonSqueezy(options) {
12
+ config = { ...config, ...options };
13
+ }
14
+ function getApiKey() {
15
+ const apiKey = config.apiKey || process.env.LEMONSQUEEZY_API_KEY;
16
+ if (!apiKey) {
17
+ throw new Error("LEMONSQUEEZY_API_KEY is not set");
18
+ }
19
+ return apiKey;
20
+ }
21
+ function getStoreId() {
22
+ const storeId = config.storeId || process.env.LEMONSQUEEZY_STORE_ID;
23
+ if (!storeId) {
24
+ throw new Error("LEMONSQUEEZY_STORE_ID is not set");
25
+ }
26
+ return storeId;
27
+ }
28
+ function getAppUrl() {
29
+ return config.appUrl || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";
30
+ }
31
+ function initLemonSqueezy() {
32
+ lemonSqueezySetup({ apiKey: getApiKey() });
33
+ }
34
+ async function getSubscriptionById(subscriptionId) {
35
+ initLemonSqueezy();
36
+ const { data, error } = await getSubscription(subscriptionId);
37
+ if (error) throw new Error(error.message);
38
+ return data;
39
+ }
40
+ async function cancelSubscriptionById(subscriptionId) {
41
+ initLemonSqueezy();
42
+ const { data, error } = await cancelSubscription(subscriptionId);
43
+ if (error) throw new Error(error.message);
44
+ return data;
45
+ }
46
+ async function resumeSubscription(subscriptionId) {
47
+ initLemonSqueezy();
48
+ const { data, error } = await updateSubscription(subscriptionId, {
49
+ cancelled: false
50
+ });
51
+ if (error) throw new Error(error.message);
52
+ return data;
53
+ }
54
+ async function createCheckoutUrl(options) {
55
+ initLemonSqueezy();
56
+ const { data, error } = await createCheckout(getStoreId(), options.variantId, {
57
+ checkoutData: {
58
+ email: options.email,
59
+ name: options.name || void 0,
60
+ custom: {
61
+ user_id: options.userId
62
+ }
63
+ },
64
+ productOptions: {
65
+ redirectUrl: options.redirectUrl || `${getAppUrl()}/dashboard/settings/billing`
66
+ }
67
+ });
68
+ if (error) throw new Error(error.message);
69
+ return data?.data.attributes.url;
70
+ }
71
+ async function getCustomerSubscriptions(customerId) {
72
+ initLemonSqueezy();
73
+ const { data, error } = await listSubscriptions({
74
+ filter: { userEmail: customerId }
75
+ });
76
+ if (error) throw new Error(error.message);
77
+ return data;
78
+ }
79
+ function isSubscriptionActive(status) {
80
+ return ["active", "on_trial", "paused"].includes(status);
81
+ }
82
+ function getSubscriptionStatusLabel(status) {
83
+ const labels = {
84
+ active: "Active",
85
+ on_trial: "Trial",
86
+ paused: "Paused",
87
+ past_due: "Past Due",
88
+ unpaid: "Unpaid",
89
+ cancelled: "Cancelled",
90
+ expired: "Expired"
91
+ };
92
+ return labels[status] || status;
93
+ }
94
+
95
+ // src/webhooks/index.ts
96
+ import { NextResponse } from "next/server";
97
+ import crypto from "crypto";
98
+ function verifyWebhookSignature(rawBody, signature, secret) {
99
+ const hmac = crypto.createHmac("sha256", secret);
100
+ const digest = hmac.update(rawBody).digest("hex");
101
+ return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
102
+ }
103
+ function createWebhookHandler(handlers, webhookSecret) {
104
+ return async function handleWebhook(request) {
105
+ const secret = webhookSecret || process.env.LEMONSQUEEZY_WEBHOOK_SECRET;
106
+ if (!secret) {
107
+ console.error("LEMONSQUEEZY_WEBHOOK_SECRET is not set");
108
+ return NextResponse.json(
109
+ { error: "Webhook secret not configured" },
110
+ { status: 500 }
111
+ );
112
+ }
113
+ const rawBody = await request.text();
114
+ const signature = request.headers.get("x-signature");
115
+ if (!signature) {
116
+ return NextResponse.json(
117
+ { error: "Missing signature" },
118
+ { status: 401 }
119
+ );
120
+ }
121
+ const isValid = verifyWebhookSignature(rawBody, signature, secret);
122
+ if (!isValid) {
123
+ return NextResponse.json(
124
+ { error: "Invalid signature" },
125
+ { status: 401 }
126
+ );
127
+ }
128
+ try {
129
+ const event = JSON.parse(rawBody);
130
+ const eventName = event.meta.event_name;
131
+ switch (eventName) {
132
+ case "subscription_created":
133
+ if (handlers.onSubscriptionCreated) {
134
+ await handlers.onSubscriptionCreated(event);
135
+ }
136
+ break;
137
+ case "subscription_updated":
138
+ if (handlers.onSubscriptionUpdated) {
139
+ await handlers.onSubscriptionUpdated(event);
140
+ }
141
+ break;
142
+ case "subscription_cancelled":
143
+ if (handlers.onSubscriptionCancelled) {
144
+ await handlers.onSubscriptionCancelled(event);
145
+ }
146
+ break;
147
+ case "subscription_resumed":
148
+ if (handlers.onSubscriptionResumed) {
149
+ await handlers.onSubscriptionResumed(event);
150
+ }
151
+ break;
152
+ case "subscription_expired":
153
+ if (handlers.onSubscriptionExpired) {
154
+ await handlers.onSubscriptionExpired(event);
155
+ }
156
+ break;
157
+ case "subscription_paused":
158
+ if (handlers.onSubscriptionPaused) {
159
+ await handlers.onSubscriptionPaused(event);
160
+ }
161
+ break;
162
+ case "subscription_unpaused":
163
+ if (handlers.onSubscriptionUnpaused) {
164
+ await handlers.onSubscriptionUnpaused(event);
165
+ }
166
+ break;
167
+ case "subscription_payment_success":
168
+ if (handlers.onSubscriptionPaymentSuccess) {
169
+ await handlers.onSubscriptionPaymentSuccess(event);
170
+ }
171
+ break;
172
+ case "subscription_payment_failed":
173
+ if (handlers.onSubscriptionPaymentFailed) {
174
+ await handlers.onSubscriptionPaymentFailed(event);
175
+ }
176
+ break;
177
+ case "order_created":
178
+ if (handlers.onOrderCreated) {
179
+ await handlers.onOrderCreated(event);
180
+ }
181
+ break;
182
+ default:
183
+ console.log(`Unhandled webhook event: ${eventName}`);
184
+ }
185
+ return NextResponse.json({ received: true });
186
+ } catch (error) {
187
+ console.error("Webhook processing error:", error);
188
+ return NextResponse.json(
189
+ { error: "Webhook processing failed" },
190
+ { status: 500 }
191
+ );
192
+ }
193
+ };
194
+ }
195
+ export {
196
+ cancelSubscriptionById,
197
+ configureLemonSqueezy,
198
+ createCheckoutUrl,
199
+ createWebhookHandler,
200
+ getCustomerSubscriptions,
201
+ getSubscriptionById,
202
+ getSubscriptionStatusLabel,
203
+ initLemonSqueezy,
204
+ isSubscriptionActive,
205
+ resumeSubscription
206
+ };
207
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lemonsqueezy/index.ts","../src/webhooks/index.ts"],"sourcesContent":["import {\n lemonSqueezySetup,\n getSubscription,\n cancelSubscription,\n updateSubscription,\n createCheckout,\n listSubscriptions,\n} from \"@lemonsqueezy/lemonsqueezy.js\";\n\nexport interface LemonSqueezyConfig {\n apiKey?: string;\n storeId?: string;\n appUrl?: string;\n}\n\nlet config: LemonSqueezyConfig = {};\n\nexport function configureLemonSqueezy(options: LemonSqueezyConfig) {\n config = { ...config, ...options };\n}\n\nfunction getApiKey(): string {\n const apiKey = config.apiKey || process.env.LEMONSQUEEZY_API_KEY;\n if (!apiKey) {\n throw new Error(\"LEMONSQUEEZY_API_KEY is not set\");\n }\n return apiKey;\n}\n\nfunction getStoreId(): string {\n const storeId = config.storeId || process.env.LEMONSQUEEZY_STORE_ID;\n if (!storeId) {\n throw new Error(\"LEMONSQUEEZY_STORE_ID is not set\");\n }\n return storeId;\n}\n\nfunction getAppUrl(): string {\n return config.appUrl || process.env.NEXT_PUBLIC_APP_URL || \"http://localhost:3000\";\n}\n\nexport function initLemonSqueezy() {\n lemonSqueezySetup({ apiKey: getApiKey() });\n}\n\nexport async function getSubscriptionById(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await getSubscription(subscriptionId);\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport async function cancelSubscriptionById(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await cancelSubscription(subscriptionId);\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport async function resumeSubscription(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await updateSubscription(subscriptionId, {\n cancelled: false,\n });\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport interface CreateCheckoutOptions {\n variantId: string;\n email: string;\n name?: string;\n userId: string;\n redirectUrl?: string;\n}\n\nexport async function createCheckoutUrl(options: CreateCheckoutOptions) {\n initLemonSqueezy();\n\n const { data, error } = await createCheckout(getStoreId(), options.variantId, {\n checkoutData: {\n email: options.email,\n name: options.name || undefined,\n custom: {\n user_id: options.userId,\n },\n },\n productOptions: {\n redirectUrl: options.redirectUrl || `${getAppUrl()}/dashboard/settings/billing`,\n },\n });\n\n if (error) throw new Error(error.message);\n return data?.data.attributes.url;\n}\n\nexport async function getCustomerSubscriptions(customerId: string) {\n initLemonSqueezy();\n const { data, error } = await listSubscriptions({\n filter: { userEmail: customerId },\n });\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport function isSubscriptionActive(status: string) {\n return [\"active\", \"on_trial\", \"paused\"].includes(status);\n}\n\nexport function getSubscriptionStatusLabel(status: string) {\n const labels: Record<string, string> = {\n active: \"Active\",\n on_trial: \"Trial\",\n paused: \"Paused\",\n past_due: \"Past Due\",\n unpaid: \"Unpaid\",\n cancelled: \"Cancelled\",\n expired: \"Expired\",\n };\n return labels[status] || status;\n}\n","import { NextRequest, NextResponse } from \"next/server\";\nimport crypto from \"crypto\";\n\nexport interface WebhookEvent {\n meta: {\n event_name: string;\n custom_data?: {\n user_id?: string;\n };\n };\n data: {\n id: string;\n type: string;\n attributes: {\n status: string;\n customer_id: number;\n product_id: number;\n variant_id: number;\n renews_at: string | null;\n ends_at: string | null;\n trial_ends_at: string | null;\n };\n };\n}\n\nexport interface WebhookHandlers {\n onSubscriptionCreated?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionUpdated?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionCancelled?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionResumed?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionExpired?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaused?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionUnpaused?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaymentSuccess?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaymentFailed?: (event: WebhookEvent) => Promise<void>;\n onOrderCreated?: (event: WebhookEvent) => Promise<void>;\n}\n\nfunction verifyWebhookSignature(\n rawBody: string,\n signature: string,\n secret: string\n): boolean {\n const hmac = crypto.createHmac(\"sha256\", secret);\n const digest = hmac.update(rawBody).digest(\"hex\");\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));\n}\n\nexport function createWebhookHandler(handlers: WebhookHandlers, webhookSecret?: string) {\n return async function handleWebhook(request: NextRequest): Promise<NextResponse> {\n const secret = webhookSecret || process.env.LEMONSQUEEZY_WEBHOOK_SECRET;\n\n if (!secret) {\n console.error(\"LEMONSQUEEZY_WEBHOOK_SECRET is not set\");\n return NextResponse.json(\n { error: \"Webhook secret not configured\" },\n { status: 500 }\n );\n }\n\n const rawBody = await request.text();\n const signature = request.headers.get(\"x-signature\");\n\n if (!signature) {\n return NextResponse.json(\n { error: \"Missing signature\" },\n { status: 401 }\n );\n }\n\n const isValid = verifyWebhookSignature(rawBody, signature, secret);\n\n if (!isValid) {\n return NextResponse.json(\n { error: \"Invalid signature\" },\n { status: 401 }\n );\n }\n\n try {\n const event: WebhookEvent = JSON.parse(rawBody);\n const eventName = event.meta.event_name;\n\n switch (eventName) {\n case \"subscription_created\":\n if (handlers.onSubscriptionCreated) {\n await handlers.onSubscriptionCreated(event);\n }\n break;\n case \"subscription_updated\":\n if (handlers.onSubscriptionUpdated) {\n await handlers.onSubscriptionUpdated(event);\n }\n break;\n case \"subscription_cancelled\":\n if (handlers.onSubscriptionCancelled) {\n await handlers.onSubscriptionCancelled(event);\n }\n break;\n case \"subscription_resumed\":\n if (handlers.onSubscriptionResumed) {\n await handlers.onSubscriptionResumed(event);\n }\n break;\n case \"subscription_expired\":\n if (handlers.onSubscriptionExpired) {\n await handlers.onSubscriptionExpired(event);\n }\n break;\n case \"subscription_paused\":\n if (handlers.onSubscriptionPaused) {\n await handlers.onSubscriptionPaused(event);\n }\n break;\n case \"subscription_unpaused\":\n if (handlers.onSubscriptionUnpaused) {\n await handlers.onSubscriptionUnpaused(event);\n }\n break;\n case \"subscription_payment_success\":\n if (handlers.onSubscriptionPaymentSuccess) {\n await handlers.onSubscriptionPaymentSuccess(event);\n }\n break;\n case \"subscription_payment_failed\":\n if (handlers.onSubscriptionPaymentFailed) {\n await handlers.onSubscriptionPaymentFailed(event);\n }\n break;\n case \"order_created\":\n if (handlers.onOrderCreated) {\n await handlers.onOrderCreated(event);\n }\n break;\n default:\n console.log(`Unhandled webhook event: ${eventName}`);\n }\n\n return NextResponse.json({ received: true });\n } catch (error) {\n console.error(\"Webhook processing error:\", error);\n return NextResponse.json(\n { error: \"Webhook processing failed\" },\n { status: 500 }\n );\n }\n };\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,25 @@
1
+ import * as _lemonsqueezy_lemonsqueezy_js from '@lemonsqueezy/lemonsqueezy.js';
2
+
3
+ interface LemonSqueezyConfig {
4
+ apiKey?: string;
5
+ storeId?: string;
6
+ appUrl?: string;
7
+ }
8
+ declare function configureLemonSqueezy(options: LemonSqueezyConfig): void;
9
+ declare function initLemonSqueezy(): void;
10
+ declare function getSubscriptionById(subscriptionId: string): Promise<_lemonsqueezy_lemonsqueezy_js.Subscription>;
11
+ declare function cancelSubscriptionById(subscriptionId: string): Promise<_lemonsqueezy_lemonsqueezy_js.Subscription>;
12
+ declare function resumeSubscription(subscriptionId: string): Promise<_lemonsqueezy_lemonsqueezy_js.Subscription>;
13
+ interface CreateCheckoutOptions {
14
+ variantId: string;
15
+ email: string;
16
+ name?: string;
17
+ userId: string;
18
+ redirectUrl?: string;
19
+ }
20
+ declare function createCheckoutUrl(options: CreateCheckoutOptions): Promise<string>;
21
+ declare function getCustomerSubscriptions(customerId: string): Promise<_lemonsqueezy_lemonsqueezy_js.ListSubscriptions>;
22
+ declare function isSubscriptionActive(status: string): boolean;
23
+ declare function getSubscriptionStatusLabel(status: string): string;
24
+
25
+ export { type CreateCheckoutOptions, type LemonSqueezyConfig, cancelSubscriptionById, configureLemonSqueezy, createCheckoutUrl, getCustomerSubscriptions, getSubscriptionById, getSubscriptionStatusLabel, initLemonSqueezy, isSubscriptionActive, resumeSubscription };
@@ -0,0 +1,25 @@
1
+ import * as _lemonsqueezy_lemonsqueezy_js from '@lemonsqueezy/lemonsqueezy.js';
2
+
3
+ interface LemonSqueezyConfig {
4
+ apiKey?: string;
5
+ storeId?: string;
6
+ appUrl?: string;
7
+ }
8
+ declare function configureLemonSqueezy(options: LemonSqueezyConfig): void;
9
+ declare function initLemonSqueezy(): void;
10
+ declare function getSubscriptionById(subscriptionId: string): Promise<_lemonsqueezy_lemonsqueezy_js.Subscription>;
11
+ declare function cancelSubscriptionById(subscriptionId: string): Promise<_lemonsqueezy_lemonsqueezy_js.Subscription>;
12
+ declare function resumeSubscription(subscriptionId: string): Promise<_lemonsqueezy_lemonsqueezy_js.Subscription>;
13
+ interface CreateCheckoutOptions {
14
+ variantId: string;
15
+ email: string;
16
+ name?: string;
17
+ userId: string;
18
+ redirectUrl?: string;
19
+ }
20
+ declare function createCheckoutUrl(options: CreateCheckoutOptions): Promise<string>;
21
+ declare function getCustomerSubscriptions(customerId: string): Promise<_lemonsqueezy_lemonsqueezy_js.ListSubscriptions>;
22
+ declare function isSubscriptionActive(status: string): boolean;
23
+ declare function getSubscriptionStatusLabel(status: string): string;
24
+
25
+ export { type CreateCheckoutOptions, type LemonSqueezyConfig, cancelSubscriptionById, configureLemonSqueezy, createCheckoutUrl, getCustomerSubscriptions, getSubscriptionById, getSubscriptionStatusLabel, initLemonSqueezy, isSubscriptionActive, resumeSubscription };
@@ -0,0 +1,131 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/lemonsqueezy/index.ts
21
+ var lemonsqueezy_exports = {};
22
+ __export(lemonsqueezy_exports, {
23
+ cancelSubscriptionById: () => cancelSubscriptionById,
24
+ configureLemonSqueezy: () => configureLemonSqueezy,
25
+ createCheckoutUrl: () => createCheckoutUrl,
26
+ getCustomerSubscriptions: () => getCustomerSubscriptions,
27
+ getSubscriptionById: () => getSubscriptionById,
28
+ getSubscriptionStatusLabel: () => getSubscriptionStatusLabel,
29
+ initLemonSqueezy: () => initLemonSqueezy,
30
+ isSubscriptionActive: () => isSubscriptionActive,
31
+ resumeSubscription: () => resumeSubscription
32
+ });
33
+ module.exports = __toCommonJS(lemonsqueezy_exports);
34
+ var import_lemonsqueezy = require("@lemonsqueezy/lemonsqueezy.js");
35
+ var config = {};
36
+ function configureLemonSqueezy(options) {
37
+ config = { ...config, ...options };
38
+ }
39
+ function getApiKey() {
40
+ const apiKey = config.apiKey || process.env.LEMONSQUEEZY_API_KEY;
41
+ if (!apiKey) {
42
+ throw new Error("LEMONSQUEEZY_API_KEY is not set");
43
+ }
44
+ return apiKey;
45
+ }
46
+ function getStoreId() {
47
+ const storeId = config.storeId || process.env.LEMONSQUEEZY_STORE_ID;
48
+ if (!storeId) {
49
+ throw new Error("LEMONSQUEEZY_STORE_ID is not set");
50
+ }
51
+ return storeId;
52
+ }
53
+ function getAppUrl() {
54
+ return config.appUrl || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";
55
+ }
56
+ function initLemonSqueezy() {
57
+ (0, import_lemonsqueezy.lemonSqueezySetup)({ apiKey: getApiKey() });
58
+ }
59
+ async function getSubscriptionById(subscriptionId) {
60
+ initLemonSqueezy();
61
+ const { data, error } = await (0, import_lemonsqueezy.getSubscription)(subscriptionId);
62
+ if (error) throw new Error(error.message);
63
+ return data;
64
+ }
65
+ async function cancelSubscriptionById(subscriptionId) {
66
+ initLemonSqueezy();
67
+ const { data, error } = await (0, import_lemonsqueezy.cancelSubscription)(subscriptionId);
68
+ if (error) throw new Error(error.message);
69
+ return data;
70
+ }
71
+ async function resumeSubscription(subscriptionId) {
72
+ initLemonSqueezy();
73
+ const { data, error } = await (0, import_lemonsqueezy.updateSubscription)(subscriptionId, {
74
+ cancelled: false
75
+ });
76
+ if (error) throw new Error(error.message);
77
+ return data;
78
+ }
79
+ async function createCheckoutUrl(options) {
80
+ initLemonSqueezy();
81
+ const { data, error } = await (0, import_lemonsqueezy.createCheckout)(getStoreId(), options.variantId, {
82
+ checkoutData: {
83
+ email: options.email,
84
+ name: options.name || void 0,
85
+ custom: {
86
+ user_id: options.userId
87
+ }
88
+ },
89
+ productOptions: {
90
+ redirectUrl: options.redirectUrl || `${getAppUrl()}/dashboard/settings/billing`
91
+ }
92
+ });
93
+ if (error) throw new Error(error.message);
94
+ return data?.data.attributes.url;
95
+ }
96
+ async function getCustomerSubscriptions(customerId) {
97
+ initLemonSqueezy();
98
+ const { data, error } = await (0, import_lemonsqueezy.listSubscriptions)({
99
+ filter: { userEmail: customerId }
100
+ });
101
+ if (error) throw new Error(error.message);
102
+ return data;
103
+ }
104
+ function isSubscriptionActive(status) {
105
+ return ["active", "on_trial", "paused"].includes(status);
106
+ }
107
+ function getSubscriptionStatusLabel(status) {
108
+ const labels = {
109
+ active: "Active",
110
+ on_trial: "Trial",
111
+ paused: "Paused",
112
+ past_due: "Past Due",
113
+ unpaid: "Unpaid",
114
+ cancelled: "Cancelled",
115
+ expired: "Expired"
116
+ };
117
+ return labels[status] || status;
118
+ }
119
+ // Annotate the CommonJS export names for ESM import in node:
120
+ 0 && (module.exports = {
121
+ cancelSubscriptionById,
122
+ configureLemonSqueezy,
123
+ createCheckoutUrl,
124
+ getCustomerSubscriptions,
125
+ getSubscriptionById,
126
+ getSubscriptionStatusLabel,
127
+ initLemonSqueezy,
128
+ isSubscriptionActive,
129
+ resumeSubscription
130
+ });
131
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lemonsqueezy/index.ts"],"sourcesContent":["import {\n lemonSqueezySetup,\n getSubscription,\n cancelSubscription,\n updateSubscription,\n createCheckout,\n listSubscriptions,\n} from \"@lemonsqueezy/lemonsqueezy.js\";\n\nexport interface LemonSqueezyConfig {\n apiKey?: string;\n storeId?: string;\n appUrl?: string;\n}\n\nlet config: LemonSqueezyConfig = {};\n\nexport function configureLemonSqueezy(options: LemonSqueezyConfig) {\n config = { ...config, ...options };\n}\n\nfunction getApiKey(): string {\n const apiKey = config.apiKey || process.env.LEMONSQUEEZY_API_KEY;\n if (!apiKey) {\n throw new Error(\"LEMONSQUEEZY_API_KEY is not set\");\n }\n return apiKey;\n}\n\nfunction getStoreId(): string {\n const storeId = config.storeId || process.env.LEMONSQUEEZY_STORE_ID;\n if (!storeId) {\n throw new Error(\"LEMONSQUEEZY_STORE_ID is not set\");\n }\n return storeId;\n}\n\nfunction getAppUrl(): string {\n return config.appUrl || process.env.NEXT_PUBLIC_APP_URL || \"http://localhost:3000\";\n}\n\nexport function initLemonSqueezy() {\n lemonSqueezySetup({ apiKey: getApiKey() });\n}\n\nexport async function getSubscriptionById(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await getSubscription(subscriptionId);\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport async function cancelSubscriptionById(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await cancelSubscription(subscriptionId);\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport async function resumeSubscription(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await updateSubscription(subscriptionId, {\n cancelled: false,\n });\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport interface CreateCheckoutOptions {\n variantId: string;\n email: string;\n name?: string;\n userId: string;\n redirectUrl?: string;\n}\n\nexport async function createCheckoutUrl(options: CreateCheckoutOptions) {\n initLemonSqueezy();\n\n const { data, error } = await createCheckout(getStoreId(), options.variantId, {\n checkoutData: {\n email: options.email,\n name: options.name || undefined,\n custom: {\n user_id: options.userId,\n },\n },\n productOptions: {\n redirectUrl: options.redirectUrl || `${getAppUrl()}/dashboard/settings/billing`,\n },\n });\n\n if (error) throw new Error(error.message);\n return data?.data.attributes.url;\n}\n\nexport async function getCustomerSubscriptions(customerId: string) {\n initLemonSqueezy();\n const { data, error } = await listSubscriptions({\n filter: { userEmail: customerId },\n });\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport function isSubscriptionActive(status: string) {\n return [\"active\", \"on_trial\", \"paused\"].includes(status);\n}\n\nexport function getSubscriptionStatusLabel(status: string) {\n const labels: Record<string, string> = {\n active: \"Active\",\n on_trial: \"Trial\",\n paused: \"Paused\",\n past_due: \"Past Due\",\n unpaid: \"Unpaid\",\n cancelled: \"Cancelled\",\n expired: \"Expired\",\n };\n return labels[status] || status;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAOO;AAQP,IAAI,SAA6B,CAAC;AAE3B,SAAS,sBAAsB,SAA6B;AACjE,WAAS,EAAE,GAAG,QAAQ,GAAG,QAAQ;AACnC;AAEA,SAAS,YAAoB;AAC3B,QAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;AAC5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AACA,SAAO;AACT;AAEA,SAAS,aAAqB;AAC5B,QAAM,UAAU,OAAO,WAAW,QAAQ,IAAI;AAC9C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AACA,SAAO;AACT;AAEA,SAAS,YAAoB;AAC3B,SAAO,OAAO,UAAU,QAAQ,IAAI,uBAAuB;AAC7D;AAEO,SAAS,mBAAmB;AACjC,6CAAkB,EAAE,QAAQ,UAAU,EAAE,CAAC;AAC3C;AAEA,eAAsB,oBAAoB,gBAAwB;AAChE,mBAAiB;AACjB,QAAM,EAAE,MAAM,MAAM,IAAI,UAAM,qCAAgB,cAAc;AAC5D,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO;AACT;AAEA,eAAsB,uBAAuB,gBAAwB;AACnE,mBAAiB;AACjB,QAAM,EAAE,MAAM,MAAM,IAAI,UAAM,wCAAmB,cAAc;AAC/D,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO;AACT;AAEA,eAAsB,mBAAmB,gBAAwB;AAC/D,mBAAiB;AACjB,QAAM,EAAE,MAAM,MAAM,IAAI,UAAM,wCAAmB,gBAAgB;AAAA,IAC/D,WAAW;AAAA,EACb,CAAC;AACD,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO;AACT;AAUA,eAAsB,kBAAkB,SAAgC;AACtE,mBAAiB;AAEjB,QAAM,EAAE,MAAM,MAAM,IAAI,UAAM,oCAAe,WAAW,GAAG,QAAQ,WAAW;AAAA,IAC5E,cAAc;AAAA,MACZ,OAAO,QAAQ;AAAA,MACf,MAAM,QAAQ,QAAQ;AAAA,MACtB,QAAQ;AAAA,QACN,SAAS,QAAQ;AAAA,MACnB;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,MACd,aAAa,QAAQ,eAAe,GAAG,UAAU,CAAC;AAAA,IACpD;AAAA,EACF,CAAC;AAED,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO,MAAM,KAAK,WAAW;AAC/B;AAEA,eAAsB,yBAAyB,YAAoB;AACjE,mBAAiB;AACjB,QAAM,EAAE,MAAM,MAAM,IAAI,UAAM,uCAAkB;AAAA,IAC9C,QAAQ,EAAE,WAAW,WAAW;AAAA,EAClC,CAAC;AACD,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO;AACT;AAEO,SAAS,qBAAqB,QAAgB;AACnD,SAAO,CAAC,UAAU,YAAY,QAAQ,EAAE,SAAS,MAAM;AACzD;AAEO,SAAS,2BAA2B,QAAgB;AACzD,QAAM,SAAiC;AAAA,IACrC,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AACA,SAAO,OAAO,MAAM,KAAK;AAC3B;","names":[]}
@@ -0,0 +1,105 @@
1
+ // src/lemonsqueezy/index.ts
2
+ import {
3
+ lemonSqueezySetup,
4
+ getSubscription,
5
+ cancelSubscription,
6
+ updateSubscription,
7
+ createCheckout,
8
+ listSubscriptions
9
+ } from "@lemonsqueezy/lemonsqueezy.js";
10
+ var config = {};
11
+ function configureLemonSqueezy(options) {
12
+ config = { ...config, ...options };
13
+ }
14
+ function getApiKey() {
15
+ const apiKey = config.apiKey || process.env.LEMONSQUEEZY_API_KEY;
16
+ if (!apiKey) {
17
+ throw new Error("LEMONSQUEEZY_API_KEY is not set");
18
+ }
19
+ return apiKey;
20
+ }
21
+ function getStoreId() {
22
+ const storeId = config.storeId || process.env.LEMONSQUEEZY_STORE_ID;
23
+ if (!storeId) {
24
+ throw new Error("LEMONSQUEEZY_STORE_ID is not set");
25
+ }
26
+ return storeId;
27
+ }
28
+ function getAppUrl() {
29
+ return config.appUrl || process.env.NEXT_PUBLIC_APP_URL || "http://localhost:3000";
30
+ }
31
+ function initLemonSqueezy() {
32
+ lemonSqueezySetup({ apiKey: getApiKey() });
33
+ }
34
+ async function getSubscriptionById(subscriptionId) {
35
+ initLemonSqueezy();
36
+ const { data, error } = await getSubscription(subscriptionId);
37
+ if (error) throw new Error(error.message);
38
+ return data;
39
+ }
40
+ async function cancelSubscriptionById(subscriptionId) {
41
+ initLemonSqueezy();
42
+ const { data, error } = await cancelSubscription(subscriptionId);
43
+ if (error) throw new Error(error.message);
44
+ return data;
45
+ }
46
+ async function resumeSubscription(subscriptionId) {
47
+ initLemonSqueezy();
48
+ const { data, error } = await updateSubscription(subscriptionId, {
49
+ cancelled: false
50
+ });
51
+ if (error) throw new Error(error.message);
52
+ return data;
53
+ }
54
+ async function createCheckoutUrl(options) {
55
+ initLemonSqueezy();
56
+ const { data, error } = await createCheckout(getStoreId(), options.variantId, {
57
+ checkoutData: {
58
+ email: options.email,
59
+ name: options.name || void 0,
60
+ custom: {
61
+ user_id: options.userId
62
+ }
63
+ },
64
+ productOptions: {
65
+ redirectUrl: options.redirectUrl || `${getAppUrl()}/dashboard/settings/billing`
66
+ }
67
+ });
68
+ if (error) throw new Error(error.message);
69
+ return data?.data.attributes.url;
70
+ }
71
+ async function getCustomerSubscriptions(customerId) {
72
+ initLemonSqueezy();
73
+ const { data, error } = await listSubscriptions({
74
+ filter: { userEmail: customerId }
75
+ });
76
+ if (error) throw new Error(error.message);
77
+ return data;
78
+ }
79
+ function isSubscriptionActive(status) {
80
+ return ["active", "on_trial", "paused"].includes(status);
81
+ }
82
+ function getSubscriptionStatusLabel(status) {
83
+ const labels = {
84
+ active: "Active",
85
+ on_trial: "Trial",
86
+ paused: "Paused",
87
+ past_due: "Past Due",
88
+ unpaid: "Unpaid",
89
+ cancelled: "Cancelled",
90
+ expired: "Expired"
91
+ };
92
+ return labels[status] || status;
93
+ }
94
+ export {
95
+ cancelSubscriptionById,
96
+ configureLemonSqueezy,
97
+ createCheckoutUrl,
98
+ getCustomerSubscriptions,
99
+ getSubscriptionById,
100
+ getSubscriptionStatusLabel,
101
+ initLemonSqueezy,
102
+ isSubscriptionActive,
103
+ resumeSubscription
104
+ };
105
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/lemonsqueezy/index.ts"],"sourcesContent":["import {\n lemonSqueezySetup,\n getSubscription,\n cancelSubscription,\n updateSubscription,\n createCheckout,\n listSubscriptions,\n} from \"@lemonsqueezy/lemonsqueezy.js\";\n\nexport interface LemonSqueezyConfig {\n apiKey?: string;\n storeId?: string;\n appUrl?: string;\n}\n\nlet config: LemonSqueezyConfig = {};\n\nexport function configureLemonSqueezy(options: LemonSqueezyConfig) {\n config = { ...config, ...options };\n}\n\nfunction getApiKey(): string {\n const apiKey = config.apiKey || process.env.LEMONSQUEEZY_API_KEY;\n if (!apiKey) {\n throw new Error(\"LEMONSQUEEZY_API_KEY is not set\");\n }\n return apiKey;\n}\n\nfunction getStoreId(): string {\n const storeId = config.storeId || process.env.LEMONSQUEEZY_STORE_ID;\n if (!storeId) {\n throw new Error(\"LEMONSQUEEZY_STORE_ID is not set\");\n }\n return storeId;\n}\n\nfunction getAppUrl(): string {\n return config.appUrl || process.env.NEXT_PUBLIC_APP_URL || \"http://localhost:3000\";\n}\n\nexport function initLemonSqueezy() {\n lemonSqueezySetup({ apiKey: getApiKey() });\n}\n\nexport async function getSubscriptionById(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await getSubscription(subscriptionId);\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport async function cancelSubscriptionById(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await cancelSubscription(subscriptionId);\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport async function resumeSubscription(subscriptionId: string) {\n initLemonSqueezy();\n const { data, error } = await updateSubscription(subscriptionId, {\n cancelled: false,\n });\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport interface CreateCheckoutOptions {\n variantId: string;\n email: string;\n name?: string;\n userId: string;\n redirectUrl?: string;\n}\n\nexport async function createCheckoutUrl(options: CreateCheckoutOptions) {\n initLemonSqueezy();\n\n const { data, error } = await createCheckout(getStoreId(), options.variantId, {\n checkoutData: {\n email: options.email,\n name: options.name || undefined,\n custom: {\n user_id: options.userId,\n },\n },\n productOptions: {\n redirectUrl: options.redirectUrl || `${getAppUrl()}/dashboard/settings/billing`,\n },\n });\n\n if (error) throw new Error(error.message);\n return data?.data.attributes.url;\n}\n\nexport async function getCustomerSubscriptions(customerId: string) {\n initLemonSqueezy();\n const { data, error } = await listSubscriptions({\n filter: { userEmail: customerId },\n });\n if (error) throw new Error(error.message);\n return data;\n}\n\nexport function isSubscriptionActive(status: string) {\n return [\"active\", \"on_trial\", \"paused\"].includes(status);\n}\n\nexport function getSubscriptionStatusLabel(status: string) {\n const labels: Record<string, string> = {\n active: \"Active\",\n on_trial: \"Trial\",\n paused: \"Paused\",\n past_due: \"Past Due\",\n unpaid: \"Unpaid\",\n cancelled: \"Cancelled\",\n expired: \"Expired\",\n };\n return labels[status] || status;\n}\n"],"mappings":";AAAA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAQP,IAAI,SAA6B,CAAC;AAE3B,SAAS,sBAAsB,SAA6B;AACjE,WAAS,EAAE,GAAG,QAAQ,GAAG,QAAQ;AACnC;AAEA,SAAS,YAAoB;AAC3B,QAAM,SAAS,OAAO,UAAU,QAAQ,IAAI;AAC5C,MAAI,CAAC,QAAQ;AACX,UAAM,IAAI,MAAM,iCAAiC;AAAA,EACnD;AACA,SAAO;AACT;AAEA,SAAS,aAAqB;AAC5B,QAAM,UAAU,OAAO,WAAW,QAAQ,IAAI;AAC9C,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,kCAAkC;AAAA,EACpD;AACA,SAAO;AACT;AAEA,SAAS,YAAoB;AAC3B,SAAO,OAAO,UAAU,QAAQ,IAAI,uBAAuB;AAC7D;AAEO,SAAS,mBAAmB;AACjC,oBAAkB,EAAE,QAAQ,UAAU,EAAE,CAAC;AAC3C;AAEA,eAAsB,oBAAoB,gBAAwB;AAChE,mBAAiB;AACjB,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,gBAAgB,cAAc;AAC5D,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO;AACT;AAEA,eAAsB,uBAAuB,gBAAwB;AACnE,mBAAiB;AACjB,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,mBAAmB,cAAc;AAC/D,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO;AACT;AAEA,eAAsB,mBAAmB,gBAAwB;AAC/D,mBAAiB;AACjB,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,mBAAmB,gBAAgB;AAAA,IAC/D,WAAW;AAAA,EACb,CAAC;AACD,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO;AACT;AAUA,eAAsB,kBAAkB,SAAgC;AACtE,mBAAiB;AAEjB,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,eAAe,WAAW,GAAG,QAAQ,WAAW;AAAA,IAC5E,cAAc;AAAA,MACZ,OAAO,QAAQ;AAAA,MACf,MAAM,QAAQ,QAAQ;AAAA,MACtB,QAAQ;AAAA,QACN,SAAS,QAAQ;AAAA,MACnB;AAAA,IACF;AAAA,IACA,gBAAgB;AAAA,MACd,aAAa,QAAQ,eAAe,GAAG,UAAU,CAAC;AAAA,IACpD;AAAA,EACF,CAAC;AAED,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO,MAAM,KAAK,WAAW;AAC/B;AAEA,eAAsB,yBAAyB,YAAoB;AACjE,mBAAiB;AACjB,QAAM,EAAE,MAAM,MAAM,IAAI,MAAM,kBAAkB;AAAA,IAC9C,QAAQ,EAAE,WAAW,WAAW;AAAA,EAClC,CAAC;AACD,MAAI,MAAO,OAAM,IAAI,MAAM,MAAM,OAAO;AACxC,SAAO;AACT;AAEO,SAAS,qBAAqB,QAAgB;AACnD,SAAO,CAAC,UAAU,YAAY,QAAQ,EAAE,SAAS,MAAM;AACzD;AAEO,SAAS,2BAA2B,QAAgB;AACzD,QAAM,SAAiC;AAAA,IACrC,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,SAAS;AAAA,EACX;AACA,SAAO,OAAO,MAAM,KAAK;AAC3B;","names":[]}
@@ -0,0 +1,38 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ interface WebhookEvent {
4
+ meta: {
5
+ event_name: string;
6
+ custom_data?: {
7
+ user_id?: string;
8
+ };
9
+ };
10
+ data: {
11
+ id: string;
12
+ type: string;
13
+ attributes: {
14
+ status: string;
15
+ customer_id: number;
16
+ product_id: number;
17
+ variant_id: number;
18
+ renews_at: string | null;
19
+ ends_at: string | null;
20
+ trial_ends_at: string | null;
21
+ };
22
+ };
23
+ }
24
+ interface WebhookHandlers {
25
+ onSubscriptionCreated?: (event: WebhookEvent) => Promise<void>;
26
+ onSubscriptionUpdated?: (event: WebhookEvent) => Promise<void>;
27
+ onSubscriptionCancelled?: (event: WebhookEvent) => Promise<void>;
28
+ onSubscriptionResumed?: (event: WebhookEvent) => Promise<void>;
29
+ onSubscriptionExpired?: (event: WebhookEvent) => Promise<void>;
30
+ onSubscriptionPaused?: (event: WebhookEvent) => Promise<void>;
31
+ onSubscriptionUnpaused?: (event: WebhookEvent) => Promise<void>;
32
+ onSubscriptionPaymentSuccess?: (event: WebhookEvent) => Promise<void>;
33
+ onSubscriptionPaymentFailed?: (event: WebhookEvent) => Promise<void>;
34
+ onOrderCreated?: (event: WebhookEvent) => Promise<void>;
35
+ }
36
+ declare function createWebhookHandler(handlers: WebhookHandlers, webhookSecret?: string): (request: NextRequest) => Promise<NextResponse>;
37
+
38
+ export { type WebhookEvent, type WebhookHandlers, createWebhookHandler };
@@ -0,0 +1,38 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+
3
+ interface WebhookEvent {
4
+ meta: {
5
+ event_name: string;
6
+ custom_data?: {
7
+ user_id?: string;
8
+ };
9
+ };
10
+ data: {
11
+ id: string;
12
+ type: string;
13
+ attributes: {
14
+ status: string;
15
+ customer_id: number;
16
+ product_id: number;
17
+ variant_id: number;
18
+ renews_at: string | null;
19
+ ends_at: string | null;
20
+ trial_ends_at: string | null;
21
+ };
22
+ };
23
+ }
24
+ interface WebhookHandlers {
25
+ onSubscriptionCreated?: (event: WebhookEvent) => Promise<void>;
26
+ onSubscriptionUpdated?: (event: WebhookEvent) => Promise<void>;
27
+ onSubscriptionCancelled?: (event: WebhookEvent) => Promise<void>;
28
+ onSubscriptionResumed?: (event: WebhookEvent) => Promise<void>;
29
+ onSubscriptionExpired?: (event: WebhookEvent) => Promise<void>;
30
+ onSubscriptionPaused?: (event: WebhookEvent) => Promise<void>;
31
+ onSubscriptionUnpaused?: (event: WebhookEvent) => Promise<void>;
32
+ onSubscriptionPaymentSuccess?: (event: WebhookEvent) => Promise<void>;
33
+ onSubscriptionPaymentFailed?: (event: WebhookEvent) => Promise<void>;
34
+ onOrderCreated?: (event: WebhookEvent) => Promise<void>;
35
+ }
36
+ declare function createWebhookHandler(handlers: WebhookHandlers, webhookSecret?: string): (request: NextRequest) => Promise<NextResponse>;
37
+
38
+ export { type WebhookEvent, type WebhookHandlers, createWebhookHandler };
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/webhooks/index.ts
31
+ var webhooks_exports = {};
32
+ __export(webhooks_exports, {
33
+ createWebhookHandler: () => createWebhookHandler
34
+ });
35
+ module.exports = __toCommonJS(webhooks_exports);
36
+ var import_server = require("next/server");
37
+ var import_crypto = __toESM(require("crypto"));
38
+ function verifyWebhookSignature(rawBody, signature, secret) {
39
+ const hmac = import_crypto.default.createHmac("sha256", secret);
40
+ const digest = hmac.update(rawBody).digest("hex");
41
+ return import_crypto.default.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
42
+ }
43
+ function createWebhookHandler(handlers, webhookSecret) {
44
+ return async function handleWebhook(request) {
45
+ const secret = webhookSecret || process.env.LEMONSQUEEZY_WEBHOOK_SECRET;
46
+ if (!secret) {
47
+ console.error("LEMONSQUEEZY_WEBHOOK_SECRET is not set");
48
+ return import_server.NextResponse.json(
49
+ { error: "Webhook secret not configured" },
50
+ { status: 500 }
51
+ );
52
+ }
53
+ const rawBody = await request.text();
54
+ const signature = request.headers.get("x-signature");
55
+ if (!signature) {
56
+ return import_server.NextResponse.json(
57
+ { error: "Missing signature" },
58
+ { status: 401 }
59
+ );
60
+ }
61
+ const isValid = verifyWebhookSignature(rawBody, signature, secret);
62
+ if (!isValid) {
63
+ return import_server.NextResponse.json(
64
+ { error: "Invalid signature" },
65
+ { status: 401 }
66
+ );
67
+ }
68
+ try {
69
+ const event = JSON.parse(rawBody);
70
+ const eventName = event.meta.event_name;
71
+ switch (eventName) {
72
+ case "subscription_created":
73
+ if (handlers.onSubscriptionCreated) {
74
+ await handlers.onSubscriptionCreated(event);
75
+ }
76
+ break;
77
+ case "subscription_updated":
78
+ if (handlers.onSubscriptionUpdated) {
79
+ await handlers.onSubscriptionUpdated(event);
80
+ }
81
+ break;
82
+ case "subscription_cancelled":
83
+ if (handlers.onSubscriptionCancelled) {
84
+ await handlers.onSubscriptionCancelled(event);
85
+ }
86
+ break;
87
+ case "subscription_resumed":
88
+ if (handlers.onSubscriptionResumed) {
89
+ await handlers.onSubscriptionResumed(event);
90
+ }
91
+ break;
92
+ case "subscription_expired":
93
+ if (handlers.onSubscriptionExpired) {
94
+ await handlers.onSubscriptionExpired(event);
95
+ }
96
+ break;
97
+ case "subscription_paused":
98
+ if (handlers.onSubscriptionPaused) {
99
+ await handlers.onSubscriptionPaused(event);
100
+ }
101
+ break;
102
+ case "subscription_unpaused":
103
+ if (handlers.onSubscriptionUnpaused) {
104
+ await handlers.onSubscriptionUnpaused(event);
105
+ }
106
+ break;
107
+ case "subscription_payment_success":
108
+ if (handlers.onSubscriptionPaymentSuccess) {
109
+ await handlers.onSubscriptionPaymentSuccess(event);
110
+ }
111
+ break;
112
+ case "subscription_payment_failed":
113
+ if (handlers.onSubscriptionPaymentFailed) {
114
+ await handlers.onSubscriptionPaymentFailed(event);
115
+ }
116
+ break;
117
+ case "order_created":
118
+ if (handlers.onOrderCreated) {
119
+ await handlers.onOrderCreated(event);
120
+ }
121
+ break;
122
+ default:
123
+ console.log(`Unhandled webhook event: ${eventName}`);
124
+ }
125
+ return import_server.NextResponse.json({ received: true });
126
+ } catch (error) {
127
+ console.error("Webhook processing error:", error);
128
+ return import_server.NextResponse.json(
129
+ { error: "Webhook processing failed" },
130
+ { status: 500 }
131
+ );
132
+ }
133
+ };
134
+ }
135
+ // Annotate the CommonJS export names for ESM import in node:
136
+ 0 && (module.exports = {
137
+ createWebhookHandler
138
+ });
139
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/webhooks/index.ts"],"sourcesContent":["import { NextRequest, NextResponse } from \"next/server\";\nimport crypto from \"crypto\";\n\nexport interface WebhookEvent {\n meta: {\n event_name: string;\n custom_data?: {\n user_id?: string;\n };\n };\n data: {\n id: string;\n type: string;\n attributes: {\n status: string;\n customer_id: number;\n product_id: number;\n variant_id: number;\n renews_at: string | null;\n ends_at: string | null;\n trial_ends_at: string | null;\n };\n };\n}\n\nexport interface WebhookHandlers {\n onSubscriptionCreated?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionUpdated?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionCancelled?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionResumed?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionExpired?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaused?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionUnpaused?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaymentSuccess?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaymentFailed?: (event: WebhookEvent) => Promise<void>;\n onOrderCreated?: (event: WebhookEvent) => Promise<void>;\n}\n\nfunction verifyWebhookSignature(\n rawBody: string,\n signature: string,\n secret: string\n): boolean {\n const hmac = crypto.createHmac(\"sha256\", secret);\n const digest = hmac.update(rawBody).digest(\"hex\");\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));\n}\n\nexport function createWebhookHandler(handlers: WebhookHandlers, webhookSecret?: string) {\n return async function handleWebhook(request: NextRequest): Promise<NextResponse> {\n const secret = webhookSecret || process.env.LEMONSQUEEZY_WEBHOOK_SECRET;\n\n if (!secret) {\n console.error(\"LEMONSQUEEZY_WEBHOOK_SECRET is not set\");\n return NextResponse.json(\n { error: \"Webhook secret not configured\" },\n { status: 500 }\n );\n }\n\n const rawBody = await request.text();\n const signature = request.headers.get(\"x-signature\");\n\n if (!signature) {\n return NextResponse.json(\n { error: \"Missing signature\" },\n { status: 401 }\n );\n }\n\n const isValid = verifyWebhookSignature(rawBody, signature, secret);\n\n if (!isValid) {\n return NextResponse.json(\n { error: \"Invalid signature\" },\n { status: 401 }\n );\n }\n\n try {\n const event: WebhookEvent = JSON.parse(rawBody);\n const eventName = event.meta.event_name;\n\n switch (eventName) {\n case \"subscription_created\":\n if (handlers.onSubscriptionCreated) {\n await handlers.onSubscriptionCreated(event);\n }\n break;\n case \"subscription_updated\":\n if (handlers.onSubscriptionUpdated) {\n await handlers.onSubscriptionUpdated(event);\n }\n break;\n case \"subscription_cancelled\":\n if (handlers.onSubscriptionCancelled) {\n await handlers.onSubscriptionCancelled(event);\n }\n break;\n case \"subscription_resumed\":\n if (handlers.onSubscriptionResumed) {\n await handlers.onSubscriptionResumed(event);\n }\n break;\n case \"subscription_expired\":\n if (handlers.onSubscriptionExpired) {\n await handlers.onSubscriptionExpired(event);\n }\n break;\n case \"subscription_paused\":\n if (handlers.onSubscriptionPaused) {\n await handlers.onSubscriptionPaused(event);\n }\n break;\n case \"subscription_unpaused\":\n if (handlers.onSubscriptionUnpaused) {\n await handlers.onSubscriptionUnpaused(event);\n }\n break;\n case \"subscription_payment_success\":\n if (handlers.onSubscriptionPaymentSuccess) {\n await handlers.onSubscriptionPaymentSuccess(event);\n }\n break;\n case \"subscription_payment_failed\":\n if (handlers.onSubscriptionPaymentFailed) {\n await handlers.onSubscriptionPaymentFailed(event);\n }\n break;\n case \"order_created\":\n if (handlers.onOrderCreated) {\n await handlers.onOrderCreated(event);\n }\n break;\n default:\n console.log(`Unhandled webhook event: ${eventName}`);\n }\n\n return NextResponse.json({ received: true });\n } catch (error) {\n console.error(\"Webhook processing error:\", error);\n return NextResponse.json(\n { error: \"Webhook processing failed\" },\n { status: 500 }\n );\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,oBAA0C;AAC1C,oBAAmB;AAqCnB,SAAS,uBACP,SACA,WACA,QACS;AACT,QAAM,OAAO,cAAAA,QAAO,WAAW,UAAU,MAAM;AAC/C,QAAM,SAAS,KAAK,OAAO,OAAO,EAAE,OAAO,KAAK;AAChD,SAAO,cAAAA,QAAO,gBAAgB,OAAO,KAAK,SAAS,GAAG,OAAO,KAAK,MAAM,CAAC;AAC3E;AAEO,SAAS,qBAAqB,UAA2B,eAAwB;AACtF,SAAO,eAAe,cAAc,SAA6C;AAC/E,UAAM,SAAS,iBAAiB,QAAQ,IAAI;AAE5C,QAAI,CAAC,QAAQ;AACX,cAAQ,MAAM,wCAAwC;AACtD,aAAO,2BAAa;AAAA,QAClB,EAAE,OAAO,gCAAgC;AAAA,QACzC,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,UAAM,YAAY,QAAQ,QAAQ,IAAI,aAAa;AAEnD,QAAI,CAAC,WAAW;AACd,aAAO,2BAAa;AAAA,QAClB,EAAE,OAAO,oBAAoB;AAAA,QAC7B,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAEA,UAAM,UAAU,uBAAuB,SAAS,WAAW,MAAM;AAEjE,QAAI,CAAC,SAAS;AACZ,aAAO,2BAAa;AAAA,QAClB,EAAE,OAAO,oBAAoB;AAAA,QAC7B,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAAsB,KAAK,MAAM,OAAO;AAC9C,YAAM,YAAY,MAAM,KAAK;AAE7B,cAAQ,WAAW;AAAA,QACjB,KAAK;AACH,cAAI,SAAS,uBAAuB;AAClC,kBAAM,SAAS,sBAAsB,KAAK;AAAA,UAC5C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,uBAAuB;AAClC,kBAAM,SAAS,sBAAsB,KAAK;AAAA,UAC5C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,yBAAyB;AACpC,kBAAM,SAAS,wBAAwB,KAAK;AAAA,UAC9C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,uBAAuB;AAClC,kBAAM,SAAS,sBAAsB,KAAK;AAAA,UAC5C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,uBAAuB;AAClC,kBAAM,SAAS,sBAAsB,KAAK;AAAA,UAC5C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,sBAAsB;AACjC,kBAAM,SAAS,qBAAqB,KAAK;AAAA,UAC3C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,wBAAwB;AACnC,kBAAM,SAAS,uBAAuB,KAAK;AAAA,UAC7C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,8BAA8B;AACzC,kBAAM,SAAS,6BAA6B,KAAK;AAAA,UACnD;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,6BAA6B;AACxC,kBAAM,SAAS,4BAA4B,KAAK;AAAA,UAClD;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,gBAAgB;AAC3B,kBAAM,SAAS,eAAe,KAAK;AAAA,UACrC;AACA;AAAA,QACF;AACE,kBAAQ,IAAI,4BAA4B,SAAS,EAAE;AAAA,MACvD;AAEA,aAAO,2BAAa,KAAK,EAAE,UAAU,KAAK,CAAC;AAAA,IAC7C,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAChD,aAAO,2BAAa;AAAA,QAClB,EAAE,OAAO,4BAA4B;AAAA,QACrC,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;","names":["crypto"]}
@@ -0,0 +1,104 @@
1
+ // src/webhooks/index.ts
2
+ import { NextResponse } from "next/server";
3
+ import crypto from "crypto";
4
+ function verifyWebhookSignature(rawBody, signature, secret) {
5
+ const hmac = crypto.createHmac("sha256", secret);
6
+ const digest = hmac.update(rawBody).digest("hex");
7
+ return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));
8
+ }
9
+ function createWebhookHandler(handlers, webhookSecret) {
10
+ return async function handleWebhook(request) {
11
+ const secret = webhookSecret || process.env.LEMONSQUEEZY_WEBHOOK_SECRET;
12
+ if (!secret) {
13
+ console.error("LEMONSQUEEZY_WEBHOOK_SECRET is not set");
14
+ return NextResponse.json(
15
+ { error: "Webhook secret not configured" },
16
+ { status: 500 }
17
+ );
18
+ }
19
+ const rawBody = await request.text();
20
+ const signature = request.headers.get("x-signature");
21
+ if (!signature) {
22
+ return NextResponse.json(
23
+ { error: "Missing signature" },
24
+ { status: 401 }
25
+ );
26
+ }
27
+ const isValid = verifyWebhookSignature(rawBody, signature, secret);
28
+ if (!isValid) {
29
+ return NextResponse.json(
30
+ { error: "Invalid signature" },
31
+ { status: 401 }
32
+ );
33
+ }
34
+ try {
35
+ const event = JSON.parse(rawBody);
36
+ const eventName = event.meta.event_name;
37
+ switch (eventName) {
38
+ case "subscription_created":
39
+ if (handlers.onSubscriptionCreated) {
40
+ await handlers.onSubscriptionCreated(event);
41
+ }
42
+ break;
43
+ case "subscription_updated":
44
+ if (handlers.onSubscriptionUpdated) {
45
+ await handlers.onSubscriptionUpdated(event);
46
+ }
47
+ break;
48
+ case "subscription_cancelled":
49
+ if (handlers.onSubscriptionCancelled) {
50
+ await handlers.onSubscriptionCancelled(event);
51
+ }
52
+ break;
53
+ case "subscription_resumed":
54
+ if (handlers.onSubscriptionResumed) {
55
+ await handlers.onSubscriptionResumed(event);
56
+ }
57
+ break;
58
+ case "subscription_expired":
59
+ if (handlers.onSubscriptionExpired) {
60
+ await handlers.onSubscriptionExpired(event);
61
+ }
62
+ break;
63
+ case "subscription_paused":
64
+ if (handlers.onSubscriptionPaused) {
65
+ await handlers.onSubscriptionPaused(event);
66
+ }
67
+ break;
68
+ case "subscription_unpaused":
69
+ if (handlers.onSubscriptionUnpaused) {
70
+ await handlers.onSubscriptionUnpaused(event);
71
+ }
72
+ break;
73
+ case "subscription_payment_success":
74
+ if (handlers.onSubscriptionPaymentSuccess) {
75
+ await handlers.onSubscriptionPaymentSuccess(event);
76
+ }
77
+ break;
78
+ case "subscription_payment_failed":
79
+ if (handlers.onSubscriptionPaymentFailed) {
80
+ await handlers.onSubscriptionPaymentFailed(event);
81
+ }
82
+ break;
83
+ case "order_created":
84
+ if (handlers.onOrderCreated) {
85
+ await handlers.onOrderCreated(event);
86
+ }
87
+ break;
88
+ default:
89
+ console.log(`Unhandled webhook event: ${eventName}`);
90
+ }
91
+ return NextResponse.json({ received: true });
92
+ } catch (error) {
93
+ console.error("Webhook processing error:", error);
94
+ return NextResponse.json(
95
+ { error: "Webhook processing failed" },
96
+ { status: 500 }
97
+ );
98
+ }
99
+ };
100
+ }
101
+ export {
102
+ createWebhookHandler
103
+ };
104
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/webhooks/index.ts"],"sourcesContent":["import { NextRequest, NextResponse } from \"next/server\";\nimport crypto from \"crypto\";\n\nexport interface WebhookEvent {\n meta: {\n event_name: string;\n custom_data?: {\n user_id?: string;\n };\n };\n data: {\n id: string;\n type: string;\n attributes: {\n status: string;\n customer_id: number;\n product_id: number;\n variant_id: number;\n renews_at: string | null;\n ends_at: string | null;\n trial_ends_at: string | null;\n };\n };\n}\n\nexport interface WebhookHandlers {\n onSubscriptionCreated?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionUpdated?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionCancelled?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionResumed?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionExpired?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaused?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionUnpaused?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaymentSuccess?: (event: WebhookEvent) => Promise<void>;\n onSubscriptionPaymentFailed?: (event: WebhookEvent) => Promise<void>;\n onOrderCreated?: (event: WebhookEvent) => Promise<void>;\n}\n\nfunction verifyWebhookSignature(\n rawBody: string,\n signature: string,\n secret: string\n): boolean {\n const hmac = crypto.createHmac(\"sha256\", secret);\n const digest = hmac.update(rawBody).digest(\"hex\");\n return crypto.timingSafeEqual(Buffer.from(signature), Buffer.from(digest));\n}\n\nexport function createWebhookHandler(handlers: WebhookHandlers, webhookSecret?: string) {\n return async function handleWebhook(request: NextRequest): Promise<NextResponse> {\n const secret = webhookSecret || process.env.LEMONSQUEEZY_WEBHOOK_SECRET;\n\n if (!secret) {\n console.error(\"LEMONSQUEEZY_WEBHOOK_SECRET is not set\");\n return NextResponse.json(\n { error: \"Webhook secret not configured\" },\n { status: 500 }\n );\n }\n\n const rawBody = await request.text();\n const signature = request.headers.get(\"x-signature\");\n\n if (!signature) {\n return NextResponse.json(\n { error: \"Missing signature\" },\n { status: 401 }\n );\n }\n\n const isValid = verifyWebhookSignature(rawBody, signature, secret);\n\n if (!isValid) {\n return NextResponse.json(\n { error: \"Invalid signature\" },\n { status: 401 }\n );\n }\n\n try {\n const event: WebhookEvent = JSON.parse(rawBody);\n const eventName = event.meta.event_name;\n\n switch (eventName) {\n case \"subscription_created\":\n if (handlers.onSubscriptionCreated) {\n await handlers.onSubscriptionCreated(event);\n }\n break;\n case \"subscription_updated\":\n if (handlers.onSubscriptionUpdated) {\n await handlers.onSubscriptionUpdated(event);\n }\n break;\n case \"subscription_cancelled\":\n if (handlers.onSubscriptionCancelled) {\n await handlers.onSubscriptionCancelled(event);\n }\n break;\n case \"subscription_resumed\":\n if (handlers.onSubscriptionResumed) {\n await handlers.onSubscriptionResumed(event);\n }\n break;\n case \"subscription_expired\":\n if (handlers.onSubscriptionExpired) {\n await handlers.onSubscriptionExpired(event);\n }\n break;\n case \"subscription_paused\":\n if (handlers.onSubscriptionPaused) {\n await handlers.onSubscriptionPaused(event);\n }\n break;\n case \"subscription_unpaused\":\n if (handlers.onSubscriptionUnpaused) {\n await handlers.onSubscriptionUnpaused(event);\n }\n break;\n case \"subscription_payment_success\":\n if (handlers.onSubscriptionPaymentSuccess) {\n await handlers.onSubscriptionPaymentSuccess(event);\n }\n break;\n case \"subscription_payment_failed\":\n if (handlers.onSubscriptionPaymentFailed) {\n await handlers.onSubscriptionPaymentFailed(event);\n }\n break;\n case \"order_created\":\n if (handlers.onOrderCreated) {\n await handlers.onOrderCreated(event);\n }\n break;\n default:\n console.log(`Unhandled webhook event: ${eventName}`);\n }\n\n return NextResponse.json({ received: true });\n } catch (error) {\n console.error(\"Webhook processing error:\", error);\n return NextResponse.json(\n { error: \"Webhook processing failed\" },\n { status: 500 }\n );\n }\n };\n}\n"],"mappings":";AAAA,SAAsB,oBAAoB;AAC1C,OAAO,YAAY;AAqCnB,SAAS,uBACP,SACA,WACA,QACS;AACT,QAAM,OAAO,OAAO,WAAW,UAAU,MAAM;AAC/C,QAAM,SAAS,KAAK,OAAO,OAAO,EAAE,OAAO,KAAK;AAChD,SAAO,OAAO,gBAAgB,OAAO,KAAK,SAAS,GAAG,OAAO,KAAK,MAAM,CAAC;AAC3E;AAEO,SAAS,qBAAqB,UAA2B,eAAwB;AACtF,SAAO,eAAe,cAAc,SAA6C;AAC/E,UAAM,SAAS,iBAAiB,QAAQ,IAAI;AAE5C,QAAI,CAAC,QAAQ;AACX,cAAQ,MAAM,wCAAwC;AACtD,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,gCAAgC;AAAA,QACzC,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,QAAQ,KAAK;AACnC,UAAM,YAAY,QAAQ,QAAQ,IAAI,aAAa;AAEnD,QAAI,CAAC,WAAW;AACd,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,oBAAoB;AAAA,QAC7B,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAEA,UAAM,UAAU,uBAAuB,SAAS,WAAW,MAAM;AAEjE,QAAI,CAAC,SAAS;AACZ,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,oBAAoB;AAAA,QAC7B,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAEA,QAAI;AACF,YAAM,QAAsB,KAAK,MAAM,OAAO;AAC9C,YAAM,YAAY,MAAM,KAAK;AAE7B,cAAQ,WAAW;AAAA,QACjB,KAAK;AACH,cAAI,SAAS,uBAAuB;AAClC,kBAAM,SAAS,sBAAsB,KAAK;AAAA,UAC5C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,uBAAuB;AAClC,kBAAM,SAAS,sBAAsB,KAAK;AAAA,UAC5C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,yBAAyB;AACpC,kBAAM,SAAS,wBAAwB,KAAK;AAAA,UAC9C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,uBAAuB;AAClC,kBAAM,SAAS,sBAAsB,KAAK;AAAA,UAC5C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,uBAAuB;AAClC,kBAAM,SAAS,sBAAsB,KAAK;AAAA,UAC5C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,sBAAsB;AACjC,kBAAM,SAAS,qBAAqB,KAAK;AAAA,UAC3C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,wBAAwB;AACnC,kBAAM,SAAS,uBAAuB,KAAK;AAAA,UAC7C;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,8BAA8B;AACzC,kBAAM,SAAS,6BAA6B,KAAK;AAAA,UACnD;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,6BAA6B;AACxC,kBAAM,SAAS,4BAA4B,KAAK;AAAA,UAClD;AACA;AAAA,QACF,KAAK;AACH,cAAI,SAAS,gBAAgB;AAC3B,kBAAM,SAAS,eAAe,KAAK;AAAA,UACrC;AACA;AAAA,QACF;AACE,kBAAQ,IAAI,4BAA4B,SAAS,EAAE;AAAA,MACvD;AAEA,aAAO,aAAa,KAAK,EAAE,UAAU,KAAK,CAAC;AAAA,IAC7C,SAAS,OAAO;AACd,cAAQ,MAAM,6BAA6B,KAAK;AAChD,aAAO,aAAa;AAAA,QAClB,EAAE,OAAO,4BAA4B;AAAA,QACrC,EAAE,QAAQ,IAAI;AAAA,MAChB;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@victusvinceere/saas-payments",
3
+ "version": "0.1.0",
4
+ "description": "Payment integration for SaaS Kit with Lemon Squeezy support",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.mjs",
12
+ "require": "./dist/index.js"
13
+ },
14
+ "./lemonsqueezy": {
15
+ "types": "./dist/lemonsqueezy/index.d.ts",
16
+ "import": "./dist/lemonsqueezy/index.mjs",
17
+ "require": "./dist/lemonsqueezy/index.js"
18
+ },
19
+ "./webhooks": {
20
+ "types": "./dist/webhooks/index.d.ts",
21
+ "import": "./dist/webhooks/index.mjs",
22
+ "require": "./dist/webhooks/index.js"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "prisma"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsup",
31
+ "dev": "tsup --watch",
32
+ "typecheck": "tsc --noEmit",
33
+ "clean": "rm -rf dist"
34
+ },
35
+ "peerDependencies": {
36
+ "@victusvinceere/saas-core": "workspace:*",
37
+ "next": ">=15.0.0",
38
+ "react": ">=19.0.0"
39
+ },
40
+ "dependencies": {
41
+ "@lemonsqueezy/lemonsqueezy.js": "^4.0.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20",
45
+ "@types/react": "^19",
46
+ "tsup": "^8.0.2",
47
+ "typescript": "^5"
48
+ },
49
+ "keywords": [
50
+ "saas",
51
+ "payments",
52
+ "lemon-squeezy",
53
+ "subscriptions"
54
+ ],
55
+ "license": "MIT"
56
+ }
@@ -0,0 +1,25 @@
1
+ // Payments Prisma Schema Extension
2
+ // Adds subscription models for Lemon Squeezy integration
3
+
4
+ // ==================== SUBSCRIPTION MODELS (Lemon Squeezy) ====================
5
+
6
+ model Subscription {
7
+ id String @id @default(cuid())
8
+ userId String @unique @map("user_id")
9
+ lemonSqueezyId String @unique @map("lemonsqueezy_id")
10
+ lemonSqueezyCustomerId String @map("lemonsqueezy_customer_id")
11
+ productId String @map("product_id")
12
+ variantId String @map("variant_id")
13
+ status String // active, cancelled, expired, past_due, paused, on_trial, unpaid
14
+ renewsAt DateTime? @map("renews_at")
15
+ endsAt DateTime? @map("ends_at")
16
+ trialEndsAt DateTime? @map("trial_ends_at")
17
+ createdAt DateTime @default(now()) @map("created_at")
18
+ updatedAt DateTime @updatedAt @map("updated_at")
19
+
20
+ @@map("subscriptions")
21
+ }
22
+
23
+ // Note: This schema fragment should be merged with the core schema
24
+ // The User model relation should be added:
25
+ // subscription Subscription?