n8n-nodes-lemonsqueezy 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +216 -0
  3. package/dist/credentials/LemonSqueezyApi.credentials.d.ts +10 -0
  4. package/dist/credentials/LemonSqueezyApi.credentials.js +41 -0
  5. package/dist/nodes/LemonSqueezy/LemonSqueezy.node.d.ts +5 -0
  6. package/dist/nodes/LemonSqueezy/LemonSqueezy.node.js +358 -0
  7. package/dist/nodes/LemonSqueezy/LemonSqueezyTrigger.node.d.ts +12 -0
  8. package/dist/nodes/LemonSqueezy/LemonSqueezyTrigger.node.js +230 -0
  9. package/dist/nodes/LemonSqueezy/constants.d.ts +89 -0
  10. package/dist/nodes/LemonSqueezy/constants.js +207 -0
  11. package/dist/nodes/LemonSqueezy/helpers.d.ts +28 -0
  12. package/dist/nodes/LemonSqueezy/helpers.js +241 -0
  13. package/dist/nodes/LemonSqueezy/lemonSqueezy.svg +20 -0
  14. package/dist/nodes/LemonSqueezy/resources/checkout.d.ts +3 -0
  15. package/dist/nodes/LemonSqueezy/resources/checkout.js +272 -0
  16. package/dist/nodes/LemonSqueezy/resources/customer.d.ts +3 -0
  17. package/dist/nodes/LemonSqueezy/resources/customer.js +242 -0
  18. package/dist/nodes/LemonSqueezy/resources/discount.d.ts +3 -0
  19. package/dist/nodes/LemonSqueezy/resources/discount.js +210 -0
  20. package/dist/nodes/LemonSqueezy/resources/index.d.ts +15 -0
  21. package/dist/nodes/LemonSqueezy/resources/index.js +76 -0
  22. package/dist/nodes/LemonSqueezy/resources/licenseKey.d.ts +3 -0
  23. package/dist/nodes/LemonSqueezy/resources/licenseKey.js +209 -0
  24. package/dist/nodes/LemonSqueezy/resources/order.d.ts +3 -0
  25. package/dist/nodes/LemonSqueezy/resources/order.js +113 -0
  26. package/dist/nodes/LemonSqueezy/resources/product.d.ts +3 -0
  27. package/dist/nodes/LemonSqueezy/resources/product.js +93 -0
  28. package/dist/nodes/LemonSqueezy/resources/store.d.ts +3 -0
  29. package/dist/nodes/LemonSqueezy/resources/store.js +64 -0
  30. package/dist/nodes/LemonSqueezy/resources/subscription.d.ts +3 -0
  31. package/dist/nodes/LemonSqueezy/resources/subscription.js +196 -0
  32. package/dist/nodes/LemonSqueezy/resources/variant.d.ts +3 -0
  33. package/dist/nodes/LemonSqueezy/resources/variant.js +96 -0
  34. package/dist/nodes/LemonSqueezy/resources/webhook.d.ts +3 -0
  35. package/dist/nodes/LemonSqueezy/resources/webhook.js +206 -0
  36. package/dist/nodes/LemonSqueezy/types.d.ts +364 -0
  37. package/dist/nodes/LemonSqueezy/types.js +2 -0
  38. package/package.json +71 -0
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LemonSqueezyTrigger = void 0;
4
+ const constants_1 = require("./constants");
5
+ const helpers_1 = require("./helpers");
6
+ class LemonSqueezyTrigger {
7
+ constructor() {
8
+ this.description = {
9
+ displayName: 'Lemon Squeezy Trigger',
10
+ name: 'lemonSqueezyTrigger',
11
+ icon: 'file:lemonSqueezy.svg',
12
+ group: ['trigger'],
13
+ version: 1,
14
+ subtitle: '={{$parameter["events"].join(", ")}}',
15
+ description: 'Receive events from Lemon Squeezy in real-time',
16
+ defaults: {
17
+ name: 'Lemon Squeezy Trigger',
18
+ },
19
+ inputs: [],
20
+ outputs: ['main'],
21
+ credentials: [
22
+ {
23
+ name: 'lemonSqueezyApi',
24
+ required: true,
25
+ },
26
+ ],
27
+ webhooks: [
28
+ {
29
+ name: 'default',
30
+ httpMethod: 'POST',
31
+ responseMode: 'onReceived',
32
+ path: 'webhook',
33
+ },
34
+ ],
35
+ properties: [
36
+ {
37
+ displayName: 'Store ID',
38
+ name: 'storeId',
39
+ type: 'string',
40
+ required: true,
41
+ default: '',
42
+ description: 'The ID of the store to receive events from',
43
+ },
44
+ {
45
+ displayName: 'Events',
46
+ name: 'events',
47
+ type: 'multiOptions',
48
+ required: true,
49
+ default: [],
50
+ options: constants_1.WEBHOOK_EVENTS,
51
+ description: 'The events to listen for',
52
+ },
53
+ {
54
+ displayName: 'Webhook Secret',
55
+ name: 'webhookSecret',
56
+ type: 'string',
57
+ typeOptions: { password: true },
58
+ required: true,
59
+ default: '',
60
+ description: 'A secret string to verify webhook payloads. Generate a secure random string.',
61
+ },
62
+ {
63
+ displayName: 'Options',
64
+ name: 'options',
65
+ type: 'collection',
66
+ placeholder: 'Add Option',
67
+ default: {},
68
+ options: [
69
+ {
70
+ displayName: 'Test Mode Only',
71
+ name: 'testMode',
72
+ type: 'boolean',
73
+ default: false,
74
+ description: 'Whether to only receive test mode events',
75
+ },
76
+ {
77
+ displayName: 'Verify Signature',
78
+ name: 'verifySignature',
79
+ type: 'boolean',
80
+ default: true,
81
+ description: 'Whether to verify the webhook signature (recommended)',
82
+ },
83
+ ],
84
+ },
85
+ ],
86
+ };
87
+ this.webhookMethods = {
88
+ default: {
89
+ async checkExists() {
90
+ const webhookUrl = this.getNodeWebhookUrl('default');
91
+ const storeId = this.getNodeParameter('storeId');
92
+ const webhookData = this.getWorkflowStaticData('node');
93
+ // Check if we have stored webhook data
94
+ if (webhookData.webhookId) {
95
+ try {
96
+ // Verify the webhook still exists
97
+ await helpers_1.lemonSqueezyApiRequest.call(this, 'GET', `/webhooks/${String(webhookData.webhookId)}`);
98
+ return true;
99
+ }
100
+ catch {
101
+ // Webhook doesn't exist anymore
102
+ delete webhookData.webhookId;
103
+ return false;
104
+ }
105
+ }
106
+ // Check if a webhook with our URL already exists
107
+ try {
108
+ const response = await helpers_1.lemonSqueezyApiRequest.call(this, 'GET', '/webhooks', undefined, {
109
+ 'filter[store_id]': storeId,
110
+ });
111
+ const responseData = response;
112
+ const webhooks = responseData.data;
113
+ if (Array.isArray(webhooks)) {
114
+ const existingWebhook = webhooks.find((webhook) => { var _a; return ((_a = webhook.attributes) === null || _a === void 0 ? void 0 : _a.url) === webhookUrl; });
115
+ if (existingWebhook) {
116
+ webhookData.webhookId = existingWebhook.id;
117
+ return true;
118
+ }
119
+ }
120
+ }
121
+ catch {
122
+ // Error checking webhooks, assume doesn't exist
123
+ }
124
+ return false;
125
+ },
126
+ async create() {
127
+ const webhookUrl = this.getNodeWebhookUrl('default');
128
+ const storeId = this.getNodeParameter('storeId');
129
+ const events = this.getNodeParameter('events');
130
+ const webhookSecret = this.getNodeParameter('webhookSecret');
131
+ const options = this.getNodeParameter('options');
132
+ const webhookData = this.getWorkflowStaticData('node');
133
+ const body = {
134
+ data: {
135
+ type: 'webhooks',
136
+ attributes: {
137
+ url: webhookUrl,
138
+ events,
139
+ secret: webhookSecret,
140
+ test_mode: options.testMode || false,
141
+ },
142
+ relationships: {
143
+ store: {
144
+ data: {
145
+ type: 'stores',
146
+ id: storeId,
147
+ },
148
+ },
149
+ },
150
+ },
151
+ };
152
+ const response = await helpers_1.lemonSqueezyApiRequest.call(this, 'POST', '/webhooks', body);
153
+ const responseData = response;
154
+ const data = responseData.data;
155
+ if (data === null || data === void 0 ? void 0 : data.id) {
156
+ webhookData.webhookId = data.id;
157
+ return true;
158
+ }
159
+ return false;
160
+ },
161
+ async delete() {
162
+ const webhookData = this.getWorkflowStaticData('node');
163
+ if (webhookData.webhookId) {
164
+ try {
165
+ await helpers_1.lemonSqueezyApiRequest.call(this, 'DELETE', `/webhooks/${String(webhookData.webhookId)}`);
166
+ }
167
+ catch {
168
+ // Webhook might already be deleted
169
+ }
170
+ delete webhookData.webhookId;
171
+ }
172
+ return true;
173
+ },
174
+ },
175
+ };
176
+ }
177
+ async webhook() {
178
+ const options = this.getNodeParameter('options');
179
+ const webhookSecret = this.getNodeParameter('webhookSecret');
180
+ // Verify signature if enabled
181
+ if (options.verifySignature !== false) {
182
+ const signature = this.getHeaderData()['x-signature'];
183
+ if (!signature) {
184
+ return {
185
+ webhookResponse: {
186
+ status: 401,
187
+ body: { error: 'Missing signature header' },
188
+ },
189
+ };
190
+ }
191
+ const bodyData = this.getBodyData();
192
+ const rawBody = JSON.stringify(bodyData);
193
+ const isValid = (0, helpers_1.verifyWebhookSignature)(rawBody, signature, webhookSecret);
194
+ if (!isValid) {
195
+ return {
196
+ webhookResponse: {
197
+ status: 401,
198
+ body: { error: 'Invalid signature' },
199
+ },
200
+ };
201
+ }
202
+ }
203
+ const body = this.getBodyData();
204
+ const meta = body.meta;
205
+ const eventName = meta === null || meta === void 0 ? void 0 : meta.event_name;
206
+ // Check if we should process this event
207
+ const subscribedEvents = this.getNodeParameter('events');
208
+ if (!eventName || !subscribedEvents.includes(eventName)) {
209
+ // Event not subscribed, acknowledge but don't trigger workflow
210
+ return {
211
+ webhookResponse: {
212
+ status: 200,
213
+ body: { received: true, processed: false },
214
+ },
215
+ };
216
+ }
217
+ // Return the webhook data
218
+ return {
219
+ workflowData: [
220
+ this.helpers.returnJsonArray({
221
+ event: eventName,
222
+ meta: body.meta,
223
+ data: body.data,
224
+ timestamp: new Date().toISOString(),
225
+ }),
226
+ ],
227
+ };
228
+ }
229
+ }
230
+ exports.LemonSqueezyTrigger = LemonSqueezyTrigger;
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Lemon Squeezy API constants
3
+ */
4
+ export declare const API_BASE_URL = "https://api.lemonsqueezy.com/v1";
5
+ export declare const DEFAULT_PAGE_SIZE = 100;
6
+ export declare const MAX_RETRIES = 3;
7
+ export declare const RETRY_DELAY_MS = 1000;
8
+ export declare const RATE_LIMIT_DELAY_MS = 60000;
9
+ /**
10
+ * Resource to API endpoint mapping
11
+ */
12
+ export declare const RESOURCE_ENDPOINTS: Record<string, string>;
13
+ /**
14
+ * Resource to ID parameter mapping
15
+ */
16
+ export declare const RESOURCE_ID_PARAMS: Record<string, string>;
17
+ /**
18
+ * Webhook event types with descriptions
19
+ */
20
+ export declare const WEBHOOK_EVENTS: {
21
+ name: string;
22
+ value: string;
23
+ description: string;
24
+ }[];
25
+ /**
26
+ * Subscription statuses
27
+ */
28
+ export declare const SUBSCRIPTION_STATUSES: {
29
+ name: string;
30
+ value: string;
31
+ }[];
32
+ /**
33
+ * Order statuses
34
+ */
35
+ export declare const ORDER_STATUSES: {
36
+ name: string;
37
+ value: string;
38
+ }[];
39
+ /**
40
+ * Customer statuses
41
+ */
42
+ export declare const CUSTOMER_STATUSES: {
43
+ name: string;
44
+ value: string;
45
+ }[];
46
+ /**
47
+ * License key statuses
48
+ */
49
+ export declare const LICENSE_KEY_STATUSES: {
50
+ name: string;
51
+ value: string;
52
+ }[];
53
+ /**
54
+ * Discount types
55
+ */
56
+ export declare const DISCOUNT_AMOUNT_TYPES: {
57
+ name: string;
58
+ value: string;
59
+ }[];
60
+ /**
61
+ * Discount duration types
62
+ */
63
+ export declare const DISCOUNT_DURATION_TYPES: {
64
+ name: string;
65
+ value: string;
66
+ description: string;
67
+ }[];
68
+ /**
69
+ * Pause modes for subscriptions
70
+ */
71
+ export declare const PAUSE_MODES: {
72
+ name: string;
73
+ value: string;
74
+ description: string;
75
+ }[];
76
+ /**
77
+ * Product statuses
78
+ */
79
+ export declare const PRODUCT_STATUSES: {
80
+ name: string;
81
+ value: string;
82
+ }[];
83
+ /**
84
+ * Interval types for subscriptions
85
+ */
86
+ export declare const INTERVAL_TYPES: {
87
+ name: string;
88
+ value: string;
89
+ }[];
@@ -0,0 +1,207 @@
1
+ "use strict";
2
+ /**
3
+ * Lemon Squeezy API constants
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.INTERVAL_TYPES = exports.PRODUCT_STATUSES = exports.PAUSE_MODES = exports.DISCOUNT_DURATION_TYPES = exports.DISCOUNT_AMOUNT_TYPES = exports.LICENSE_KEY_STATUSES = exports.CUSTOMER_STATUSES = exports.ORDER_STATUSES = exports.SUBSCRIPTION_STATUSES = exports.WEBHOOK_EVENTS = exports.RESOURCE_ID_PARAMS = exports.RESOURCE_ENDPOINTS = exports.RATE_LIMIT_DELAY_MS = exports.RETRY_DELAY_MS = exports.MAX_RETRIES = exports.DEFAULT_PAGE_SIZE = exports.API_BASE_URL = void 0;
7
+ exports.API_BASE_URL = 'https://api.lemonsqueezy.com/v1';
8
+ exports.DEFAULT_PAGE_SIZE = 100;
9
+ exports.MAX_RETRIES = 3;
10
+ exports.RETRY_DELAY_MS = 1000;
11
+ exports.RATE_LIMIT_DELAY_MS = 60000;
12
+ /**
13
+ * Resource to API endpoint mapping
14
+ */
15
+ exports.RESOURCE_ENDPOINTS = {
16
+ product: 'products',
17
+ order: 'orders',
18
+ subscription: 'subscriptions',
19
+ customer: 'customers',
20
+ licenseKey: 'license-keys',
21
+ discount: 'discounts',
22
+ store: 'stores',
23
+ variant: 'variants',
24
+ checkout: 'checkouts',
25
+ webhook: 'webhooks',
26
+ licenseKeyInstance: 'license-key-instances',
27
+ };
28
+ /**
29
+ * Resource to ID parameter mapping
30
+ */
31
+ exports.RESOURCE_ID_PARAMS = {
32
+ product: 'productId',
33
+ order: 'orderId',
34
+ subscription: 'subscriptionId',
35
+ customer: 'customerId',
36
+ licenseKey: 'licenseKeyId',
37
+ discount: 'discountId',
38
+ store: 'storeId',
39
+ variant: 'variantId',
40
+ checkout: 'checkoutId',
41
+ webhook: 'webhookId',
42
+ licenseKeyInstance: 'licenseKeyInstanceId',
43
+ };
44
+ /**
45
+ * Webhook event types with descriptions
46
+ */
47
+ exports.WEBHOOK_EVENTS = [
48
+ {
49
+ name: 'Order Created',
50
+ value: 'order_created',
51
+ description: 'Triggered when a new order is created',
52
+ },
53
+ {
54
+ name: 'Order Refunded',
55
+ value: 'order_refunded',
56
+ description: 'Triggered when an order is refunded',
57
+ },
58
+ {
59
+ name: 'Subscription Created',
60
+ value: 'subscription_created',
61
+ description: 'Triggered when a new subscription is created',
62
+ },
63
+ {
64
+ name: 'Subscription Updated',
65
+ value: 'subscription_updated',
66
+ description: 'Triggered when a subscription is updated',
67
+ },
68
+ {
69
+ name: 'Subscription Cancelled',
70
+ value: 'subscription_cancelled',
71
+ description: 'Triggered when a subscription is cancelled',
72
+ },
73
+ {
74
+ name: 'Subscription Resumed',
75
+ value: 'subscription_resumed',
76
+ description: 'Triggered when a subscription is resumed',
77
+ },
78
+ {
79
+ name: 'Subscription Expired',
80
+ value: 'subscription_expired',
81
+ description: 'Triggered when a subscription expires',
82
+ },
83
+ {
84
+ name: 'Subscription Paused',
85
+ value: 'subscription_paused',
86
+ description: 'Triggered when a subscription is paused',
87
+ },
88
+ {
89
+ name: 'Subscription Unpaused',
90
+ value: 'subscription_unpaused',
91
+ description: 'Triggered when a subscription is unpaused',
92
+ },
93
+ {
94
+ name: 'Subscription Payment Success',
95
+ value: 'subscription_payment_success',
96
+ description: 'Triggered when a subscription payment succeeds',
97
+ },
98
+ {
99
+ name: 'Subscription Payment Failed',
100
+ value: 'subscription_payment_failed',
101
+ description: 'Triggered when a subscription payment fails',
102
+ },
103
+ {
104
+ name: 'Subscription Payment Recovered',
105
+ value: 'subscription_payment_recovered',
106
+ description: 'Triggered when a failed payment is recovered',
107
+ },
108
+ {
109
+ name: 'Subscription Payment Refunded',
110
+ value: 'subscription_payment_refunded',
111
+ description: 'Triggered when a subscription payment is refunded',
112
+ },
113
+ {
114
+ name: 'License Key Created',
115
+ value: 'license_key_created',
116
+ description: 'Triggered when a license key is created',
117
+ },
118
+ {
119
+ name: 'License Key Updated',
120
+ value: 'license_key_updated',
121
+ description: 'Triggered when a license key is updated',
122
+ },
123
+ ];
124
+ /**
125
+ * Subscription statuses
126
+ */
127
+ exports.SUBSCRIPTION_STATUSES = [
128
+ { name: 'On Trial', value: 'on_trial' },
129
+ { name: 'Active', value: 'active' },
130
+ { name: 'Paused', value: 'paused' },
131
+ { name: 'Past Due', value: 'past_due' },
132
+ { name: 'Unpaid', value: 'unpaid' },
133
+ { name: 'Cancelled', value: 'cancelled' },
134
+ { name: 'Expired', value: 'expired' },
135
+ ];
136
+ /**
137
+ * Order statuses
138
+ */
139
+ exports.ORDER_STATUSES = [
140
+ { name: 'Pending', value: 'pending' },
141
+ { name: 'Failed', value: 'failed' },
142
+ { name: 'Paid', value: 'paid' },
143
+ { name: 'Refunded', value: 'refunded' },
144
+ ];
145
+ /**
146
+ * Customer statuses
147
+ */
148
+ exports.CUSTOMER_STATUSES = [
149
+ { name: 'Subscribed', value: 'subscribed' },
150
+ { name: 'Unsubscribed', value: 'unsubscribed' },
151
+ { name: 'Archived', value: 'archived' },
152
+ { name: 'Requires Verification', value: 'requires_verification' },
153
+ { name: 'Invalid Email', value: 'invalid_email' },
154
+ { name: 'Bounced', value: 'bounced' },
155
+ ];
156
+ /**
157
+ * License key statuses
158
+ */
159
+ exports.LICENSE_KEY_STATUSES = [
160
+ { name: 'Inactive', value: 'inactive' },
161
+ { name: 'Active', value: 'active' },
162
+ { name: 'Expired', value: 'expired' },
163
+ { name: 'Disabled', value: 'disabled' },
164
+ ];
165
+ /**
166
+ * Discount types
167
+ */
168
+ exports.DISCOUNT_AMOUNT_TYPES = [
169
+ { name: 'Percent', value: 'percent' },
170
+ { name: 'Fixed', value: 'fixed' },
171
+ ];
172
+ /**
173
+ * Discount duration types
174
+ */
175
+ exports.DISCOUNT_DURATION_TYPES = [
176
+ { name: 'Once', value: 'once', description: 'Applied only to the first payment' },
177
+ {
178
+ name: 'Repeating',
179
+ value: 'repeating',
180
+ description: 'Applied for a specified number of months',
181
+ },
182
+ { name: 'Forever', value: 'forever', description: 'Applied to all payments' },
183
+ ];
184
+ /**
185
+ * Pause modes for subscriptions
186
+ */
187
+ exports.PAUSE_MODES = [
188
+ { name: 'Not Paused', value: '', description: 'Resume or keep subscription active' },
189
+ { name: 'Void', value: 'void', description: 'Pause without extending billing period' },
190
+ { name: 'Free', value: 'free', description: 'Pause and give free days when resumed' },
191
+ ];
192
+ /**
193
+ * Product statuses
194
+ */
195
+ exports.PRODUCT_STATUSES = [
196
+ { name: 'Draft', value: 'draft' },
197
+ { name: 'Published', value: 'published' },
198
+ ];
199
+ /**
200
+ * Interval types for subscriptions
201
+ */
202
+ exports.INTERVAL_TYPES = [
203
+ { name: 'Day', value: 'day' },
204
+ { name: 'Week', value: 'week' },
205
+ { name: 'Month', value: 'month' },
206
+ { name: 'Year', value: 'year' },
207
+ ];
@@ -0,0 +1,28 @@
1
+ import type { IExecuteFunctions, IWebhookFunctions, IHookFunctions, IHttpRequestMethods, IDataObject } from 'n8n-workflow';
2
+ /**
3
+ * Make an authenticated request to the Lemon Squeezy API with retry logic
4
+ */
5
+ export declare function lemonSqueezyApiRequest(this: IExecuteFunctions | IWebhookFunctions | IHookFunctions, method: IHttpRequestMethods, endpoint: string, body?: IDataObject, qs?: Record<string, string | number>): Promise<IDataObject>;
6
+ /**
7
+ * Make paginated requests to fetch all items
8
+ */
9
+ export declare function lemonSqueezyApiRequestAllItems(this: IExecuteFunctions, method: IHttpRequestMethods, endpoint: string, qs?: Record<string, string | number>): Promise<IDataObject[]>;
10
+ /**
11
+ * Validate required fields before making API request
12
+ */
13
+ export declare function validateRequiredFields(fields: Record<string, unknown>, requiredFields: string[]): void;
14
+ /**
15
+ * Build filter query string parameters
16
+ */
17
+ export declare function buildFilterParams(filters: IDataObject): Record<string, string | number>;
18
+ /**
19
+ * Build JSON:API request body
20
+ */
21
+ export declare function buildJsonApiBody(type: string, attributes: IDataObject, relationships?: Record<string, {
22
+ type: string;
23
+ id: string;
24
+ }>, id?: string): IDataObject;
25
+ /**
26
+ * Parse webhook signature for validation
27
+ */
28
+ export declare function verifyWebhookSignature(payload: string, signature: string, secret: string): boolean;