n8n-nodes-lemonsqueezy 0.7.2 → 0.9.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.
- package/README.md +31 -2
- package/dist/nodes/LemonSqueezy/LemonSqueezy.node.js +65 -4
- package/dist/nodes/LemonSqueezy/constants.js +2 -0
- package/dist/nodes/LemonSqueezy/helpers.d.ts +51 -2
- package/dist/nodes/LemonSqueezy/helpers.js +96 -1
- package/dist/nodes/LemonSqueezy/resources/customer.d.ts +14 -0
- package/dist/nodes/LemonSqueezy/resources/customer.js +6 -4
- package/dist/nodes/LemonSqueezy/resources/discount.d.ts +14 -0
- package/dist/nodes/LemonSqueezy/resources/discount.js +93 -6
- package/dist/nodes/LemonSqueezy/resources/file.d.ts +3 -0
- package/dist/nodes/LemonSqueezy/resources/file.js +86 -0
- package/dist/nodes/LemonSqueezy/resources/index.d.ts +2 -1
- package/dist/nodes/LemonSqueezy/resources/index.js +7 -1
- package/dist/nodes/LemonSqueezy/resources/order.d.ts +13 -0
- package/dist/nodes/LemonSqueezy/resources/order.js +2 -2
- package/dist/nodes/LemonSqueezy/resources/product.d.ts +13 -0
- package/dist/nodes/LemonSqueezy/resources/product.js +2 -2
- package/dist/nodes/LemonSqueezy/resources/subscriptionInvoice.d.ts +13 -0
- package/dist/nodes/LemonSqueezy/resources/subscriptionInvoice.js +48 -3
- package/dist/nodes/LemonSqueezy/resources/webhook.d.ts +19 -0
- package/dist/nodes/LemonSqueezy/types.d.ts +18 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -55,8 +55,9 @@ The main node for interacting with the Lemon Squeezy API.
|
|
|
55
55
|
|----------|------------|
|
|
56
56
|
| **Checkout** | Create, Get, Get Many |
|
|
57
57
|
| **Customer** | Create, Update, Delete, Get, Get Many |
|
|
58
|
-
| **Discount** | Create, Delete, Get, Get Many |
|
|
58
|
+
| **Discount** | Create, Update, Delete, Get, Get Many |
|
|
59
59
|
| **Discount Redemption** | Get, Get Many |
|
|
60
|
+
| **File** | Get, Get Many |
|
|
60
61
|
| **License Key** | Get, Get Many, Update, Validate, Activate, Deactivate |
|
|
61
62
|
| **License Key Instance** | Get, Get Many |
|
|
62
63
|
| **Order** | Get, Get Many, Refund |
|
|
@@ -64,7 +65,7 @@ The main node for interacting with the Lemon Squeezy API.
|
|
|
64
65
|
| **Product** | Get, Get Many |
|
|
65
66
|
| **Store** | Get, Get Many |
|
|
66
67
|
| **Subscription** | Get, Get Many, Update, Cancel, Resume |
|
|
67
|
-
| **Subscription Invoice** | Get, Get Many |
|
|
68
|
+
| **Subscription Invoice** | Get, Get Many, Generate, Refund |
|
|
68
69
|
| **Usage Record** | Create, Get, Get Many |
|
|
69
70
|
| **User** | Get Current |
|
|
70
71
|
| **Variant** | Get, Get Many |
|
|
@@ -303,6 +304,34 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
303
304
|
|
|
304
305
|
## Changelog
|
|
305
306
|
|
|
307
|
+
### v0.9.0
|
|
308
|
+
|
|
309
|
+
**New Features:**
|
|
310
|
+
- Added **Subscription Invoice Generate operation** for generating invoices on subscriptions with outstanding balances
|
|
311
|
+
- Added **Subscription Invoice Refund operation** for issuing full or partial refunds
|
|
312
|
+
- Added custom data payload size validation (max 10KB) for checkout creation
|
|
313
|
+
- Added Retry-After header extraction for smarter rate limit handling
|
|
314
|
+
- Added resource validation guard to catch unknown resources early
|
|
315
|
+
|
|
316
|
+
**Improvements:**
|
|
317
|
+
- Added JSDoc documentation to all resource files
|
|
318
|
+
- Improved field descriptions with units (cents), ranges (0-100), and examples
|
|
319
|
+
- Added maxValue (100) to all limit fields for API compliance
|
|
320
|
+
- 189 tests with comprehensive coverage
|
|
321
|
+
|
|
322
|
+
### v0.8.0
|
|
323
|
+
|
|
324
|
+
**New Features:**
|
|
325
|
+
- Added **File resource** for accessing product files (digital downloads)
|
|
326
|
+
- Added **Discount Update operation** for modifying existing discount codes
|
|
327
|
+
- Added discount amount validation (0-100 for percent, positive integer for fixed)
|
|
328
|
+
- Webhook URLs now require HTTPS (Lemon Squeezy requirement)
|
|
329
|
+
|
|
330
|
+
**Improvements:**
|
|
331
|
+
- Enhanced field descriptions with examples and placeholders
|
|
332
|
+
- Added API limit hints (max 100 per page) to limit fields
|
|
333
|
+
- 178 tests with comprehensive coverage
|
|
334
|
+
|
|
306
335
|
### v0.7.2
|
|
307
336
|
|
|
308
337
|
**n8n Community Package Compliance:**
|
|
@@ -22,6 +22,8 @@ async function handleCreate(ctx, resource, itemIndex) {
|
|
|
22
22
|
const amount = ctx.getNodeParameter('discountAmount', itemIndex);
|
|
23
23
|
const amountType = ctx.getNodeParameter('discountAmountType', itemIndex);
|
|
24
24
|
const additionalOptions = ctx.getNodeParameter('additionalOptions', itemIndex, {});
|
|
25
|
+
// Validate discount amount based on type
|
|
26
|
+
(0, helpers_1.validateDiscountAmount)(amount, amountType);
|
|
25
27
|
const attributes = {
|
|
26
28
|
name,
|
|
27
29
|
code,
|
|
@@ -93,6 +95,8 @@ async function handleCreate(ctx, resource, itemIndex) {
|
|
|
93
95
|
}
|
|
94
96
|
if (additionalOptions.customData !== undefined && additionalOptions.customData !== null) {
|
|
95
97
|
const customDataValue = additionalOptions.customData;
|
|
98
|
+
// Validate payload size before processing (max 10KB)
|
|
99
|
+
(0, helpers_1.validateCustomDataSize)(customDataValue);
|
|
96
100
|
if (typeof customDataValue === 'string') {
|
|
97
101
|
try {
|
|
98
102
|
const parsed = JSON.parse(customDataValue);
|
|
@@ -169,8 +173,8 @@ async function handleCreate(ctx, resource, itemIndex) {
|
|
|
169
173
|
const events = ctx.getNodeParameter('webhookEvents', itemIndex);
|
|
170
174
|
const secret = ctx.getNodeParameter('webhookSecret', itemIndex);
|
|
171
175
|
const additionalOptions = ctx.getNodeParameter('additionalOptions', itemIndex, {});
|
|
172
|
-
// Validate URL before API call
|
|
173
|
-
(0, helpers_1.validateField)('url', url, '
|
|
176
|
+
// Validate URL before API call - Lemon Squeezy requires HTTPS for webhooks
|
|
177
|
+
(0, helpers_1.validateField)('url', url, 'httpsUrl');
|
|
174
178
|
// Validate webhook secret minimum length for security (32+ chars recommended)
|
|
175
179
|
if (secret.length < 32) {
|
|
176
180
|
throw new Error('Webhook secret must be at least 32 characters for security. Generate one using: openssl rand -hex 32');
|
|
@@ -263,8 +267,8 @@ async function handleUpdate(ctx, resource, itemIndex) {
|
|
|
263
267
|
const updateFields = ctx.getNodeParameter('updateFields', itemIndex);
|
|
264
268
|
const attributes = {};
|
|
265
269
|
if (updateFields.url) {
|
|
266
|
-
// Validate URL before API call
|
|
267
|
-
(0, helpers_1.validateField)('url', updateFields.url, '
|
|
270
|
+
// Validate URL before API call - Lemon Squeezy requires HTTPS for webhooks
|
|
271
|
+
(0, helpers_1.validateField)('url', updateFields.url, 'httpsUrl');
|
|
268
272
|
attributes.url = updateFields.url;
|
|
269
273
|
}
|
|
270
274
|
if (updateFields.events) {
|
|
@@ -280,6 +284,44 @@ async function handleUpdate(ctx, resource, itemIndex) {
|
|
|
280
284
|
const body = (0, helpers_1.buildJsonApiBody)('webhooks', attributes, undefined, webhookId);
|
|
281
285
|
return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'PATCH', `/webhooks/${webhookId}`, body);
|
|
282
286
|
}
|
|
287
|
+
if (resource === 'discount') {
|
|
288
|
+
const discountId = ctx.getNodeParameter('discountId', itemIndex);
|
|
289
|
+
const updateFields = ctx.getNodeParameter('updateFields', itemIndex);
|
|
290
|
+
const attributes = {};
|
|
291
|
+
if (updateFields.name) {
|
|
292
|
+
attributes.name = updateFields.name;
|
|
293
|
+
}
|
|
294
|
+
if (updateFields.code) {
|
|
295
|
+
attributes.code = updateFields.code;
|
|
296
|
+
}
|
|
297
|
+
if (updateFields.amount !== undefined) {
|
|
298
|
+
// Validate discount amount if both amount and type are being updated
|
|
299
|
+
const amountType = updateFields.amountType || 'percent';
|
|
300
|
+
(0, helpers_1.validateDiscountAmount)(updateFields.amount, amountType);
|
|
301
|
+
attributes.amount = updateFields.amount;
|
|
302
|
+
}
|
|
303
|
+
if (updateFields.amountType) {
|
|
304
|
+
attributes.amount_type = updateFields.amountType;
|
|
305
|
+
}
|
|
306
|
+
if (updateFields.duration) {
|
|
307
|
+
attributes.duration = updateFields.duration;
|
|
308
|
+
}
|
|
309
|
+
if (updateFields.durationInMonths !== undefined) {
|
|
310
|
+
attributes.duration_in_months = updateFields.durationInMonths;
|
|
311
|
+
}
|
|
312
|
+
if (updateFields.maxRedemptions !== undefined) {
|
|
313
|
+
attributes.max_redemptions = updateFields.maxRedemptions;
|
|
314
|
+
attributes.is_limited_redemptions = updateFields.maxRedemptions > 0;
|
|
315
|
+
}
|
|
316
|
+
if (updateFields.startsAt) {
|
|
317
|
+
attributes.starts_at = updateFields.startsAt;
|
|
318
|
+
}
|
|
319
|
+
if (updateFields.expiresAt) {
|
|
320
|
+
attributes.expires_at = updateFields.expiresAt;
|
|
321
|
+
}
|
|
322
|
+
const body = (0, helpers_1.buildJsonApiBody)('discounts', attributes, undefined, discountId);
|
|
323
|
+
return await helpers_1.lemonSqueezyApiRequest.call(ctx, 'PATCH', `/discounts/${discountId}`, body);
|
|
324
|
+
}
|
|
283
325
|
throw new Error(`Update operation not supported for resource: ${resource}`);
|
|
284
326
|
}
|
|
285
327
|
class LemonSqueezy {
|
|
@@ -316,6 +358,10 @@ class LemonSqueezy {
|
|
|
316
358
|
try {
|
|
317
359
|
let responseData;
|
|
318
360
|
const endpoint = constants_1.RESOURCE_ENDPOINTS[resource];
|
|
361
|
+
// Validate that the resource exists in our endpoints mapping
|
|
362
|
+
if (!endpoint && !['user'].includes(resource)) {
|
|
363
|
+
throw new Error(`Unknown resource: ${resource}`);
|
|
364
|
+
}
|
|
319
365
|
if (operation === 'get') {
|
|
320
366
|
const idParam = constants_1.RESOURCE_ID_PARAMS[resource];
|
|
321
367
|
const id = this.getNodeParameter(idParam, i);
|
|
@@ -404,6 +450,21 @@ class LemonSqueezy {
|
|
|
404
450
|
else if (resource === 'user' && operation === 'getCurrent') {
|
|
405
451
|
responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'GET', '/users/me');
|
|
406
452
|
}
|
|
453
|
+
else if (resource === 'subscriptionInvoice') {
|
|
454
|
+
if (operation === 'generate') {
|
|
455
|
+
const subscriptionId = this.getNodeParameter('generateSubscriptionId', i);
|
|
456
|
+
const body = (0, helpers_1.buildJsonApiBody)('subscription-invoices', {}, { subscription: { type: 'subscriptions', id: subscriptionId } });
|
|
457
|
+
responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'POST', '/subscription-invoices', body);
|
|
458
|
+
}
|
|
459
|
+
else if (operation === 'refund') {
|
|
460
|
+
const invoiceId = this.getNodeParameter('subscriptionInvoiceId', i);
|
|
461
|
+
const refundAmount = this.getNodeParameter('refundAmount', i, 0);
|
|
462
|
+
const body = refundAmount > 0
|
|
463
|
+
? { data: { type: 'subscription-invoices', attributes: { amount: refundAmount } } }
|
|
464
|
+
: {};
|
|
465
|
+
responseData = await helpers_1.lemonSqueezyApiRequest.call(this, 'POST', `/subscription-invoices/${invoiceId}/refund`, body);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
407
468
|
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
|
|
408
469
|
returnData.push(...executionData);
|
|
409
470
|
}
|
|
@@ -31,6 +31,7 @@ exports.RESOURCE_ENDPOINTS = {
|
|
|
31
31
|
webhook: 'webhooks',
|
|
32
32
|
usageRecord: 'usage-records',
|
|
33
33
|
user: 'users',
|
|
34
|
+
file: 'files',
|
|
34
35
|
};
|
|
35
36
|
/**
|
|
36
37
|
* Resource to ID parameter mapping
|
|
@@ -51,6 +52,7 @@ exports.RESOURCE_ID_PARAMS = {
|
|
|
51
52
|
checkout: 'checkoutId',
|
|
52
53
|
webhook: 'webhookId',
|
|
53
54
|
usageRecord: 'usageRecordId',
|
|
55
|
+
file: 'fileId',
|
|
54
56
|
};
|
|
55
57
|
/**
|
|
56
58
|
* Webhook event types with descriptions
|
|
@@ -41,6 +41,7 @@ export declare function isValidEmail(email: string): boolean;
|
|
|
41
41
|
* This prevents Server-Side Request Forgery (SSRF) attacks.
|
|
42
42
|
*
|
|
43
43
|
* @param url - The URL to validate
|
|
44
|
+
* @param requireHttps - If true, only HTTPS URLs are allowed (default: false)
|
|
44
45
|
* @returns True if the URL is valid and safe, false otherwise
|
|
45
46
|
*
|
|
46
47
|
* @example
|
|
@@ -48,8 +49,9 @@ export declare function isValidEmail(email: string): boolean;
|
|
|
48
49
|
* isValidUrl('http://localhost:3000') // false (internal)
|
|
49
50
|
* isValidUrl('ftp://files.example.com') // false (non-http protocol)
|
|
50
51
|
* isValidUrl('http://169.254.169.254') // false (AWS metadata)
|
|
52
|
+
* isValidUrl('http://example.com', true) // false (HTTPS required)
|
|
51
53
|
*/
|
|
52
|
-
export declare function isValidUrl(url: string): boolean;
|
|
54
|
+
export declare function isValidUrl(url: string, requireHttps?: boolean): boolean;
|
|
53
55
|
/**
|
|
54
56
|
* Validates ISO 8601 date format.
|
|
55
57
|
*
|
|
@@ -88,6 +90,7 @@ export declare function isPositiveInteger(value: unknown): boolean;
|
|
|
88
90
|
* - 'required': Ensures value is not empty/null/undefined
|
|
89
91
|
* - 'email': RFC 5322 compliant email validation
|
|
90
92
|
* - 'url': Safe URL validation with SSRF protection
|
|
93
|
+
* - 'httpsUrl': Safe URL validation requiring HTTPS (for webhooks)
|
|
91
94
|
* - 'date': ISO 8601 date format validation
|
|
92
95
|
* - 'positiveInteger': Positive integer validation
|
|
93
96
|
*
|
|
@@ -99,8 +102,9 @@ export declare function isPositiveInteger(value: unknown): boolean;
|
|
|
99
102
|
* @example
|
|
100
103
|
* validateField('email', 'user@example.com', 'email') // passes
|
|
101
104
|
* validateField('email', 'invalid', 'email') // throws "email must be a valid email address"
|
|
105
|
+
* validateField('webhookUrl', 'http://example.com', 'httpsUrl') // throws "webhookUrl must be a valid HTTPS URL"
|
|
102
106
|
*/
|
|
103
|
-
export declare function validateField(fieldName: string, value: unknown, validationType: 'email' | 'url' | 'date' | 'positiveInteger' | 'required'): void;
|
|
107
|
+
export declare function validateField(fieldName: string, value: unknown, validationType: 'email' | 'url' | 'httpsUrl' | 'date' | 'positiveInteger' | 'required'): void;
|
|
104
108
|
/**
|
|
105
109
|
* Safely parses a JSON string with descriptive error handling.
|
|
106
110
|
*
|
|
@@ -118,6 +122,51 @@ export declare function validateField(fieldName: string, value: unknown, validat
|
|
|
118
122
|
* // Throws: "config contains invalid JSON"
|
|
119
123
|
*/
|
|
120
124
|
export declare function safeJsonParse<T = unknown>(jsonString: string, fieldName: string): T;
|
|
125
|
+
/**
|
|
126
|
+
* Validates discount amount based on the amount type.
|
|
127
|
+
*
|
|
128
|
+
* - For 'percent' type: amount must be between 0 and 100 (inclusive)
|
|
129
|
+
* - For 'fixed' type: amount must be a positive integer (in cents)
|
|
130
|
+
*
|
|
131
|
+
* @param amount - The discount amount to validate
|
|
132
|
+
* @param amountType - The type of discount ('percent' or 'fixed')
|
|
133
|
+
* @throws Error if the amount is invalid for the given type
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* validateDiscountAmount(50, 'percent') // passes (50%)
|
|
137
|
+
* validateDiscountAmount(150, 'percent') // throws "Percent discount must be between 0 and 100"
|
|
138
|
+
* validateDiscountAmount(1000, 'fixed') // passes ($10.00 in cents)
|
|
139
|
+
* validateDiscountAmount(-100, 'fixed') // throws "Fixed discount amount must be a positive integer"
|
|
140
|
+
*/
|
|
141
|
+
export declare function validateDiscountAmount(amount: number, amountType: string): void;
|
|
142
|
+
/**
|
|
143
|
+
* Validates that a custom data payload doesn't exceed the maximum size.
|
|
144
|
+
*
|
|
145
|
+
* This prevents memory issues and potential abuse from extremely large payloads.
|
|
146
|
+
*
|
|
147
|
+
* @param data - The custom data object or string to validate
|
|
148
|
+
* @param maxSizeBytes - Maximum allowed size in bytes (default: 10KB)
|
|
149
|
+
* @throws Error if the payload exceeds the maximum size
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* validateCustomDataSize({ key: 'value' }) // passes
|
|
153
|
+
* validateCustomDataSize(veryLargeObject) // throws if > 10KB
|
|
154
|
+
*/
|
|
155
|
+
export declare function validateCustomDataSize(data: unknown, maxSizeBytes?: number): void;
|
|
156
|
+
/**
|
|
157
|
+
* Extracts the Retry-After header value from an error response.
|
|
158
|
+
*
|
|
159
|
+
* The Retry-After header indicates how long to wait before retrying
|
|
160
|
+
* a rate-limited or temporarily unavailable request.
|
|
161
|
+
*
|
|
162
|
+
* @param error - The error object that may contain Retry-After header
|
|
163
|
+
* @returns Number of seconds to wait, or undefined if not present
|
|
164
|
+
*
|
|
165
|
+
* @example
|
|
166
|
+
* getRetryAfterSeconds({ response: { headers: { 'retry-after': '60' } } }) // 60
|
|
167
|
+
* getRetryAfterSeconds({ response: { headers: {} } }) // undefined
|
|
168
|
+
*/
|
|
169
|
+
export declare function getRetryAfterSeconds(error: unknown): number | undefined;
|
|
121
170
|
/**
|
|
122
171
|
* Checks if an error is a rate limit error (HTTP 429).
|
|
123
172
|
*
|
|
@@ -51,6 +51,9 @@ exports.isValidIsoDate = isValidIsoDate;
|
|
|
51
51
|
exports.isPositiveInteger = isPositiveInteger;
|
|
52
52
|
exports.validateField = validateField;
|
|
53
53
|
exports.safeJsonParse = safeJsonParse;
|
|
54
|
+
exports.validateDiscountAmount = validateDiscountAmount;
|
|
55
|
+
exports.validateCustomDataSize = validateCustomDataSize;
|
|
56
|
+
exports.getRetryAfterSeconds = getRetryAfterSeconds;
|
|
54
57
|
exports.isRateLimitError = isRateLimitError;
|
|
55
58
|
exports.isRetryableError = isRetryableError;
|
|
56
59
|
exports.lemonSqueezyApiRequest = lemonSqueezyApiRequest;
|
|
@@ -101,6 +104,7 @@ function isValidEmail(email) {
|
|
|
101
104
|
* This prevents Server-Side Request Forgery (SSRF) attacks.
|
|
102
105
|
*
|
|
103
106
|
* @param url - The URL to validate
|
|
107
|
+
* @param requireHttps - If true, only HTTPS URLs are allowed (default: false)
|
|
104
108
|
* @returns True if the URL is valid and safe, false otherwise
|
|
105
109
|
*
|
|
106
110
|
* @example
|
|
@@ -108,10 +112,15 @@ function isValidEmail(email) {
|
|
|
108
112
|
* isValidUrl('http://localhost:3000') // false (internal)
|
|
109
113
|
* isValidUrl('ftp://files.example.com') // false (non-http protocol)
|
|
110
114
|
* isValidUrl('http://169.254.169.254') // false (AWS metadata)
|
|
115
|
+
* isValidUrl('http://example.com', true) // false (HTTPS required)
|
|
111
116
|
*/
|
|
112
|
-
function isValidUrl(url) {
|
|
117
|
+
function isValidUrl(url, requireHttps = false) {
|
|
113
118
|
try {
|
|
114
119
|
const parsedUrl = new URL(url);
|
|
120
|
+
// If HTTPS is required, reject HTTP URLs
|
|
121
|
+
if (requireHttps && parsedUrl.protocol !== 'https:') {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
115
124
|
// Only allow http and https protocols (security: prevent file://, javascript:, etc.)
|
|
116
125
|
if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
|
|
117
126
|
return false;
|
|
@@ -199,6 +208,7 @@ function isPositiveInteger(value) {
|
|
|
199
208
|
* - 'required': Ensures value is not empty/null/undefined
|
|
200
209
|
* - 'email': RFC 5322 compliant email validation
|
|
201
210
|
* - 'url': Safe URL validation with SSRF protection
|
|
211
|
+
* - 'httpsUrl': Safe URL validation requiring HTTPS (for webhooks)
|
|
202
212
|
* - 'date': ISO 8601 date format validation
|
|
203
213
|
* - 'positiveInteger': Positive integer validation
|
|
204
214
|
*
|
|
@@ -210,6 +220,7 @@ function isPositiveInteger(value) {
|
|
|
210
220
|
* @example
|
|
211
221
|
* validateField('email', 'user@example.com', 'email') // passes
|
|
212
222
|
* validateField('email', 'invalid', 'email') // throws "email must be a valid email address"
|
|
223
|
+
* validateField('webhookUrl', 'http://example.com', 'httpsUrl') // throws "webhookUrl must be a valid HTTPS URL"
|
|
213
224
|
*/
|
|
214
225
|
function validateField(fieldName, value, validationType) {
|
|
215
226
|
if (validationType === 'required') {
|
|
@@ -233,6 +244,11 @@ function validateField(fieldName, value, validationType) {
|
|
|
233
244
|
throw new Error(`${fieldName} must be a valid URL`);
|
|
234
245
|
}
|
|
235
246
|
break;
|
|
247
|
+
case 'httpsUrl':
|
|
248
|
+
if (typeof value !== 'string' || !isValidUrl(value, true)) {
|
|
249
|
+
throw new Error(`${fieldName} must be a valid HTTPS URL (Lemon Squeezy requires HTTPS for webhooks)`);
|
|
250
|
+
}
|
|
251
|
+
break;
|
|
236
252
|
case 'date':
|
|
237
253
|
if (typeof value !== 'string' || !isValidIsoDate(value)) {
|
|
238
254
|
throw new Error(`${fieldName} must be a valid ISO 8601 date`);
|
|
@@ -269,6 +285,85 @@ function safeJsonParse(jsonString, fieldName) {
|
|
|
269
285
|
throw new Error(`${fieldName} contains invalid JSON`);
|
|
270
286
|
}
|
|
271
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* Validates discount amount based on the amount type.
|
|
290
|
+
*
|
|
291
|
+
* - For 'percent' type: amount must be between 0 and 100 (inclusive)
|
|
292
|
+
* - For 'fixed' type: amount must be a positive integer (in cents)
|
|
293
|
+
*
|
|
294
|
+
* @param amount - The discount amount to validate
|
|
295
|
+
* @param amountType - The type of discount ('percent' or 'fixed')
|
|
296
|
+
* @throws Error if the amount is invalid for the given type
|
|
297
|
+
*
|
|
298
|
+
* @example
|
|
299
|
+
* validateDiscountAmount(50, 'percent') // passes (50%)
|
|
300
|
+
* validateDiscountAmount(150, 'percent') // throws "Percent discount must be between 0 and 100"
|
|
301
|
+
* validateDiscountAmount(1000, 'fixed') // passes ($10.00 in cents)
|
|
302
|
+
* validateDiscountAmount(-100, 'fixed') // throws "Fixed discount amount must be a positive integer"
|
|
303
|
+
*/
|
|
304
|
+
function validateDiscountAmount(amount, amountType) {
|
|
305
|
+
if (amountType === 'percent') {
|
|
306
|
+
if (amount < 0 || amount > 100) {
|
|
307
|
+
throw new Error('Percent discount must be between 0 and 100');
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
else if (amountType === 'fixed') {
|
|
311
|
+
if (!Number.isInteger(amount) || amount < 0) {
|
|
312
|
+
throw new Error('Fixed discount amount must be a positive integer (in cents)');
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
/** Maximum payload size for custom data (10KB) */
|
|
317
|
+
const MAX_CUSTOM_DATA_SIZE_BYTES = 10 * 1024;
|
|
318
|
+
/**
|
|
319
|
+
* Validates that a custom data payload doesn't exceed the maximum size.
|
|
320
|
+
*
|
|
321
|
+
* This prevents memory issues and potential abuse from extremely large payloads.
|
|
322
|
+
*
|
|
323
|
+
* @param data - The custom data object or string to validate
|
|
324
|
+
* @param maxSizeBytes - Maximum allowed size in bytes (default: 10KB)
|
|
325
|
+
* @throws Error if the payload exceeds the maximum size
|
|
326
|
+
*
|
|
327
|
+
* @example
|
|
328
|
+
* validateCustomDataSize({ key: 'value' }) // passes
|
|
329
|
+
* validateCustomDataSize(veryLargeObject) // throws if > 10KB
|
|
330
|
+
*/
|
|
331
|
+
function validateCustomDataSize(data, maxSizeBytes = MAX_CUSTOM_DATA_SIZE_BYTES) {
|
|
332
|
+
const jsonString = typeof data === 'string' ? data : JSON.stringify(data);
|
|
333
|
+
const sizeBytes = Buffer.byteLength(jsonString, 'utf8');
|
|
334
|
+
if (sizeBytes > maxSizeBytes) {
|
|
335
|
+
const sizeKb = Math.round(sizeBytes / 1024);
|
|
336
|
+
const maxKb = Math.round(maxSizeBytes / 1024);
|
|
337
|
+
throw new Error(`Custom data exceeds maximum size (${sizeKb}KB > ${maxKb}KB). Reduce the payload size.`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Extracts the Retry-After header value from an error response.
|
|
342
|
+
*
|
|
343
|
+
* The Retry-After header indicates how long to wait before retrying
|
|
344
|
+
* a rate-limited or temporarily unavailable request.
|
|
345
|
+
*
|
|
346
|
+
* @param error - The error object that may contain Retry-After header
|
|
347
|
+
* @returns Number of seconds to wait, or undefined if not present
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* getRetryAfterSeconds({ response: { headers: { 'retry-after': '60' } } }) // 60
|
|
351
|
+
* getRetryAfterSeconds({ response: { headers: {} } }) // undefined
|
|
352
|
+
*/
|
|
353
|
+
function getRetryAfterSeconds(error) {
|
|
354
|
+
var _a, _b;
|
|
355
|
+
if (error && typeof error === 'object') {
|
|
356
|
+
const err = error;
|
|
357
|
+
const retryAfter = (_b = (_a = err.response) === null || _a === void 0 ? void 0 : _a.headers) === null || _b === void 0 ? void 0 : _b['retry-after'];
|
|
358
|
+
if (retryAfter) {
|
|
359
|
+
const seconds = parseInt(retryAfter, 10);
|
|
360
|
+
if (!isNaN(seconds) && seconds > 0) {
|
|
361
|
+
return seconds;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
return undefined;
|
|
366
|
+
}
|
|
272
367
|
/**
|
|
273
368
|
* Checks if an error is a rate limit error (HTTP 429).
|
|
274
369
|
*
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Customer Resource
|
|
3
|
+
*
|
|
4
|
+
* Provides operations for managing customers in Lemon Squeezy.
|
|
5
|
+
*
|
|
6
|
+
* Available operations:
|
|
7
|
+
* - Create: Create a new customer
|
|
8
|
+
* - Delete: Archive a customer
|
|
9
|
+
* - Get: Retrieve a single customer by ID
|
|
10
|
+
* - Get Many: Retrieve multiple customers with filtering
|
|
11
|
+
* - Update: Update customer information
|
|
12
|
+
*
|
|
13
|
+
* @see https://docs.lemonsqueezy.com/api/customers
|
|
14
|
+
*/
|
|
1
15
|
import type { INodeProperties } from 'n8n-workflow';
|
|
2
16
|
export declare const customerOperations: INodeProperties;
|
|
3
17
|
export declare const customerFields: INodeProperties[];
|
|
@@ -52,7 +52,8 @@ exports.customerFields = [
|
|
|
52
52
|
type: 'string',
|
|
53
53
|
required: true,
|
|
54
54
|
default: '',
|
|
55
|
-
|
|
55
|
+
placeholder: 'e.g., 12345',
|
|
56
|
+
description: 'The ID of the customer (numeric string)',
|
|
56
57
|
displayOptions: {
|
|
57
58
|
show: { resource: ['customer'], operation: ['get', 'update', 'delete'] },
|
|
58
59
|
},
|
|
@@ -64,7 +65,8 @@ exports.customerFields = [
|
|
|
64
65
|
type: 'string',
|
|
65
66
|
required: true,
|
|
66
67
|
default: '',
|
|
67
|
-
|
|
68
|
+
placeholder: 'e.g., 12345',
|
|
69
|
+
description: 'The ID of the store this customer belongs to. Find this in your <a href="https://app.lemonsqueezy.com/settings/stores" target="_blank">Lemon Squeezy Dashboard</a>.',
|
|
68
70
|
displayOptions: {
|
|
69
71
|
show: { resource: ['customer'], operation: ['create'] },
|
|
70
72
|
},
|
|
@@ -198,8 +200,8 @@ exports.customerFields = [
|
|
|
198
200
|
name: 'limit',
|
|
199
201
|
type: 'number',
|
|
200
202
|
default: 50,
|
|
201
|
-
description: 'Max number of results to return',
|
|
202
|
-
typeOptions: { minValue: 1 },
|
|
203
|
+
description: 'Max number of results to return (API maximum is 100 per page)',
|
|
204
|
+
typeOptions: { minValue: 1, maxValue: 100 },
|
|
203
205
|
displayOptions: {
|
|
204
206
|
show: { resource: ['customer'], operation: ['getAll'], returnAll: [false] },
|
|
205
207
|
},
|
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Discount Resource
|
|
3
|
+
*
|
|
4
|
+
* Provides operations for managing discount codes in Lemon Squeezy.
|
|
5
|
+
*
|
|
6
|
+
* Available operations:
|
|
7
|
+
* - Create: Create a new discount code
|
|
8
|
+
* - Delete: Delete an existing discount code
|
|
9
|
+
* - Get: Retrieve a single discount by ID
|
|
10
|
+
* - Get Many: Retrieve multiple discounts with filtering
|
|
11
|
+
* - Update: Update an existing discount code
|
|
12
|
+
*
|
|
13
|
+
* @see https://docs.lemonsqueezy.com/api/discounts
|
|
14
|
+
*/
|
|
1
15
|
import type { INodeProperties } from 'n8n-workflow';
|
|
2
16
|
export declare const discountOperations: INodeProperties;
|
|
3
17
|
export declare const discountFields: INodeProperties[];
|
|
@@ -35,20 +35,27 @@ exports.discountOperations = {
|
|
|
35
35
|
action: 'Get many discounts',
|
|
36
36
|
description: 'Retrieve multiple discounts',
|
|
37
37
|
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Update',
|
|
40
|
+
value: 'update',
|
|
41
|
+
action: 'Update a discount',
|
|
42
|
+
description: 'Update an existing discount code',
|
|
43
|
+
},
|
|
38
44
|
],
|
|
39
45
|
default: 'getAll',
|
|
40
46
|
};
|
|
41
47
|
exports.discountFields = [
|
|
42
|
-
// Discount ID for Get/Delete operations
|
|
48
|
+
// Discount ID for Get/Update/Delete operations
|
|
43
49
|
{
|
|
44
50
|
displayName: 'Discount ID',
|
|
45
51
|
name: 'discountId',
|
|
46
52
|
type: 'string',
|
|
47
53
|
required: true,
|
|
48
54
|
default: '',
|
|
49
|
-
|
|
55
|
+
placeholder: 'e.g., 12345',
|
|
56
|
+
description: 'The ID of the discount (numeric string)',
|
|
50
57
|
displayOptions: {
|
|
51
|
-
show: { resource: ['discount'], operation: ['get', 'delete'] },
|
|
58
|
+
show: { resource: ['discount'], operation: ['get', 'update', 'delete'] },
|
|
52
59
|
},
|
|
53
60
|
},
|
|
54
61
|
// Create Fields
|
|
@@ -91,7 +98,7 @@ exports.discountFields = [
|
|
|
91
98
|
type: 'number',
|
|
92
99
|
required: true,
|
|
93
100
|
default: 0,
|
|
94
|
-
description: 'Discount amount
|
|
101
|
+
description: 'Discount amount. For percent type: 0-100 (e.g., 25 = 25% off). For fixed type: amount in cents (e.g., 1000 = $10.00 off).',
|
|
95
102
|
displayOptions: {
|
|
96
103
|
show: { resource: ['discount'], operation: ['create'] },
|
|
97
104
|
},
|
|
@@ -164,6 +171,86 @@ exports.discountFields = [
|
|
|
164
171
|
},
|
|
165
172
|
],
|
|
166
173
|
},
|
|
174
|
+
// Update Fields
|
|
175
|
+
{
|
|
176
|
+
displayName: 'Update Fields',
|
|
177
|
+
name: 'updateFields',
|
|
178
|
+
type: 'collection',
|
|
179
|
+
placeholder: 'Add Field',
|
|
180
|
+
default: {},
|
|
181
|
+
displayOptions: {
|
|
182
|
+
show: { resource: ['discount'], operation: ['update'] },
|
|
183
|
+
},
|
|
184
|
+
options: [
|
|
185
|
+
{
|
|
186
|
+
displayName: 'Name',
|
|
187
|
+
name: 'name',
|
|
188
|
+
type: 'string',
|
|
189
|
+
default: '',
|
|
190
|
+
description: 'Internal name for the discount (not visible to customers)',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
displayName: 'Code',
|
|
194
|
+
name: 'code',
|
|
195
|
+
type: 'string',
|
|
196
|
+
default: '',
|
|
197
|
+
description: 'The discount code customers will use at checkout',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
displayName: 'Amount',
|
|
201
|
+
name: 'amount',
|
|
202
|
+
type: 'number',
|
|
203
|
+
default: 0,
|
|
204
|
+
description: 'Discount amount (percentage 0-100 for percent type, or fixed amount in cents for fixed type)',
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
displayName: 'Amount Type',
|
|
208
|
+
name: 'amountType',
|
|
209
|
+
type: 'options',
|
|
210
|
+
options: constants_1.DISCOUNT_AMOUNT_TYPES,
|
|
211
|
+
default: 'percent',
|
|
212
|
+
description: 'Whether the discount is a percentage or fixed amount',
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
displayName: 'Duration',
|
|
216
|
+
name: 'duration',
|
|
217
|
+
type: 'options',
|
|
218
|
+
options: constants_1.DISCOUNT_DURATION_TYPES,
|
|
219
|
+
default: 'once',
|
|
220
|
+
description: 'How long the discount should apply for subscriptions',
|
|
221
|
+
},
|
|
222
|
+
{
|
|
223
|
+
displayName: 'Duration In Months',
|
|
224
|
+
name: 'durationInMonths',
|
|
225
|
+
type: 'number',
|
|
226
|
+
default: 1,
|
|
227
|
+
description: 'Number of months the discount applies (only for "repeating" duration)',
|
|
228
|
+
typeOptions: { minValue: 1 },
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
displayName: 'Max Redemptions',
|
|
232
|
+
name: 'maxRedemptions',
|
|
233
|
+
type: 'number',
|
|
234
|
+
default: 0,
|
|
235
|
+
description: 'Maximum number of times this discount can be used (0 for unlimited)',
|
|
236
|
+
typeOptions: { minValue: 0 },
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
displayName: 'Starts At',
|
|
240
|
+
name: 'startsAt',
|
|
241
|
+
type: 'dateTime',
|
|
242
|
+
default: '',
|
|
243
|
+
description: 'When the discount becomes active (ISO 8601 format, e.g., 2024-01-15T10:30:00Z)',
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
displayName: 'Expires At',
|
|
247
|
+
name: 'expiresAt',
|
|
248
|
+
type: 'dateTime',
|
|
249
|
+
default: '',
|
|
250
|
+
description: 'When the discount expires (ISO 8601 format, e.g., 2024-12-31T23:59:59Z)',
|
|
251
|
+
},
|
|
252
|
+
],
|
|
253
|
+
},
|
|
167
254
|
// Return All
|
|
168
255
|
{
|
|
169
256
|
displayName: 'Return All',
|
|
@@ -181,8 +268,8 @@ exports.discountFields = [
|
|
|
181
268
|
name: 'limit',
|
|
182
269
|
type: 'number',
|
|
183
270
|
default: 50,
|
|
184
|
-
description: 'Max number of results to return',
|
|
185
|
-
typeOptions: { minValue: 1 },
|
|
271
|
+
description: 'Max number of results to return (API maximum is 100 per page)',
|
|
272
|
+
typeOptions: { minValue: 1, maxValue: 100 },
|
|
186
273
|
displayOptions: {
|
|
187
274
|
show: { resource: ['discount'], operation: ['getAll'], returnAll: [false] },
|
|
188
275
|
},
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fileFields = exports.fileOperations = void 0;
|
|
4
|
+
exports.fileOperations = {
|
|
5
|
+
displayName: 'Operation',
|
|
6
|
+
name: 'operation',
|
|
7
|
+
type: 'options',
|
|
8
|
+
noDataExpression: true,
|
|
9
|
+
displayOptions: {
|
|
10
|
+
show: { resource: ['file'] },
|
|
11
|
+
},
|
|
12
|
+
options: [
|
|
13
|
+
{
|
|
14
|
+
name: 'Get',
|
|
15
|
+
value: 'get',
|
|
16
|
+
action: 'Get a file',
|
|
17
|
+
description: 'Retrieve a single file by ID',
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
name: 'Get Many',
|
|
21
|
+
value: 'getAll',
|
|
22
|
+
action: 'Get many files',
|
|
23
|
+
description: 'Retrieve multiple files',
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
default: 'getAll',
|
|
27
|
+
};
|
|
28
|
+
exports.fileFields = [
|
|
29
|
+
// File ID for Get operation
|
|
30
|
+
{
|
|
31
|
+
displayName: 'File ID',
|
|
32
|
+
name: 'fileId',
|
|
33
|
+
type: 'string',
|
|
34
|
+
required: true,
|
|
35
|
+
default: '',
|
|
36
|
+
placeholder: 'e.g., 12345',
|
|
37
|
+
description: 'The ID of the file (numeric string)',
|
|
38
|
+
displayOptions: {
|
|
39
|
+
show: { resource: ['file'], operation: ['get'] },
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
// Return All
|
|
43
|
+
{
|
|
44
|
+
displayName: 'Return All',
|
|
45
|
+
name: 'returnAll',
|
|
46
|
+
type: 'boolean',
|
|
47
|
+
default: false,
|
|
48
|
+
description: 'Whether to return all results or only up to a given limit',
|
|
49
|
+
displayOptions: {
|
|
50
|
+
show: { resource: ['file'], operation: ['getAll'] },
|
|
51
|
+
},
|
|
52
|
+
},
|
|
53
|
+
// Limit
|
|
54
|
+
{
|
|
55
|
+
displayName: 'Limit',
|
|
56
|
+
name: 'limit',
|
|
57
|
+
type: 'number',
|
|
58
|
+
default: 50,
|
|
59
|
+
description: 'Max number of results to return (API maximum is 100 per page)',
|
|
60
|
+
typeOptions: { minValue: 1, maxValue: 100 },
|
|
61
|
+
displayOptions: {
|
|
62
|
+
show: { resource: ['file'], operation: ['getAll'], returnAll: [false] },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
// Filters
|
|
66
|
+
{
|
|
67
|
+
displayName: 'Filters',
|
|
68
|
+
name: 'filters',
|
|
69
|
+
type: 'collection',
|
|
70
|
+
placeholder: 'Add Filter',
|
|
71
|
+
default: {},
|
|
72
|
+
displayOptions: {
|
|
73
|
+
show: { resource: ['file'], operation: ['getAll'] },
|
|
74
|
+
},
|
|
75
|
+
options: [
|
|
76
|
+
{
|
|
77
|
+
displayName: 'Variant ID',
|
|
78
|
+
name: 'variantId',
|
|
79
|
+
type: 'string',
|
|
80
|
+
default: '',
|
|
81
|
+
placeholder: 'e.g., 12345',
|
|
82
|
+
description: 'Filter files by variant ID',
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
},
|
|
86
|
+
];
|
|
@@ -15,7 +15,8 @@ import { checkoutOperations, checkoutFields } from './checkout';
|
|
|
15
15
|
import { webhookOperations, webhookFields } from './webhook';
|
|
16
16
|
import { usageRecordOperations, usageRecordFields } from './usageRecord';
|
|
17
17
|
import { userOperations, userFields } from './user';
|
|
18
|
+
import { fileOperations, fileFields } from './file';
|
|
18
19
|
export declare const resourceProperty: INodeProperties;
|
|
19
20
|
export declare const allOperations: INodeProperties[];
|
|
20
21
|
export declare const allFields: INodeProperties[];
|
|
21
|
-
export { productOperations, productFields, orderOperations, orderFields, orderItemOperations, orderItemFields, subscriptionOperations, subscriptionFields, subscriptionInvoiceOperations, subscriptionInvoiceFields, customerOperations, customerFields, licenseKeyOperations, licenseKeyFields, licenseKeyInstanceOperations, licenseKeyInstanceFields, discountOperations, discountFields, discountRedemptionOperations, discountRedemptionFields, storeOperations, storeFields, variantOperations, variantFields, checkoutOperations, checkoutFields, webhookOperations, webhookFields, usageRecordOperations, usageRecordFields, userOperations, userFields, };
|
|
22
|
+
export { productOperations, productFields, orderOperations, orderFields, orderItemOperations, orderItemFields, subscriptionOperations, subscriptionFields, subscriptionInvoiceOperations, subscriptionInvoiceFields, customerOperations, customerFields, licenseKeyOperations, licenseKeyFields, licenseKeyInstanceOperations, licenseKeyInstanceFields, discountOperations, discountFields, discountRedemptionOperations, discountRedemptionFields, storeOperations, storeFields, variantOperations, variantFields, checkoutOperations, checkoutFields, webhookOperations, webhookFields, usageRecordOperations, usageRecordFields, userOperations, userFields, fileOperations, fileFields, };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.userFields = exports.userOperations = exports.usageRecordFields = exports.usageRecordOperations = exports.webhookFields = exports.webhookOperations = exports.checkoutFields = exports.checkoutOperations = exports.variantFields = exports.variantOperations = exports.storeFields = exports.storeOperations = exports.discountRedemptionFields = exports.discountRedemptionOperations = exports.discountFields = exports.discountOperations = exports.licenseKeyInstanceFields = exports.licenseKeyInstanceOperations = exports.licenseKeyFields = exports.licenseKeyOperations = exports.customerFields = exports.customerOperations = exports.subscriptionInvoiceFields = exports.subscriptionInvoiceOperations = exports.subscriptionFields = exports.subscriptionOperations = exports.orderItemFields = exports.orderItemOperations = exports.orderFields = exports.orderOperations = exports.productFields = exports.productOperations = exports.allFields = exports.allOperations = exports.resourceProperty = void 0;
|
|
3
|
+
exports.fileFields = exports.fileOperations = exports.userFields = exports.userOperations = exports.usageRecordFields = exports.usageRecordOperations = exports.webhookFields = exports.webhookOperations = exports.checkoutFields = exports.checkoutOperations = exports.variantFields = exports.variantOperations = exports.storeFields = exports.storeOperations = exports.discountRedemptionFields = exports.discountRedemptionOperations = exports.discountFields = exports.discountOperations = exports.licenseKeyInstanceFields = exports.licenseKeyInstanceOperations = exports.licenseKeyFields = exports.licenseKeyOperations = exports.customerFields = exports.customerOperations = exports.subscriptionInvoiceFields = exports.subscriptionInvoiceOperations = exports.subscriptionFields = exports.subscriptionOperations = exports.orderItemFields = exports.orderItemOperations = exports.orderFields = exports.orderOperations = exports.productFields = exports.productOperations = exports.allFields = exports.allOperations = exports.resourceProperty = void 0;
|
|
4
4
|
const product_1 = require("./product");
|
|
5
5
|
Object.defineProperty(exports, "productOperations", { enumerable: true, get: function () { return product_1.productOperations; } });
|
|
6
6
|
Object.defineProperty(exports, "productFields", { enumerable: true, get: function () { return product_1.productFields; } });
|
|
@@ -49,6 +49,9 @@ Object.defineProperty(exports, "usageRecordFields", { enumerable: true, get: fun
|
|
|
49
49
|
const user_1 = require("./user");
|
|
50
50
|
Object.defineProperty(exports, "userOperations", { enumerable: true, get: function () { return user_1.userOperations; } });
|
|
51
51
|
Object.defineProperty(exports, "userFields", { enumerable: true, get: function () { return user_1.userFields; } });
|
|
52
|
+
const file_1 = require("./file");
|
|
53
|
+
Object.defineProperty(exports, "fileOperations", { enumerable: true, get: function () { return file_1.fileOperations; } });
|
|
54
|
+
Object.defineProperty(exports, "fileFields", { enumerable: true, get: function () { return file_1.fileFields; } });
|
|
52
55
|
const shared_1 = require("./shared");
|
|
53
56
|
exports.resourceProperty = {
|
|
54
57
|
displayName: 'Resource',
|
|
@@ -60,6 +63,7 @@ exports.resourceProperty = {
|
|
|
60
63
|
{ name: 'Customer', value: 'customer' },
|
|
61
64
|
{ name: 'Discount', value: 'discount' },
|
|
62
65
|
{ name: 'Discount Redemption', value: 'discountRedemption' },
|
|
66
|
+
{ name: 'File', value: 'file' },
|
|
63
67
|
{ name: 'License Key', value: 'licenseKey' },
|
|
64
68
|
{ name: 'License Key Instance', value: 'licenseKeyInstance' },
|
|
65
69
|
{ name: 'Order', value: 'order' },
|
|
@@ -92,6 +96,7 @@ exports.allOperations = [
|
|
|
92
96
|
webhook_1.webhookOperations,
|
|
93
97
|
...usageRecord_1.usageRecordOperations,
|
|
94
98
|
...user_1.userOperations,
|
|
99
|
+
file_1.fileOperations,
|
|
95
100
|
];
|
|
96
101
|
exports.allFields = [
|
|
97
102
|
...product_1.productFields,
|
|
@@ -118,4 +123,5 @@ exports.allFields = [
|
|
|
118
123
|
...webhook_1.webhookFields,
|
|
119
124
|
...usageRecord_1.usageRecordFields,
|
|
120
125
|
...user_1.userFields,
|
|
126
|
+
...file_1.fileFields,
|
|
121
127
|
];
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Order Resource
|
|
3
|
+
*
|
|
4
|
+
* Provides operations for managing orders in Lemon Squeezy.
|
|
5
|
+
* Orders are created when customers complete purchases.
|
|
6
|
+
*
|
|
7
|
+
* Available operations:
|
|
8
|
+
* - Get: Retrieve a single order by ID
|
|
9
|
+
* - Get Many: Retrieve multiple orders with filtering
|
|
10
|
+
* - Refund: Issue a full refund for an order
|
|
11
|
+
*
|
|
12
|
+
* @see https://docs.lemonsqueezy.com/api/orders
|
|
13
|
+
*/
|
|
1
14
|
import type { INodeProperties } from 'n8n-workflow';
|
|
2
15
|
export declare const orderOperations: INodeProperties;
|
|
3
16
|
export declare const orderFields: INodeProperties[];
|
|
@@ -62,8 +62,8 @@ exports.orderFields = [
|
|
|
62
62
|
name: 'limit',
|
|
63
63
|
type: 'number',
|
|
64
64
|
default: 50,
|
|
65
|
-
description: 'Max number of results to return',
|
|
66
|
-
typeOptions: { minValue: 1 },
|
|
65
|
+
description: 'Max number of results to return (API maximum is 100 per page)',
|
|
66
|
+
typeOptions: { minValue: 1, maxValue: 100 },
|
|
67
67
|
displayOptions: {
|
|
68
68
|
show: { resource: ['order'], operation: ['getAll'], returnAll: [false] },
|
|
69
69
|
},
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Product Resource
|
|
3
|
+
*
|
|
4
|
+
* Provides read-only operations for products in Lemon Squeezy.
|
|
5
|
+
* Products are managed in the Lemon Squeezy dashboard and cannot be
|
|
6
|
+
* created, updated, or deleted via the API.
|
|
7
|
+
*
|
|
8
|
+
* Available operations:
|
|
9
|
+
* - Get: Retrieve a single product by ID
|
|
10
|
+
* - Get Many: Retrieve multiple products with filtering
|
|
11
|
+
*
|
|
12
|
+
* @see https://docs.lemonsqueezy.com/api/products
|
|
13
|
+
*/
|
|
1
14
|
import type { INodeProperties } from 'n8n-workflow';
|
|
2
15
|
export declare const productOperations: INodeProperties;
|
|
3
16
|
export declare const productFields: INodeProperties[];
|
|
@@ -56,8 +56,8 @@ exports.productFields = [
|
|
|
56
56
|
name: 'limit',
|
|
57
57
|
type: 'number',
|
|
58
58
|
default: 50,
|
|
59
|
-
description: 'Max number of results to return',
|
|
60
|
-
typeOptions: { minValue: 1 },
|
|
59
|
+
description: 'Max number of results to return (API maximum is 100 per page)',
|
|
60
|
+
typeOptions: { minValue: 1, maxValue: 100 },
|
|
61
61
|
displayOptions: {
|
|
62
62
|
show: { resource: ['product'], operation: ['getAll'], returnAll: [false] },
|
|
63
63
|
},
|
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subscription Invoice Resource
|
|
3
|
+
*
|
|
4
|
+
* Provides operations for managing subscription invoices in Lemon Squeezy.
|
|
5
|
+
*
|
|
6
|
+
* Available operations:
|
|
7
|
+
* - Get: Retrieve a single subscription invoice by ID
|
|
8
|
+
* - Get Many: Retrieve multiple subscription invoices with filtering
|
|
9
|
+
* - Generate: Generate a new invoice for an outstanding balance
|
|
10
|
+
* - Refund: Issue a refund for a subscription invoice
|
|
11
|
+
*
|
|
12
|
+
* @see https://docs.lemonsqueezy.com/api/subscription-invoices
|
|
13
|
+
*/
|
|
1
14
|
import type { INodeProperties } from 'n8n-workflow';
|
|
2
15
|
export declare const subscriptionInvoiceOperations: INodeProperties[];
|
|
3
16
|
export declare const subscriptionInvoiceFields: INodeProperties[];
|
|
@@ -13,6 +13,12 @@ exports.subscriptionInvoiceOperations = [
|
|
|
13
13
|
},
|
|
14
14
|
},
|
|
15
15
|
options: [
|
|
16
|
+
{
|
|
17
|
+
name: 'Generate',
|
|
18
|
+
value: 'generate',
|
|
19
|
+
description: 'Generate an invoice for outstanding subscription balance',
|
|
20
|
+
action: 'Generate a subscription invoice',
|
|
21
|
+
},
|
|
16
22
|
{
|
|
17
23
|
name: 'Get',
|
|
18
24
|
value: 'get',
|
|
@@ -25,12 +31,18 @@ exports.subscriptionInvoiceOperations = [
|
|
|
25
31
|
description: 'Get many subscription invoices',
|
|
26
32
|
action: 'Get many subscription invoices',
|
|
27
33
|
},
|
|
34
|
+
{
|
|
35
|
+
name: 'Refund',
|
|
36
|
+
value: 'refund',
|
|
37
|
+
description: 'Issue a refund for a subscription invoice',
|
|
38
|
+
action: 'Refund a subscription invoice',
|
|
39
|
+
},
|
|
28
40
|
],
|
|
29
41
|
default: 'getAll',
|
|
30
42
|
},
|
|
31
43
|
];
|
|
32
44
|
exports.subscriptionInvoiceFields = [
|
|
33
|
-
// Get
|
|
45
|
+
// Subscription Invoice ID for Get and Refund operations
|
|
34
46
|
{
|
|
35
47
|
displayName: 'Subscription Invoice ID',
|
|
36
48
|
name: 'subscriptionInvoiceId',
|
|
@@ -40,10 +52,43 @@ exports.subscriptionInvoiceFields = [
|
|
|
40
52
|
displayOptions: {
|
|
41
53
|
show: {
|
|
42
54
|
resource: ['subscriptionInvoice'],
|
|
43
|
-
operation: ['get'],
|
|
55
|
+
operation: ['get', 'refund'],
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
description: 'The ID of the subscription invoice (e.g., "123456")',
|
|
59
|
+
},
|
|
60
|
+
// Subscription ID for Generate operation
|
|
61
|
+
{
|
|
62
|
+
displayName: 'Subscription ID',
|
|
63
|
+
name: 'generateSubscriptionId',
|
|
64
|
+
type: 'string',
|
|
65
|
+
required: true,
|
|
66
|
+
default: '',
|
|
67
|
+
displayOptions: {
|
|
68
|
+
show: {
|
|
69
|
+
resource: ['subscriptionInvoice'],
|
|
70
|
+
operation: ['generate'],
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
description: 'The ID of the subscription to generate an invoice for. Only works if the subscription has an outstanding balance.',
|
|
74
|
+
},
|
|
75
|
+
// Refund Options
|
|
76
|
+
{
|
|
77
|
+
displayName: 'Refund Amount',
|
|
78
|
+
name: 'refundAmount',
|
|
79
|
+
type: 'number',
|
|
80
|
+
required: false,
|
|
81
|
+
default: 0,
|
|
82
|
+
displayOptions: {
|
|
83
|
+
show: {
|
|
84
|
+
resource: ['subscriptionInvoice'],
|
|
85
|
+
operation: ['refund'],
|
|
44
86
|
},
|
|
45
87
|
},
|
|
46
|
-
description: '
|
|
88
|
+
description: 'Amount to refund in cents (e.g., 1000 = $10.00). Leave at 0 for full refund. Must not exceed the original invoice amount.',
|
|
89
|
+
typeOptions: {
|
|
90
|
+
minValue: 0,
|
|
91
|
+
},
|
|
47
92
|
},
|
|
48
93
|
// Get All
|
|
49
94
|
{
|
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Webhook Resource
|
|
3
|
+
*
|
|
4
|
+
* Provides operations for managing webhooks in Lemon Squeezy.
|
|
5
|
+
* Webhooks allow your application to receive real-time notifications
|
|
6
|
+
* about events in your store.
|
|
7
|
+
*
|
|
8
|
+
* Available operations:
|
|
9
|
+
* - Create: Create a new webhook endpoint
|
|
10
|
+
* - Delete: Delete an existing webhook
|
|
11
|
+
* - Get: Retrieve a single webhook by ID
|
|
12
|
+
* - Get Many: Retrieve multiple webhooks with filtering
|
|
13
|
+
* - Update: Update webhook URL, events, or secret
|
|
14
|
+
*
|
|
15
|
+
* Security: Webhook secrets must be at least 32 characters.
|
|
16
|
+
* Generate one using: openssl rand -hex 32
|
|
17
|
+
*
|
|
18
|
+
* @see https://docs.lemonsqueezy.com/api/webhooks
|
|
19
|
+
*/
|
|
1
20
|
import type { INodeProperties } from 'n8n-workflow';
|
|
2
21
|
export declare const webhookOperations: INodeProperties;
|
|
3
22
|
export declare const webhookFields: INodeProperties[];
|
|
@@ -325,6 +325,22 @@ export interface LicenseKeyInstanceAttributes extends BaseAttributes {
|
|
|
325
325
|
identifier: string;
|
|
326
326
|
name: string;
|
|
327
327
|
}
|
|
328
|
+
/**
|
|
329
|
+
* File attributes
|
|
330
|
+
*/
|
|
331
|
+
export interface FileAttributes extends BaseAttributes {
|
|
332
|
+
variant_id: number;
|
|
333
|
+
identifier: string;
|
|
334
|
+
name: string;
|
|
335
|
+
extension: string;
|
|
336
|
+
download_url: string;
|
|
337
|
+
size: number;
|
|
338
|
+
size_formatted: string;
|
|
339
|
+
version: string;
|
|
340
|
+
sort: number;
|
|
341
|
+
status: 'draft' | 'published';
|
|
342
|
+
test_mode: boolean;
|
|
343
|
+
}
|
|
328
344
|
/**
|
|
329
345
|
* Resource types
|
|
330
346
|
*/
|
|
@@ -339,6 +355,7 @@ export type Discount = JsonApiResource<'discounts', DiscountAttributes>;
|
|
|
339
355
|
export type Checkout = JsonApiResource<'checkouts', CheckoutAttributes>;
|
|
340
356
|
export type Webhook = JsonApiResource<'webhooks', WebhookAttributes>;
|
|
341
357
|
export type LicenseKeyInstance = JsonApiResource<'license-key-instances', LicenseKeyInstanceAttributes>;
|
|
358
|
+
export type File = JsonApiResource<'files', FileAttributes>;
|
|
342
359
|
/**
|
|
343
360
|
* Webhook event types
|
|
344
361
|
*/
|
|
@@ -413,7 +430,7 @@ export interface PaginationOptions {
|
|
|
413
430
|
/**
|
|
414
431
|
* Resource name to endpoint mapping
|
|
415
432
|
*/
|
|
416
|
-
export type ResourceName = 'product' | 'order' | 'subscription' | 'customer' | 'licenseKey' | 'discount' | 'store' | 'variant' | 'checkout' | 'webhook' | 'licenseKeyInstance';
|
|
433
|
+
export type ResourceName = 'product' | 'order' | 'subscription' | 'customer' | 'licenseKey' | 'discount' | 'store' | 'variant' | 'checkout' | 'webhook' | 'licenseKeyInstance' | 'file';
|
|
417
434
|
/**
|
|
418
435
|
* Operation types
|
|
419
436
|
*/
|