diff --git a/packages/nodes-base/nodes/Paddle/CouponDescription.ts b/packages/nodes-base/nodes/Paddle/CouponDescription.ts index 179ec825b3..64e87cf9a0 100644 --- a/packages/nodes-base/nodes/Paddle/CouponDescription.ts +++ b/packages/nodes-base/nodes/Paddle/CouponDescription.ts @@ -37,9 +37,9 @@ export const couponOperations = [ ] as INodeProperties[]; export const couponFields = [ - /* -------------------------------------------------------------------------- */ - /* coupon:create */ - /* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* coupon:create */ +/* -------------------------------------------------------------------------- */ { displayName: 'Coupon Type', name: 'couponType', @@ -71,12 +71,9 @@ export const couponFields = [ ] }, { - displayName: 'Product IDs', + displayName: 'Product ID(s)', name: 'productIds', - type: 'multiOptions', - typeOptions: { - loadOptionsMethod: 'getProducts', - }, + type: 'string', displayOptions: { show: { resource: [ @@ -187,38 +184,6 @@ export const couponFields = [ default: 'EUR', description: 'The currency must match the balance currency specified in your account.', options: [ - { - name: 'ARS', - value: 'ARS' - }, - { - name: 'AUD', - value: 'AUD' - }, - { - name: 'BRL', - value: 'BRL' - }, - { - name: 'CAD', - value: 'CAD' - }, - { - name: 'CHF', - value: 'CHF' - }, - { - name: 'CNY', - value: 'CNY' - }, - { - name: 'CZK', - value: 'CZK' - }, - { - name: 'DKK', - value: 'DKK' - }, { name: 'EUR', value: 'EUR' @@ -227,70 +192,10 @@ export const couponFields = [ name: 'GBP', value: 'GBP' }, - { - name: 'HKD', - value: 'HKD' - }, - { - name: 'HUF', - value: 'HUF' - }, - { - name: 'INR', - value: 'INR' - }, - { - name: 'JPY', - value: 'JPY' - }, - { - name: 'KRW', - value: 'KRW' - }, - { - name: 'MXN', - value: 'MXN' - }, - { - name: 'NOK', - value: 'NOK' - }, - { - name: 'NZD', - value: 'NZD' - }, - { - name: 'PLN', - value: 'PLN' - }, - { - name: 'RUB', - value: 'RUB' - }, - { - name: 'SEK', - value: 'SEK' - }, - { - name: 'SGD', - value: 'SGD' - }, - { - name: 'THB', - value: 'THB' - }, - { - name: 'TWD', - value: 'TWD' - }, { name: 'USD', value: 'USD' }, - { - name: 'ZAR', - value: 'ZAR' - }, ], displayOptions: { show: { @@ -390,13 +295,6 @@ export const couponFields = [ default: '', description: 'Prefix for generated codes. Not valid if coupon_code is specified.', }, - { - displayName: 'Description', - name: 'description', - type: 'string', - default: '', - description: 'Description of the coupon. This will be displayed in the Seller Dashboard.', - }, { displayName: 'Expires', name: 'expires', @@ -415,6 +313,13 @@ export const couponFields = [ default: '', description: 'The name of the coupon group this coupon should be assigned to.', }, + { + displayName: 'Recurring', + name: 'recurring', + type: 'boolean', + default: false, + description: 'If the coupon is used on subscription products, this indicates whether the discount should apply to recurring payments after the initial purchase.', + }, { displayName: 'Number of Coupons', name: 'numberOfCoupons', @@ -423,21 +328,21 @@ export const couponFields = [ description: 'Number of coupons to generate. Not valid if coupon_code is specified.', }, { - displayName: 'Recurring', - name: 'recurring', - type: 'boolean', - default: false, - description: 'If the coupon is used on subscription products, this indicates whether the discount should apply to recurring payments after the initial purchase.', + displayName: 'Description', + name: 'description', + type: 'string', + default: '', + description: 'Description of the coupon. This will be displayed in the Seller Dashboard.', }, ], }, - /* -------------------------------------------------------------------------- */ - /* coupon:getAll */ - /* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* coupon:getAll */ +/* -------------------------------------------------------------------------- */ { displayName: 'Product ID', name: 'productId', - type: 'string', + type: 'number', displayOptions: { show: { resource: [ @@ -449,53 +354,11 @@ export const couponFields = [ }, }, default: '', - required: true, description: 'The specific product/subscription ID.', }, - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'coupon', - ], - }, - }, - default: false, - description: 'If all results should be returned or only up to a given limit.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'coupon', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, - /* -------------------------------------------------------------------------- */ - /* coupon:update */ - /* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* coupon:update */ +/* -------------------------------------------------------------------------- */ { displayName: 'Update by', name: 'updateBy', @@ -590,7 +453,7 @@ export const couponFields = [ }, }, { - displayName: 'Additional Fields', + displayName: ' Additional Fields', name: 'additionalFieldsJson', type: 'json', typeOptions: { @@ -640,191 +503,36 @@ export const couponFields = [ description: 'Number of times a coupon can be used in a checkout. This will be set to 999,999 by default, if not specified.', }, { - displayName: 'Discount', - name: 'discount', - type: 'fixedCollection', - default: 'discountProperties', + displayName: 'Currency', + name: 'currency', + type: 'options', + default: 'EUR', + description: 'The currency must match the balance currency specified in your account.', options: [ { - displayName: 'Discount Properties', - name: 'discountProperties', - values: [ - { - displayName: 'Currency', - name: 'currency', - type: 'options', - default: 'EUR', - description: 'The currency must match the balance currency specified in your account.', - displayOptions: { - show: { - discountType: [ - 'flat', - ], - }, - }, - options: [ - { - name: 'ARS', - value: 'ARS' - }, - { - name: 'AUD', - value: 'AUD' - }, - { - name: 'BRL', - value: 'BRL' - }, - { - name: 'CAD', - value: 'CAD' - }, - { - name: 'CHF', - value: 'CHF' - }, - { - name: 'CNY', - value: 'CNY' - }, - { - name: 'CZK', - value: 'CZK' - }, - { - name: 'DKK', - value: 'DKK' - }, - { - name: 'EUR', - value: 'EUR' - }, - { - name: 'GBP', - value: 'GBP' - }, - { - name: 'HKD', - value: 'HKD' - }, - { - name: 'HUF', - value: 'HUF' - }, - { - name: 'INR', - value: 'INR' - }, - { - name: 'JPY', - value: 'JPY' - }, - { - name: 'KRW', - value: 'KRW' - }, - { - name: 'MXN', - value: 'MXN' - }, - { - name: 'NOK', - value: 'NOK' - }, - { - name: 'NZD', - value: 'NZD' - }, - { - name: 'PLN', - value: 'PLN' - }, - { - name: 'RUB', - value: 'RUB' - }, - { - name: 'SEK', - value: 'SEK' - }, - { - name: 'SGD', - value: 'SGD' - }, - { - name: 'THB', - value: 'THB' - }, - { - name: 'TWD', - value: 'TWD' - }, - { - name: 'USD', - value: 'USD' - }, - { - name: 'ZAR', - value: 'ZAR' - }, - ], - }, - { - displayName: 'Discount Amount Currency', - name: 'discountAmount', - type: 'number', - default: '', - description: 'Discount amount.', - displayOptions: { - show: { - discountType: [ - 'flat', - ], - }, - }, - typeOptions: { - minValue: 0 - }, - }, - { - displayName: 'Discount Amount Percentage', - name: 'discountAmount', - type: 'number', - default: '', - description: 'Discount amount.', - displayOptions: { - show: { - discountType: [ - 'percentage', - ], - }, - }, - typeOptions: { - minValue: 0, - maxValue: 100 - }, - }, - { - displayName: 'Discount Type', - name: 'discountType', - type: 'options', - default: 'flat', - description: 'Either flat or percentage.', - options: [ - { - name: 'Flat', - value: 'flat' - }, - { - name: 'Percentage', - value: 'percentage' - }, - ] - }, - ], + name: 'EUR', + value: 'EUR' + }, + { + name: 'GBP', + value: 'GBP' + }, + { + name: 'USD', + value: 'USD' }, ], }, + { + displayName: 'Discount Amount', + name: 'discountAmount', + type: 'number', + default: '', + description: 'Discount amount.', + typeOptions: { + minValue: 0 + }, + }, { displayName: 'Expires', name: 'expires', @@ -851,7 +559,7 @@ export const couponFields = [ description: 'New group name to move coupon to.', }, { - displayName: 'Product IDs', + displayName: 'Product ID(s)', name: 'productIds', type: 'string', default: '', diff --git a/packages/nodes-base/nodes/Paddle/GenericFunctions.ts b/packages/nodes-base/nodes/Paddle/GenericFunctions.ts index 243dc56ff6..8aa1318845 100644 --- a/packages/nodes-base/nodes/Paddle/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Paddle/GenericFunctions.ts @@ -1,6 +1,4 @@ -import { - OptionsWithUri, -} from 'request'; +import { OptionsWithUri } from 'request'; import { IExecuteFunctions, @@ -8,6 +6,7 @@ import { ILoadOptionsFunctions, IExecuteSingleFunctions, IWebhookFunctions, + BINARY_ENCODING } from 'n8n-core'; import { @@ -21,50 +20,37 @@ export async function paddleApiRequest(this: IHookFunctions | IExecuteFunctions throw new Error('Could not retrieve credentials!'); } - const options: OptionsWithUri = { + const options : OptionsWithUri = { method, headers: { 'content-type': 'application/json' }, - uri: `https://vendors.paddle.com/api${endpoint}`, + uri: `https://vendors.paddle.com/api${endpoint}` , body, json: true }; body['vendor_id'] = credentials.vendorId; body['vendor_auth_code'] = credentials.vendorAuthCode; + + console.log(options.body); + console.log(options); + try { const response = await this.helpers.request!(options); + console.log(response); if (!response.success) { throw new Error(`Code: ${response.error.code}. Message: ${response.error.message}`); } - return response; + return response.response; } catch (error) { - throw new Error(`ERROR: Code: ${error.code}. Message: ${error.message}`); + console.log(error); + throw new Error(error); } } -export async function paddleApiRequestAllItems(this: IHookFunctions | IExecuteFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query: IDataObject = {}): Promise { // tslint:disable-line:no-any - - const returnData: IDataObject[] = []; - - let responseData; - - body.results_per_page = 200; - body.page = 1; - - do { - responseData = await paddleApiRequest.call(this, endpoint, method, body, query); - returnData.push.apply(returnData, responseData[propertyName]); - } while ( - responseData[propertyName].length !== 0 - ); - - return returnData; -} - export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any let result; try { diff --git a/packages/nodes-base/nodes/Paddle/OrderDescription.ts b/packages/nodes-base/nodes/Paddle/OrderDescription.ts index d4afd8f7cf..367082a4b3 100644 --- a/packages/nodes-base/nodes/Paddle/OrderDescription.ts +++ b/packages/nodes-base/nodes/Paddle/OrderDescription.ts @@ -28,9 +28,9 @@ export const orderOperations = [ export const orderFields = [ - /* -------------------------------------------------------------------------- */ - /* order:get */ - /* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* order:get */ +/* -------------------------------------------------------------------------- */ { displayName: 'Checkout ID', name: 'checkoutId', diff --git a/packages/nodes-base/nodes/Paddle/Paddle.node.ts b/packages/nodes-base/nodes/Paddle/Paddle.node.ts index 94e1246f49..e5478aa115 100644 --- a/packages/nodes-base/nodes/Paddle/Paddle.node.ts +++ b/packages/nodes-base/nodes/Paddle/Paddle.node.ts @@ -1,53 +1,20 @@ -import { - IExecuteFunctions -} from 'n8n-core'; - +import { IExecuteFunctions } from 'n8n-core'; import { IDataObject, - ILoadOptionsFunctions, INodeExecutionData, - INodePropertyOptions, INodeType, - INodeTypeDescription, + INodeTypeDescription } from 'n8n-workflow'; -import { - couponFields, - couponOperations, -} from './CouponDescription'; +import { couponFields, couponOperations } from './CouponDescription'; +import { paddleApiRequest, validateJSON } from './GenericFunctions'; +import { paymentsFields, paymentsOperations } from './PaymentDescription'; +import { planFields, planOperations } from './PlanDescription'; +import { productFields, productOperations } from './ProductDescription'; +import { userFields, userOperations } from './UserDescription'; -import { - paddleApiRequest, - paddleApiRequestAllItems, - validateJSON -} from './GenericFunctions'; - -import { - paymentFields, - paymentOperations, -} from './PaymentDescription'; - -import { - planFields, - planOperations, -} from './PlanDescription'; - -import { - productFields, - productOperations, -} from './ProductDescription'; - -import { - userFields, - userOperations, -} from './UserDescription'; - -// import { -// orderOperations, -// orderFields, -// } from './OrderDescription'; - -import * as moment from 'moment'; +import moment = require('moment'); +import { orderOperations, orderFields } from './OrderDescription'; export class Paddle implements INodeType { description: INodeTypeDescription = { @@ -81,8 +48,8 @@ export class Paddle implements INodeType { value: 'coupon', }, { - name: 'Payment', - value: 'payment', + name: 'Payments', + value: 'payments', }, { name: 'Plan', @@ -92,10 +59,10 @@ export class Paddle implements INodeType { name: 'Product', value: 'product', }, - // { - // name: 'Order', - // value: 'order', - // }, + { + name: 'Order', + value: 'order', + }, { name: 'User', value: 'user', @@ -108,8 +75,8 @@ export class Paddle implements INodeType { ...couponOperations, ...couponFields, // PAYMENT - ...paymentOperations, - ...paymentFields, + ...paymentsOperations, + ...paymentsFields, // PLAN ...planOperations, ...planFields, @@ -117,69 +84,14 @@ export class Paddle implements INodeType { ...productOperations, ...productFields, // ORDER - // ...orderOperations, - // ...orderFields, + ...orderOperations, + ...orderFields, // USER ...userOperations, ...userFields ], }; - methods = { - loadOptions: { - /* -------------------------------------------------------------------------- */ - /* PAYMENT */ - /* -------------------------------------------------------------------------- */ - - // Get all payment so they can be selected in payment rescheduling - async getPayments(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/2.0/subscription/payments'; - const paymentResponse = await paddleApiRequest.call(this, endpoint, 'POST', {}); - - // Alert user if there's no payments present to be loaded into payments property - if (paymentResponse.response === undefined || paymentResponse.response.length === 0) { - throw Error('No payments on account.'); - } - - for (const payment of paymentResponse.response) { - const id = payment.id; - returnData.push({ - name: id, - value: id, - }); - } - return returnData; - }, - - /* -------------------------------------------------------------------------- */ - /* PRODUCTS */ - /* -------------------------------------------------------------------------- */ - - // Get all Products so they can be selected in coupon creation when assigning products - async getProducts(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - const endpoint = '/2.0/product/get_products'; - const products = await paddleApiRequest.call(this, endpoint, 'POST', {}); - - // Alert user if there's no products present to be loaded into payments property - if (products.length === 0) { - throw Error('No products on account.'); - } - - for (const product of products) { - const name = product.name; - const id = product.id; - returnData.push({ - name, - value: id, - }); - } - return returnData; - }, - } - }; - async execute(this: IExecuteFunctions): Promise { const items = this.getInputData(); const returnData: IDataObject[] = []; @@ -205,21 +117,20 @@ export class Paddle implements INodeType { } } else { + const discountType = this.getNodeParameter('discountType', i) as string; const couponType = this.getNodeParameter('couponType', i) as string; const discountAmount = this.getNodeParameter('discountAmount', i) as number; + const currency = this.getNodeParameter('currency', i) as string; if (couponType === 'product') { - body.product_ids = (this.getNodeParameter('productIds', i) as string[]).join(); - } - - if (discountType === 'flat') { - body.currency = this.getNodeParameter('currency', i) as string; + body.product_ids = this.getNodeParameter('productIds', i) as string; } body.coupon_type = couponType; body.discount_type = discountType; body.discount_amount = discountAmount; + body.currency = currency; const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; @@ -253,25 +164,16 @@ export class Paddle implements INodeType { const endpoint = '/2.1/product/create_coupon'; responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); - responseData = responseData.response.coupon_codes; } } if (operation === 'getAll') { - const productId = this.getNodeParameter('productId', i) as string; - const returnAll = this.getNodeParameter('returnAll', i) as boolean; + const productIds = this.getNodeParameter('productId', i) as string; const endpoint = '/2.0/product/list_coupons'; - body.product_id = productId as string; + body.product_id = productIds as string; responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); - - if (returnAll) { - responseData = responseData.response; - } else { - const limit = this.getNodeParameter('limit', i) as number; - responseData = responseData.response.splice(0, limit); - } } if (operation === 'update') { @@ -315,9 +217,9 @@ export class Paddle implements INodeType { if (additionalFields.newGroup) { body.new_group = additionalFields.newGroup as string; } - if (additionalFields.recurring === true) { + if (additionalFields.recurring) { body.recurring = 1; - } else if (additionalFields.recurring === false) { + } else { body.recurring = 0; } if (additionalFields.productIds) { @@ -326,29 +228,15 @@ export class Paddle implements INodeType { if (additionalFields.discountAmount) { body.discount_amount = additionalFields.discountAmount as number; } - if (additionalFields.discount) { - //@ts-ignore - if (additionalFields.discount.discountProperties.discountType === 'percentage') { - // @ts-ignore - body.discount_amount = additionalFields.discount.discountProperties.discountAmount as number; - } else { - //@ts-ignore - body.currency = additionalFields.discount.discountProperties.currency as string; - //@ts-ignore - body.discount_amount = additionalFields.discount.discountProperties.discountAmount as number; - } - } } const endpoint = '/2.1/product/update_coupon'; responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); - responseData = responseData.response; } } if (resource === 'payment') { if (operation === 'getAll') { - const returnAll = this.getNodeParameter('returnAll', i) as boolean; const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean; if (jsonParameters) { @@ -374,10 +262,10 @@ export class Paddle implements INodeType { if (additionalFields.state) { body.state = additionalFields.state as string; } - if (additionalFields.isPaid) { - body.is_paid = 1; + if (additionalFields.recurring) { + body.recurring = 1; } else { - body.is_paid = 0; + body.recurring = 0; } if (additionalFields.from) { body.from = moment(additionalFields.from as Date).format('YYYY-MM-DD') as string; @@ -389,16 +277,10 @@ export class Paddle implements INodeType { body.is_one_off_charge = additionalFields.isOneOffCharge as boolean; } } + const endpoint = '/2.0/subscription/payments'; responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); - - if (returnAll) { - responseData = responseData.response; - } else { - const limit = this.getNodeParameter('limit', i) as number; - responseData = responseData.response.splice(0, limit); - } } if (operation === 'reschedule') { const paymentId = this.getNodeParameter('paymentId', i) as number; @@ -414,17 +296,9 @@ export class Paddle implements INodeType { } if (resource === 'plan') { if (operation === 'getAll') { - const returnAll = this.getNodeParameter('returnAll', i) as boolean; const endpoint = '/2.0/subscription/plans'; responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); - - if (returnAll) { - responseData = responseData.response; - } else { - const limit = this.getNodeParameter('limit', i) as number; - responseData = responseData.response.splice(0, limit); - } } if (operation === 'get') { const planId = this.getNodeParameter('planId', i) as string; @@ -434,22 +308,13 @@ export class Paddle implements INodeType { const endpoint = '/2.0/subscription/plans'; responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); - responseData = responseData.response; } } if (resource === 'product') { if (operation === 'getAll') { - const returnAll = this.getNodeParameter('returnAll', i) as boolean; const endpoint = '/2.0/product/get_products'; responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); - - if (returnAll) { - responseData = responseData.response.products; - } else { - const limit = this.getNodeParameter('limit', i) as number; - responseData = responseData.response.products.splice(0, limit); - } } } if (resource === 'order') { @@ -464,7 +329,6 @@ export class Paddle implements INodeType { } if (resource === 'user') { if (operation === 'getAll') { - const returnAll = this.getNodeParameter('returnAll', i) as boolean; const jsonParameters = this.getNodeParameter('jsonParameters', i) as boolean; @@ -480,6 +344,11 @@ export class Paddle implements INodeType { } } else { + const returnAll = this.getNodeParameter('returnAll', i) as boolean; + + if (!returnAll) { + body.results_per_page = this.getNodeParameter('limit', i) as number; + } const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject; @@ -492,20 +361,16 @@ export class Paddle implements INodeType { if (additionalFields.subscriptionId) { body.subscription_id = additionalFields.subscriptionId as string; } - } + } const endpoint = '/2.0/subscription/users'; - if (returnAll) { - responseData = await paddleApiRequestAllItems.call(this, 'response', endpoint, 'POST', body); - } else { - body.results_per_page = this.getNodeParameter('limit', i) as number; - responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); - responseData = responseData.response; - } + responseData = await paddleApiRequest.call(this, endpoint, 'POST', body); } } + console.log(responseData); + if (Array.isArray(responseData)) { returnData.push.apply(returnData, responseData as IDataObject[]); } else { diff --git a/packages/nodes-base/nodes/Paddle/PaddleTrigger.node.ts b/packages/nodes-base/nodes/Paddle/PaddleTrigger.node.ts new file mode 100644 index 0000000000..f439a5beb6 --- /dev/null +++ b/packages/nodes-base/nodes/Paddle/PaddleTrigger.node.ts @@ -0,0 +1,165 @@ +import { + IHookFunctions, + IWebhookFunctions, + } from 'n8n-core'; + + import { + IDataObject, + INodeTypeDescription, + INodeType, + IWebhookResponseData, + ILoadOptionsFunctions, + INodePropertyOptions, + } from 'n8n-workflow'; +import { paddleApiRequest } from './GenericFunctions'; + + export class PaddleTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'Paddle Trigger', + name: 'paddleTrigger', + icon: 'file:paddle.png', + group: ['trigger'], + version: 1, + description: 'Handle Paddle events via webhooks', + defaults: { + name: 'Paddle Trigger', + color: '#32325d', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'paddleApi', + required: true, + } + ], + webhooks: [ + { + name: 'default', + httpMethod: 'POST', + reponseMode: 'onReceived', + path: 'webhook', + }, + ], + properties: [ + { + displayName: 'Events', + name: 'events', + type: 'multiOptions', + required: true, + default: [], + description: 'The event to listen to.', + typeOptions: { + loadOptionsMethod: 'getEvents' + }, + options: [], + }, + ], + }; + + + + // @ts-ignore (because of request) + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId === undefined) { + // No webhook id is set so no webhook can exist + return false; + } + const endpoint = `/notifications/webhooks/${webhookData.webhookId}`; + try { + await paddleApiRequest.call(this, endpoint, 'GET'); + } catch (err) { + if (err.response && err.response.name === 'INVALID_RESOURCE_ID') { + // Webhook does not exist + delete webhookData.webhookId; + return false; + } + throw new Error(`Paddle Error: ${err}`); + } + return true; + }, + + async create(this: IHookFunctions): Promise { + let webhook; + const webhookUrl = this.getNodeWebhookUrl('default'); + const events = this.getNodeParameter('events', []) as string[]; + const body = { + url: webhookUrl, + event_types: events.map(event => { + return { name: event }; + }), + }; + const endpoint = '/notifications/webhooks'; + try { + webhook = await paddleApiRequest.call(this, endpoint, 'POST', body); + } catch (e) { + throw e; + } + + if (webhook.id === undefined) { + return false; + } + const webhookData = this.getWorkflowStaticData('node'); + webhookData.webhookId = webhook.id as string; + return true; + }, + + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + if (webhookData.webhookId !== undefined) { + const endpoint = `/notifications/webhooks/${webhookData.webhookId}`; + try { + await paddleApiRequest.call(this, endpoint, 'DELETE', {}); + } catch (e) { + return false; + } + delete webhookData.webhookId; + } + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + let webhook; + const webhookData = this.getWorkflowStaticData('node') as IDataObject; + const bodyData = this.getBodyData() as IDataObject; + const req = this.getRequestObject(); + const headerData = this.getHeaderData() as IDataObject; + const endpoint = '/notifications/verify-webhook-signature'; + + if (headerData['PAYPAL-AUTH-ALGO'] !== undefined + && headerData['PAYPAL-CERT-URL'] !== undefined + && headerData['PAYPAL-TRANSMISSION-ID'] !== undefined + && headerData['PAYPAL-TRANSMISSION-SIG'] !== undefined + && headerData['PAYPAL-TRANSMISSION-TIME'] !== undefined) { + const body = { + auth_algo: headerData['PAYPAL-AUTH-ALGO'], + cert_url: headerData['PAYPAL-CERT-URL'], + transmission_id: headerData['PAYPAL-TRANSMISSION-ID'], + transmission_sig: headerData['PAYPAL-TRANSMISSION-SIG'], + transmission_time: headerData['PAYPAL-TRANSMISSION-TIME'], + webhook_id: webhookData.webhookId, + webhook_event: bodyData, + }; + try { + webhook = await paddleApiRequest.call(this, endpoint, 'POST', body); + } catch (e) { + throw e; + } + if (webhook.verification_status !== 'SUCCESS') { + return {}; + } + } else { + return {}; + } + return { + workflowData: [ + this.helpers.returnJsonArray(req.body) + ], + }; + } + } diff --git a/packages/nodes-base/nodes/Paddle/PaymentDescription.ts b/packages/nodes-base/nodes/Paddle/PaymentDescription.ts index b2020d4f56..45d3553850 100644 --- a/packages/nodes-base/nodes/Paddle/PaymentDescription.ts +++ b/packages/nodes-base/nodes/Paddle/PaymentDescription.ts @@ -2,7 +2,7 @@ import { INodeProperties, } from 'n8n-workflow'; -export const paymentOperations = [ +export const paymentsOperations = [ { displayName: 'Operation', name: 'operation', @@ -10,7 +10,7 @@ export const paymentOperations = [ displayOptions: { show: { resource: [ - 'payment', + 'payments', ], }, }, @@ -18,7 +18,7 @@ export const paymentOperations = [ { name: 'Get All', value: 'getAll', - description: 'Get all payment.', + description: 'Get all payments.', }, { name: 'Reschedule', @@ -31,51 +31,10 @@ export const paymentOperations = [ }, ] as INodeProperties[]; -export const paymentFields = [ - /* -------------------------------------------------------------------------- */ - /* payment:getAll */ - /* -------------------------------------------------------------------------- */ - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'payment', - ], - }, - }, - default: false, - description: 'If all results should be returned or only up to a given limit.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'payment', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, +export const paymentsFields = [ +/* -------------------------------------------------------------------------- */ +/* payments:getAll */ +/* -------------------------------------------------------------------------- */ { displayName: 'JSON Parameters', name: 'jsonParameters', @@ -85,7 +44,7 @@ export const paymentFields = [ displayOptions: { show: { resource: [ - 'payment', + 'payments', ], operation: [ 'getAll', @@ -94,7 +53,7 @@ export const paymentFields = [ }, }, { - displayName: 'Additional Fields', + displayName: ' Additional Fields', name: 'additionalFieldsJson', type: 'json', typeOptions: { @@ -104,7 +63,7 @@ export const paymentFields = [ displayOptions: { show: { resource: [ - 'payment', + 'payments', ], operation: [ 'getAll', @@ -124,7 +83,7 @@ export const paymentFields = [ displayOptions: { show: { resource: [ - 'payment', + 'payments', ], operation: [ 'getAll', @@ -141,14 +100,14 @@ export const paymentFields = [ name: 'from', type: 'dateTime', default: '', - description: 'payment starting from date.', + description: 'payments starting from date.', }, { displayName: 'Date To', name: 'to', type: 'dateTime', default: '', - description: 'payment up until date.', + description: 'payments up until date.', }, { displayName: 'Is Paid', @@ -158,7 +117,7 @@ export const paymentFields = [ description: 'payment is paid.', }, { - displayName: 'Plan ID', + displayName: 'Plan', name: 'plan', type: 'string', default: '', @@ -204,29 +163,26 @@ export const paymentFields = [ }, ], }, - /* -------------------------------------------------------------------------- */ - /* payment:reschedule */ - /* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* payments:reschedule */ +/* -------------------------------------------------------------------------- */ { - displayName: 'Payment ID', - name: 'paymentId', - type: 'options', - typeOptions: { - loadOptionsMethod: 'getpayment', - }, + displayName: 'payments ID', + name: 'paymentsId', + type: 'number', default: '', required: true, displayOptions: { show: { resource: [ - 'payment', + 'payments', ], operation: [ 'reschedule', ], }, }, - description: 'The upcoming subscription payment ID.', + description: 'The upcoming subscription payments ID.', // Use loadoptions to select payments }, { displayName: 'Date', @@ -236,13 +192,13 @@ export const paymentFields = [ displayOptions: { show: { resource: [ - 'payment', + 'payments', ], operation: [ 'reschedule', ], }, }, - description: 'Date you want to move the payment to.', + description: 'Date you want to move the payments to.', }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Paddle/PlanDescription.ts b/packages/nodes-base/nodes/Paddle/PlanDescription.ts index 152db9a7ff..f6887ca64f 100644 --- a/packages/nodes-base/nodes/Paddle/PlanDescription.ts +++ b/packages/nodes-base/nodes/Paddle/PlanDescription.ts @@ -26,16 +26,16 @@ export const planOperations = [ description: 'Get all plans.', } ], - default: 'get', + default: 'getAll', description: 'The operation to perform.', }, ] as INodeProperties[]; export const planFields = [ - /* -------------------------------------------------------------------------- */ - /* plan:get */ - /* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* plan:get */ +/* -------------------------------------------------------------------------- */ { displayName: 'Plan ID', name: 'planId', @@ -54,45 +54,4 @@ export const planFields = [ }, description: 'Filter: The subscription plan ID.', }, - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'plan', - ], - }, - }, - default: false, - description: 'If all results should be returned or only up to a given limit.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'plan', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Paddle/ProductDescription.ts b/packages/nodes-base/nodes/Paddle/ProductDescription.ts index 9a627a86e0..9d6ca39b39 100644 --- a/packages/nodes-base/nodes/Paddle/ProductDescription.ts +++ b/packages/nodes-base/nodes/Paddle/ProductDescription.ts @@ -27,45 +27,4 @@ export const productOperations = [ ] as INodeProperties[]; export const productFields = [ - { - displayName: 'Return All', - name: 'returnAll', - type: 'boolean', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'product', - ], - }, - }, - default: false, - description: 'If all results should be returned or only up to a given limit.', - }, - { - displayName: 'Limit', - name: 'limit', - type: 'number', - displayOptions: { - show: { - operation: [ - 'getAll', - ], - resource: [ - 'product', - ], - returnAll: [ - false, - ], - }, - }, - typeOptions: { - minValue: 1, - maxValue: 500, - }, - default: 100, - description: 'How many results to return.', - }, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Paddle/UserDescription.ts b/packages/nodes-base/nodes/Paddle/UserDescription.ts index 50b1a92959..d45cc2ddb1 100644 --- a/packages/nodes-base/nodes/Paddle/UserDescription.ts +++ b/packages/nodes-base/nodes/Paddle/UserDescription.ts @@ -27,9 +27,9 @@ export const userOperations = [ ] as INodeProperties[]; export const userFields = [ - /* -------------------------------------------------------------------------- */ - /* user:getAll */ - /* -------------------------------------------------------------------------- */ +/* -------------------------------------------------------------------------- */ +/* user:getAll */ +/* -------------------------------------------------------------------------- */ { displayName: 'Return All', name: 'returnAll', @@ -51,7 +51,7 @@ export const userFields = [ displayName: 'Limit', name: 'limit', type: 'number', - default: 100, + default: 1, required: true, typeOptions: { minValue: 1, @@ -90,7 +90,7 @@ export const userFields = [ }, }, { - displayName: 'Additional Fields', + displayName: ' Additional Fields', name: 'additionalFieldsJson', type: 'json', typeOptions: {