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
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jan Marc Coloma
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,216 @@
1
+ # n8n-nodes-lemonsqueezy
2
+
3
+ [![npm version](https://img.shields.io/npm/v/n8n-nodes-lemonsqueezy.svg)](https://www.npmjs.com/package/n8n-nodes-lemonsqueezy)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+
6
+ An [n8n](https://n8n.io/) community node for [Lemon Squeezy](https://lemonsqueezy.com) - a platform for selling digital products, subscriptions, and software licenses.
7
+
8
+ ## Features
9
+
10
+ - **Full CRUD Operations** - Create, read, update, and delete operations for all major resources
11
+ - **Webhook Trigger** - Real-time event notifications for orders, subscriptions, and license keys
12
+ - **License Key Management** - Validate, activate, and deactivate license keys
13
+ - **Checkout Links** - Create dynamic checkout URLs with custom options
14
+ - **Rate Limiting** - Built-in retry logic with exponential backoff
15
+ - **Type Safety** - Full TypeScript support with comprehensive type definitions
16
+
17
+ ## Installation
18
+
19
+ ### Community Nodes (Recommended)
20
+
21
+ 1. Go to **Settings** > **Community Nodes** in your n8n instance
22
+ 2. Select **Install**
23
+ 3. Enter `n8n-nodes-lemonsqueezy`
24
+ 4. Click **Install**
25
+
26
+ ### npm
27
+
28
+ ```bash
29
+ npm install n8n-nodes-lemonsqueezy
30
+ ```
31
+
32
+ ## Credentials
33
+
34
+ To use this node, you need a Lemon Squeezy API key:
35
+
36
+ 1. Log in to your [Lemon Squeezy Dashboard](https://app.lemonsqueezy.com)
37
+ 2. Go to **Settings** → **API**
38
+ 3. Click **Create API Key**
39
+ 4. Copy the generated key and use it in n8n
40
+
41
+ ## Nodes
42
+
43
+ ### Lemon Squeezy
44
+
45
+ The main node for interacting with the Lemon Squeezy API.
46
+
47
+ #### Resources & Operations
48
+
49
+ | Resource | Operations |
50
+ |----------|------------|
51
+ | **Checkout** | Create, Get, Get Many |
52
+ | **Customer** | Create, Update, Delete, Get, Get Many |
53
+ | **Discount** | Create, Delete, Get, Get Many |
54
+ | **License Key** | Get, Get Many, Update, Validate, Activate, Deactivate |
55
+ | **Order** | Get, Get Many, Refund |
56
+ | **Product** | Get, Get Many |
57
+ | **Store** | Get, Get Many |
58
+ | **Subscription** | Get, Get Many, Update, Cancel, Resume |
59
+ | **Variant** | Get, Get Many |
60
+ | **Webhook** | Create, Update, Delete, Get, Get Many |
61
+
62
+ ### Lemon Squeezy Trigger
63
+
64
+ Webhook trigger node for receiving real-time events.
65
+
66
+ #### Supported Events
67
+
68
+ - `order_created` - New order placed
69
+ - `order_refunded` - Order refunded
70
+ - `subscription_created` - New subscription started
71
+ - `subscription_updated` - Subscription modified
72
+ - `subscription_cancelled` - Subscription cancelled
73
+ - `subscription_resumed` - Paused subscription resumed
74
+ - `subscription_paused` - Subscription paused
75
+ - `subscription_expired` - Subscription expired
76
+ - `subscription_payment_success` - Subscription payment succeeded
77
+ - `subscription_payment_failed` - Subscription payment failed
78
+ - `subscription_payment_recovered` - Failed payment recovered
79
+ - `subscription_payment_refunded` - Subscription payment refunded
80
+ - `license_key_created` - License key generated
81
+ - `license_key_updated` - License key modified
82
+
83
+ ## Example Workflows
84
+
85
+ ### 1. New Order Notification to Slack
86
+
87
+ ```
88
+ Lemon Squeezy Trigger (order_created) → Slack (Send Message)
89
+ ```
90
+
91
+ Notify your team instantly when a new order comes in.
92
+
93
+ ### 2. Subscription Churn Prevention
94
+
95
+ ```
96
+ Schedule Trigger → Lemon Squeezy (Get Subscriptions, status=past_due) → Send Email
97
+ ```
98
+
99
+ Automatically reach out to customers with failed payments.
100
+
101
+ ### 3. License Key Validation API
102
+
103
+ ```
104
+ Webhook → Lemon Squeezy (Validate License Key) → Respond to Webhook
105
+ ```
106
+
107
+ Build a license validation endpoint for your software.
108
+
109
+ ### 4. Dynamic Checkout Link Generation
110
+
111
+ ```
112
+ HTTP Request → Lemon Squeezy (Create Checkout) → Return Checkout URL
113
+ ```
114
+
115
+ Create personalized checkout links with pre-filled customer data.
116
+
117
+ ### 5. Customer Sync to CRM
118
+
119
+ ```
120
+ Lemon Squeezy Trigger (order_created) → Lemon Squeezy (Get Customer) → HubSpot (Create Contact)
121
+ ```
122
+
123
+ Automatically sync new customers to your CRM.
124
+
125
+ ## Filtering
126
+
127
+ Most "Get Many" operations support filtering:
128
+
129
+ | Filter | Description | Available On |
130
+ |--------|-------------|--------------|
131
+ | `storeId` | Filter by store | All resources |
132
+ | `status` | Filter by status | Orders, Subscriptions, Customers, License Keys |
133
+ | `email` | Filter by email | Orders, Customers |
134
+ | `productId` | Filter by product | Subscriptions, License Keys, Variants |
135
+ | `variantId` | Filter by variant | Subscriptions, Checkouts |
136
+ | `orderId` | Filter by order | Subscriptions, License Keys |
137
+
138
+ ## Error Handling
139
+
140
+ The node includes built-in error handling:
141
+
142
+ - **Rate Limiting**: Automatically waits and retries when rate limited (429 errors)
143
+ - **Retry Logic**: Retries failed requests with exponential backoff for 5xx errors
144
+ - **Continue on Fail**: Enable to process remaining items even if some fail
145
+
146
+ ## Troubleshooting
147
+
148
+ ### "Invalid API Key" Error
149
+
150
+ 1. Verify your API key is correct in the credentials
151
+ 2. Check if the API key has been revoked in Lemon Squeezy
152
+ 3. Ensure the key has appropriate permissions
153
+
154
+ ### "Resource Not Found" (404) Error
155
+
156
+ 1. Verify the resource ID is correct
157
+ 2. Check if the resource exists in Lemon Squeezy
158
+ 3. Ensure you're using the correct resource type
159
+
160
+ ### Webhook Not Receiving Events
161
+
162
+ 1. Verify the webhook URL is publicly accessible
163
+ 2. Check if your n8n instance has HTTPS enabled
164
+ 3. Verify the webhook secret matches
165
+ 4. Check the webhook events are enabled in Lemon Squeezy
166
+
167
+ ### Rate Limiting Issues
168
+
169
+ The node handles rate limiting automatically, but if you're hitting limits frequently:
170
+
171
+ 1. Reduce the frequency of API calls
172
+ 2. Use "Return All" sparingly for large datasets
173
+ 3. Consider caching responses where appropriate
174
+
175
+ ## Development
176
+
177
+ ```bash
178
+ # Install dependencies
179
+ npm install
180
+
181
+ # Build the node
182
+ npm run build
183
+
184
+ # Run tests
185
+ npm test
186
+
187
+ # Run linter
188
+ npm run lint
189
+
190
+ # Format code
191
+ npm run format
192
+ ```
193
+
194
+ ## Contributing
195
+
196
+ Contributions are welcome! Please feel free to submit a Pull Request.
197
+
198
+ 1. Fork the repository
199
+ 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
200
+ 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
201
+ 4. Push to the branch (`git push origin feature/amazing-feature`)
202
+ 5. Open a Pull Request
203
+
204
+ ## Resources
205
+
206
+ - [Lemon Squeezy API Documentation](https://docs.lemonsqueezy.com/api)
207
+ - [n8n Community Nodes Documentation](https://docs.n8n.io/integrations/community-nodes/)
208
+ - [n8n Community Forum](https://community.n8n.io/)
209
+
210
+ ## License
211
+
212
+ [MIT](LICENSE)
213
+
214
+ ---
215
+
216
+ Made with 🍋 by [Jan Marc Coloma](https://github.com/janmaaarc)
@@ -0,0 +1,10 @@
1
+ import type { IAuthenticateGeneric, ICredentialTestRequest, ICredentialType, INodeProperties } from 'n8n-workflow';
2
+ export declare class LemonSqueezyApi implements ICredentialType {
3
+ name: string;
4
+ displayName: string;
5
+ icon: "file:lemonSqueezy.svg";
6
+ documentationUrl: string;
7
+ properties: INodeProperties[];
8
+ authenticate: IAuthenticateGeneric;
9
+ test: ICredentialTestRequest;
10
+ }
@@ -0,0 +1,41 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LemonSqueezyApi = void 0;
4
+ class LemonSqueezyApi {
5
+ constructor() {
6
+ this.name = 'lemonSqueezyApi';
7
+ this.displayName = 'Lemon Squeezy API';
8
+ this.icon = 'file:lemonSqueezy.svg';
9
+ this.documentationUrl = 'https://docs.lemonsqueezy.com/api';
10
+ this.properties = [
11
+ {
12
+ displayName: 'API Key',
13
+ name: 'apiKey',
14
+ type: 'string',
15
+ typeOptions: { password: true },
16
+ default: '',
17
+ required: true,
18
+ placeholder: 'lsq_xxxxxxxxxxxxxxxxxxxxxxxx',
19
+ hint: 'Found in Settings → API in your dashboard',
20
+ description: 'Your Lemon Squeezy API key. <a href="https://app.lemonsqueezy.com/settings/api" target="_blank">Generate one here</a>.',
21
+ },
22
+ ];
23
+ this.authenticate = {
24
+ type: 'generic',
25
+ properties: {
26
+ headers: {
27
+ Authorization: '=Bearer {{$credentials.apiKey}}',
28
+ Accept: 'application/vnd.api+json',
29
+ 'Content-Type': 'application/vnd.api+json',
30
+ },
31
+ },
32
+ };
33
+ this.test = {
34
+ request: {
35
+ baseURL: 'https://api.lemonsqueezy.com/v1',
36
+ url: '/users/me',
37
+ },
38
+ };
39
+ }
40
+ }
41
+ exports.LemonSqueezyApi = LemonSqueezyApi;
@@ -0,0 +1,5 @@
1
+ import type { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
2
+ export declare class LemonSqueezy implements INodeType {
3
+ description: INodeTypeDescription;
4
+ execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
5
+ }
@@ -0,0 +1,358 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LemonSqueezy = void 0;
4
+ const constants_1 = require("./constants");
5
+ const helpers_1 = require("./helpers");
6
+ const resources_1 = require("./resources");
7
+ async function handleCreate(ctx, resource, itemIndex) {
8
+ if (resource === 'customer') {
9
+ const storeId = ctx.getNodeParameter('customerStoreId', itemIndex);
10
+ const name = ctx.getNodeParameter('customerName', itemIndex);
11
+ const email = ctx.getNodeParameter('customerEmail', itemIndex);
12
+ const additionalFields = ctx.getNodeParameter('additionalFields', itemIndex);
13
+ const body = (0, helpers_1.buildJsonApiBody)('customers', { name, email, ...additionalFields }, { store: { type: 'stores', id: storeId } });
14
+ return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'POST', '/customers', body);
15
+ }
16
+ if (resource === 'discount') {
17
+ const storeId = ctx.getNodeParameter('discountStoreId', itemIndex);
18
+ const name = ctx.getNodeParameter('discountName', itemIndex);
19
+ const code = ctx.getNodeParameter('discountCode', itemIndex);
20
+ const amount = ctx.getNodeParameter('discountAmount', itemIndex);
21
+ const amountType = ctx.getNodeParameter('discountAmountType', itemIndex);
22
+ const additionalOptions = ctx.getNodeParameter('additionalOptions', itemIndex, {});
23
+ const attributes = {
24
+ name,
25
+ code,
26
+ amount,
27
+ amount_type: amountType,
28
+ };
29
+ if (additionalOptions.duration) {
30
+ attributes.duration = additionalOptions.duration;
31
+ }
32
+ if (additionalOptions.durationInMonths) {
33
+ attributes.duration_in_months = additionalOptions.durationInMonths;
34
+ }
35
+ if (additionalOptions.maxRedemptions) {
36
+ attributes.max_redemptions = additionalOptions.maxRedemptions;
37
+ attributes.is_limited_redemptions = true;
38
+ }
39
+ if (additionalOptions.startsAt) {
40
+ attributes.starts_at = additionalOptions.startsAt;
41
+ }
42
+ if (additionalOptions.expiresAt) {
43
+ attributes.expires_at = additionalOptions.expiresAt;
44
+ }
45
+ if (additionalOptions.testMode !== undefined) {
46
+ attributes.test_mode = additionalOptions.testMode;
47
+ }
48
+ const body = (0, helpers_1.buildJsonApiBody)('discounts', attributes, {
49
+ store: { type: 'stores', id: storeId },
50
+ });
51
+ return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'POST', '/discounts', body);
52
+ }
53
+ if (resource === 'checkout') {
54
+ const storeId = ctx.getNodeParameter('checkoutStoreId', itemIndex);
55
+ const variantId = ctx.getNodeParameter('checkoutVariantId', itemIndex);
56
+ const additionalOptions = ctx.getNodeParameter('additionalOptions', itemIndex, {});
57
+ const checkoutOptions = ctx.getNodeParameter('checkoutOptions', itemIndex, {});
58
+ const attributes = {};
59
+ const checkoutData = {};
60
+ const productOptions = {};
61
+ const checkoutOptionsObj = {};
62
+ if (additionalOptions.customPrice) {
63
+ attributes.custom_price = additionalOptions.customPrice;
64
+ }
65
+ if (additionalOptions.email) {
66
+ checkoutData.email = additionalOptions.email;
67
+ }
68
+ if (additionalOptions.name) {
69
+ checkoutData.name = additionalOptions.name;
70
+ }
71
+ if (additionalOptions.discountCode) {
72
+ checkoutData.discount_code = additionalOptions.discountCode;
73
+ }
74
+ if (additionalOptions.redirectUrl) {
75
+ productOptions.redirect_url = additionalOptions.redirectUrl;
76
+ }
77
+ if (additionalOptions.receiptButtonText) {
78
+ productOptions.receipt_button_text = additionalOptions.receiptButtonText;
79
+ }
80
+ if (additionalOptions.receiptLinkUrl) {
81
+ productOptions.receipt_link_url = additionalOptions.receiptLinkUrl;
82
+ }
83
+ if (additionalOptions.receiptThankYouNote) {
84
+ productOptions.receipt_thank_you_note = additionalOptions.receiptThankYouNote;
85
+ }
86
+ if (additionalOptions.customData) {
87
+ try {
88
+ checkoutData.custom = JSON.parse(additionalOptions.customData);
89
+ }
90
+ catch {
91
+ checkoutData.custom = additionalOptions.customData;
92
+ }
93
+ }
94
+ if (additionalOptions.expiresAt) {
95
+ attributes.expires_at = additionalOptions.expiresAt;
96
+ }
97
+ if (additionalOptions.testMode !== undefined) {
98
+ attributes.test_mode = additionalOptions.testMode;
99
+ }
100
+ if (checkoutOptions.dark !== undefined) {
101
+ checkoutOptionsObj.dark = checkoutOptions.dark;
102
+ }
103
+ if (checkoutOptions.embed !== undefined) {
104
+ checkoutOptionsObj.embed = checkoutOptions.embed;
105
+ }
106
+ if (checkoutOptions.logo !== undefined) {
107
+ checkoutOptionsObj.logo = checkoutOptions.logo;
108
+ }
109
+ if (checkoutOptions.desc !== undefined) {
110
+ checkoutOptionsObj.desc = checkoutOptions.desc;
111
+ }
112
+ if (checkoutOptions.media !== undefined) {
113
+ checkoutOptionsObj.media = checkoutOptions.media;
114
+ }
115
+ if (checkoutOptions.discount !== undefined) {
116
+ checkoutOptionsObj.discount = checkoutOptions.discount;
117
+ }
118
+ if (checkoutOptions.buttonColor) {
119
+ checkoutOptionsObj.button_color = checkoutOptions.buttonColor;
120
+ }
121
+ if (Object.keys(checkoutData).length > 0) {
122
+ attributes.checkout_data = checkoutData;
123
+ }
124
+ if (Object.keys(productOptions).length > 0) {
125
+ attributes.product_options = productOptions;
126
+ }
127
+ if (Object.keys(checkoutOptionsObj).length > 0) {
128
+ attributes.checkout_options = checkoutOptionsObj;
129
+ }
130
+ const body = (0, helpers_1.buildJsonApiBody)('checkouts', attributes, {
131
+ store: { type: 'stores', id: storeId },
132
+ variant: { type: 'variants', id: variantId },
133
+ });
134
+ return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'POST', '/checkouts', body);
135
+ }
136
+ if (resource === 'webhook') {
137
+ const storeId = ctx.getNodeParameter('webhookStoreId', itemIndex);
138
+ const url = ctx.getNodeParameter('webhookUrl', itemIndex);
139
+ const events = ctx.getNodeParameter('webhookEvents', itemIndex);
140
+ const secret = ctx.getNodeParameter('webhookSecret', itemIndex);
141
+ const additionalOptions = ctx.getNodeParameter('additionalOptions', itemIndex, {});
142
+ const attributes = { url, events, secret };
143
+ if (additionalOptions.testMode !== undefined) {
144
+ attributes.test_mode = additionalOptions.testMode;
145
+ }
146
+ const body = (0, helpers_1.buildJsonApiBody)('webhooks', attributes, {
147
+ store: { type: 'stores', id: storeId },
148
+ });
149
+ return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'POST', '/webhooks', body);
150
+ }
151
+ throw new Error(`Create operation not supported for resource: ${resource}`);
152
+ }
153
+ async function handleUpdate(ctx, resource, itemIndex) {
154
+ if (resource === 'subscription') {
155
+ const subscriptionId = ctx.getNodeParameter('subscriptionId', itemIndex);
156
+ const updateFields = ctx.getNodeParameter('updateFields', itemIndex);
157
+ const attributes = {};
158
+ if (updateFields.variantId) {
159
+ attributes.variant_id = updateFields.variantId;
160
+ }
161
+ if (updateFields.pause !== undefined && updateFields.pause !== '') {
162
+ attributes.pause = { mode: updateFields.pause };
163
+ }
164
+ else if (updateFields.pause === '') {
165
+ attributes.pause = null;
166
+ }
167
+ if (updateFields.cancelled !== undefined) {
168
+ attributes.cancelled = updateFields.cancelled;
169
+ }
170
+ if (updateFields.billingAnchor) {
171
+ attributes.billing_anchor = updateFields.billingAnchor;
172
+ }
173
+ if (updateFields.invoiceImmediately !== undefined) {
174
+ attributes.invoice_immediately = updateFields.invoiceImmediately;
175
+ }
176
+ if (updateFields.disableProrations !== undefined) {
177
+ attributes.disable_prorations = updateFields.disableProrations;
178
+ }
179
+ const body = (0, helpers_1.buildJsonApiBody)('subscriptions', attributes, undefined, subscriptionId);
180
+ return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'PATCH', `/subscriptions/${subscriptionId}`, body);
181
+ }
182
+ if (resource === 'customer') {
183
+ const customerId = ctx.getNodeParameter('customerId', itemIndex);
184
+ const updateFields = ctx.getNodeParameter('updateFields', itemIndex);
185
+ const attributes = {};
186
+ if (updateFields.name) {
187
+ attributes.name = updateFields.name;
188
+ }
189
+ if (updateFields.email) {
190
+ attributes.email = updateFields.email;
191
+ }
192
+ if (updateFields.city) {
193
+ attributes.city = updateFields.city;
194
+ }
195
+ if (updateFields.country) {
196
+ attributes.country = updateFields.country;
197
+ }
198
+ if (updateFields.region) {
199
+ attributes.region = updateFields.region;
200
+ }
201
+ if (updateFields.status) {
202
+ attributes.status = updateFields.status;
203
+ }
204
+ const body = (0, helpers_1.buildJsonApiBody)('customers', attributes, undefined, customerId);
205
+ return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'PATCH', `/customers/${customerId}`, body);
206
+ }
207
+ if (resource === 'licenseKey') {
208
+ const licenseKeyId = ctx.getNodeParameter('licenseKeyId', itemIndex);
209
+ const updateFields = ctx.getNodeParameter('updateFields', itemIndex);
210
+ const attributes = {};
211
+ if (updateFields.activationLimit !== undefined) {
212
+ attributes.activation_limit = updateFields.activationLimit;
213
+ }
214
+ if (updateFields.disabled !== undefined) {
215
+ attributes.disabled = updateFields.disabled;
216
+ }
217
+ if (updateFields.expiresAt) {
218
+ attributes.expires_at = updateFields.expiresAt;
219
+ }
220
+ const body = (0, helpers_1.buildJsonApiBody)('license-keys', attributes, undefined, licenseKeyId);
221
+ return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'PATCH', `/license-keys/${licenseKeyId}`, body);
222
+ }
223
+ if (resource === 'webhook') {
224
+ const webhookId = ctx.getNodeParameter('webhookId', itemIndex);
225
+ const updateFields = ctx.getNodeParameter('updateFields', itemIndex);
226
+ const attributes = {};
227
+ if (updateFields.url) {
228
+ attributes.url = updateFields.url;
229
+ }
230
+ if (updateFields.events) {
231
+ attributes.events = updateFields.events;
232
+ }
233
+ if (updateFields.secret) {
234
+ attributes.secret = updateFields.secret;
235
+ }
236
+ const body = (0, helpers_1.buildJsonApiBody)('webhooks', attributes, undefined, webhookId);
237
+ return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'PATCH', `/webhooks/${webhookId}`, body);
238
+ }
239
+ throw new Error(`Update operation not supported for resource: ${resource}`);
240
+ }
241
+ class LemonSqueezy {
242
+ constructor() {
243
+ this.description = {
244
+ displayName: 'Lemon Squeezy',
245
+ name: 'lemonSqueezy',
246
+ icon: 'file:lemonSqueezy.svg',
247
+ group: ['transform'],
248
+ version: 1,
249
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
250
+ description: 'Interact with Lemon Squeezy API',
251
+ defaults: {
252
+ name: 'Lemon Squeezy',
253
+ },
254
+ inputs: ['main'],
255
+ outputs: ['main'],
256
+ credentials: [
257
+ {
258
+ name: 'lemonSqueezyApi',
259
+ required: true,
260
+ },
261
+ ],
262
+ properties: [resources_1.resourceProperty, ...resources_1.allOperations, ...resources_1.allFields],
263
+ };
264
+ }
265
+ async execute() {
266
+ const items = this.getInputData();
267
+ const returnData = [];
268
+ const resource = this.getNodeParameter('resource', 0);
269
+ const operation = this.getNodeParameter('operation', 0);
270
+ for (let i = 0; i < items.length; i++) {
271
+ try {
272
+ let responseData;
273
+ const endpoint = constants_1.RESOURCE_ENDPOINTS[resource];
274
+ if (operation === 'get') {
275
+ const idParam = constants_1.RESOURCE_ID_PARAMS[resource];
276
+ const id = this.getNodeParameter(idParam, i);
277
+ responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'GET', `/${endpoint}/${id}`);
278
+ }
279
+ else if (operation === 'getAll') {
280
+ const returnAll = this.getNodeParameter('returnAll', i);
281
+ const filters = this.getNodeParameter('filters', i, {});
282
+ const qs = (0, helpers_1.buildFilterParams)(filters);
283
+ if (returnAll) {
284
+ responseData = await helpers_1.lemonSqueezyApiRequestAllItems.call(this, 'GET', `/${endpoint}`, qs);
285
+ }
286
+ else {
287
+ const limit = this.getNodeParameter('limit', i);
288
+ qs['page[size]'] = limit;
289
+ const response = await helpers_1.lemonSqueezyApiRequest.call(this, 'GET', `/${endpoint}`, undefined, qs);
290
+ responseData = response.data;
291
+ }
292
+ }
293
+ else if (operation === 'create') {
294
+ responseData = await handleCreate(this, resource, i);
295
+ }
296
+ else if (operation === 'update') {
297
+ responseData = await handleUpdate(this, resource, i);
298
+ }
299
+ else if (operation === 'delete') {
300
+ const idParam = constants_1.RESOURCE_ID_PARAMS[resource];
301
+ const id = this.getNodeParameter(idParam, i);
302
+ responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'DELETE', `/${endpoint}/${id}`);
303
+ }
304
+ else if (operation === 'cancel' && resource === 'subscription') {
305
+ const subscriptionId = this.getNodeParameter('subscriptionId', i);
306
+ responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'DELETE', `/subscriptions/${subscriptionId}`);
307
+ }
308
+ else if (operation === 'resume' && resource === 'subscription') {
309
+ const subscriptionId = this.getNodeParameter('subscriptionId', i);
310
+ const body = (0, helpers_1.buildJsonApiBody)('subscriptions', { pause: null }, undefined, subscriptionId);
311
+ responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'PATCH', `/subscriptions/${subscriptionId}`, body);
312
+ }
313
+ else if (operation === 'refund' && resource === 'order') {
314
+ const orderId = this.getNodeParameter('orderId', i);
315
+ responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'POST', `/orders/${orderId}/refund`);
316
+ }
317
+ else if (resource === 'licenseKey') {
318
+ if (operation === 'validate') {
319
+ const licenseKey = this.getNodeParameter('licenseKey', i);
320
+ responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'POST', '/licenses/validate', {
321
+ license_key: licenseKey,
322
+ });
323
+ }
324
+ else if (operation === 'activate') {
325
+ const licenseKey = this.getNodeParameter('licenseKey', i);
326
+ const instanceName = this.getNodeParameter('instanceName', i);
327
+ responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'POST', '/licenses/activate', {
328
+ license_key: licenseKey,
329
+ instance_name: instanceName,
330
+ });
331
+ }
332
+ else if (operation === 'deactivate') {
333
+ const licenseKey = this.getNodeParameter('licenseKey', i);
334
+ const instanceId = this.getNodeParameter('instanceId', i);
335
+ responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'POST', '/licenses/deactivate', {
336
+ license_key: licenseKey,
337
+ instance_id: instanceId,
338
+ });
339
+ }
340
+ }
341
+ const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
342
+ returnData.push(...executionData);
343
+ }
344
+ catch (error) {
345
+ if (this.continueOnFail()) {
346
+ returnData.push({
347
+ json: { error: error.message },
348
+ pairedItem: { item: i },
349
+ });
350
+ continue;
351
+ }
352
+ throw error;
353
+ }
354
+ }
355
+ return [returnData];
356
+ }
357
+ }
358
+ exports.LemonSqueezy = LemonSqueezy;
@@ -0,0 +1,12 @@
1
+ import type { IWebhookFunctions, IHookFunctions, INodeType, INodeTypeDescription, IWebhookResponseData } from 'n8n-workflow';
2
+ export declare class LemonSqueezyTrigger implements INodeType {
3
+ description: INodeTypeDescription;
4
+ webhookMethods: {
5
+ default: {
6
+ checkExists(this: IHookFunctions): Promise<boolean>;
7
+ create(this: IHookFunctions): Promise<boolean>;
8
+ delete(this: IHookFunctions): Promise<boolean>;
9
+ };
10
+ };
11
+ webhook(this: IWebhookFunctions): Promise<IWebhookResponseData>;
12
+ }