From 6e4d6a5c1c6648e64b1894df8fb792468969b23f Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sat, 30 Nov 2019 12:02:30 -0500 Subject: [PATCH 1/4] done --- .../nodes/Paypal/PaypalTriger.node.ts | 121 ++++++++++++++++++ packages/nodes-base/package.json | 3 +- 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 packages/nodes-base/nodes/Paypal/PaypalTriger.node.ts diff --git a/packages/nodes-base/nodes/Paypal/PaypalTriger.node.ts b/packages/nodes-base/nodes/Paypal/PaypalTriger.node.ts new file mode 100644 index 000000000..07db20110 --- /dev/null +++ b/packages/nodes-base/nodes/Paypal/PaypalTriger.node.ts @@ -0,0 +1,121 @@ +import { + IHookFunctions, + IWebhookFunctions, + } from 'n8n-core'; + + import { + IDataObject, + INodeTypeDescription, + INodeType, + IWebhookResponseData, + ILoadOptionsFunctions, + INodePropertyOptions, + } from 'n8n-workflow'; + import { + paypalApiRequest, + } from './GenericFunctions'; + + export class PayPalTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'PayPal Trigger', + name: 'PayPal', + icon: 'file:paypal.png', + group: ['trigger'], + version: 1, + description: 'Handle PayPal events via webhooks', + defaults: { + name: 'PayPal Trigger', + color: '#32325d', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'paypalApi', + 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' + } + } + ], + }; + + methods = { + loadOptions: { + // Get all the events types to display them to user so that he can + // select them easily + async getEvents(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + let events; + try { + events = await paypalApiRequest.call(this, '/webhooks-event-types', 'GET'); + } catch (err) { + throw new Error(`PayPal Error: ${err}`); + } + for (const event of events.event_types) { + const eventName = event.name; + const eventId = event.name; + const eventDescription = event.description; + + returnData.push({ + name: eventName, + value: eventId, + description: eventDescription, + }); + } + return returnData; + }, + }, + }; + + // @ts-ignore (because of request) + webhookMethods = { + default: { + async checkExists(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + + + return true; + }, + async delete(this: IHookFunctions): Promise { + const webhookData = this.getWorkflowStaticData('node'); + + + return true; + }, + }, + }; + + async webhook(this: IWebhookFunctions): Promise { + const bodyData = this.getBodyData() as IDataObject; + const req = this.getRequestObject(); + + const events = this.getNodeParameter('events', []) as string[]; + + const eventType = bodyData.type as string | undefined; + + return { + workflowData: [ + this.helpers.returnJsonArray(req.body) + ], + }; + } + } diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index da41902dd..425bd2b59 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -110,7 +110,8 @@ "dist/nodes/Pipedrive/Pipedrive.node.js", "dist/nodes/Pipedrive/PipedriveTrigger.node.js", "dist/nodes/Postgres/Postgres.node.js", - "dist/nodes/PayPal/PayPal.node.js", + "dist/nodes/Paypal/Paypal.node.js", + "dist/nodes/Paypal/PaypalTriger.node.js", "dist/nodes/Rocketchat/Rocketchat.node.js", "dist/nodes/ReadBinaryFile.node.js", "dist/nodes/ReadBinaryFiles.node.js", From d71459535365a79c4b41640b5155026aa7277fdf Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sun, 1 Dec 2019 10:38:54 -0500 Subject: [PATCH 2/4] :sparkles: Paypal trigger done --- .../nodes/Paypal/GenericFunctions.ts | 16 +- .../nodes/Paypal/PayPalTrigger.node.ts | 195 ++++++++++++++++++ .../nodes/Paypal/PaypalTriger.node.ts | 121 ----------- packages/nodes-base/package.json | 4 +- 4 files changed, 206 insertions(+), 130 deletions(-) create mode 100644 packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts delete mode 100644 packages/nodes-base/nodes/Paypal/PaypalTriger.node.ts diff --git a/packages/nodes-base/nodes/Paypal/GenericFunctions.ts b/packages/nodes-base/nodes/Paypal/GenericFunctions.ts index c70b1de23..cee00be3a 100644 --- a/packages/nodes-base/nodes/Paypal/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Paypal/GenericFunctions.ts @@ -5,6 +5,7 @@ import { IHookFunctions, ILoadOptionsFunctions, IExecuteSingleFunctions, + IWebhookFunctions, BINARY_ENCODING } from 'n8n-core'; @@ -12,7 +13,7 @@ import { IDataObject, } from 'n8n-workflow'; -export async function paypalApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any +export async function paypalApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any const credentials = this.getCredentials('paypalApi'); const env = getEnviroment(credentials!.env as string); const tokenInfo = await getAccessToken.call(this); @@ -29,11 +30,6 @@ export async function paypalApiRequest(this: IHookFunctions | IExecuteFunctions try { return await this.helpers.request!(options); } catch (error) { - const errorMessage = error.response.body.message || error.response.body.Message; - - if (errorMessage !== undefined) { - throw errorMessage; - } throw error.response.body; } } @@ -46,7 +42,7 @@ function getEnviroment(env: string): string { }[env]; } -async function getAccessToken(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions): Promise { // tslint:disable-line:no-any +async function getAccessToken(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions): Promise { // tslint:disable-line:no-any const credentials = this.getCredentials('paypalApi'); if (credentials === undefined) { throw new Error('No credentials got returned!'); @@ -116,3 +112,9 @@ export function validateJSON(json: string | undefined): any { // tslint:disable- } return result; } + +export function upperFist(s: string): string { + return s.split('.').map(e => { + return e.toLowerCase().charAt(0).toUpperCase() + e.toLowerCase().slice(1); + }).join(' '); +} diff --git a/packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts b/packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts new file mode 100644 index 000000000..6932f60fe --- /dev/null +++ b/packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts @@ -0,0 +1,195 @@ +import { + IHookFunctions, + IWebhookFunctions, + } from 'n8n-core'; + + import { + IDataObject, + INodeTypeDescription, + INodeType, + IWebhookResponseData, + ILoadOptionsFunctions, + INodePropertyOptions, + } from 'n8n-workflow'; + import { + paypalApiRequest, + upperFist + } from './GenericFunctions'; +import { queryResult } from 'pg-promise'; + + export class PayPalTrigger implements INodeType { + description: INodeTypeDescription = { + displayName: 'PayPal Trigger', + name: 'PayPal', + icon: 'file:paypal.png', + group: ['trigger'], + version: 1, + description: 'Handle PayPal events via webhooks', + defaults: { + name: 'PayPal Trigger', + color: '#32325d', + }, + inputs: [], + outputs: ['main'], + credentials: [ + { + name: 'paypalApi', + 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: [], + }, + ], + }; + + methods = { + loadOptions: { + // Get all the events types to display them to user so that he can + // select them easily + async getEvents(this: ILoadOptionsFunctions): Promise { + const returnData: INodePropertyOptions[] = []; + let events; + try { + const endpoint = '/notifications/webhooks-event-types'; + events = await paypalApiRequest.call(this, endpoint, 'GET'); + } catch (err) { + throw new Error(`PayPal Error: ${err}`); + } + for (const event of events.event_types) { + const eventName = upperFist(event.name); + const eventId = event.name; + const eventDescription = event.description; + + returnData.push({ + name: eventName, + value: eventId, + description: eventDescription, + }); + } + return returnData; + }, + }, + }; + // @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 paypalApiRequest.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(`PayPal 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 paypalApiRequest.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 paypalApiRequest.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 paypalApiRequest.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/Paypal/PaypalTriger.node.ts b/packages/nodes-base/nodes/Paypal/PaypalTriger.node.ts deleted file mode 100644 index 07db20110..000000000 --- a/packages/nodes-base/nodes/Paypal/PaypalTriger.node.ts +++ /dev/null @@ -1,121 +0,0 @@ -import { - IHookFunctions, - IWebhookFunctions, - } from 'n8n-core'; - - import { - IDataObject, - INodeTypeDescription, - INodeType, - IWebhookResponseData, - ILoadOptionsFunctions, - INodePropertyOptions, - } from 'n8n-workflow'; - import { - paypalApiRequest, - } from './GenericFunctions'; - - export class PayPalTrigger implements INodeType { - description: INodeTypeDescription = { - displayName: 'PayPal Trigger', - name: 'PayPal', - icon: 'file:paypal.png', - group: ['trigger'], - version: 1, - description: 'Handle PayPal events via webhooks', - defaults: { - name: 'PayPal Trigger', - color: '#32325d', - }, - inputs: [], - outputs: ['main'], - credentials: [ - { - name: 'paypalApi', - 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' - } - } - ], - }; - - methods = { - loadOptions: { - // Get all the events types to display them to user so that he can - // select them easily - async getEvents(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; - let events; - try { - events = await paypalApiRequest.call(this, '/webhooks-event-types', 'GET'); - } catch (err) { - throw new Error(`PayPal Error: ${err}`); - } - for (const event of events.event_types) { - const eventName = event.name; - const eventId = event.name; - const eventDescription = event.description; - - returnData.push({ - name: eventName, - value: eventId, - description: eventDescription, - }); - } - return returnData; - }, - }, - }; - - // @ts-ignore (because of request) - webhookMethods = { - default: { - async checkExists(this: IHookFunctions): Promise { - const webhookData = this.getWorkflowStaticData('node'); - - - return true; - }, - async delete(this: IHookFunctions): Promise { - const webhookData = this.getWorkflowStaticData('node'); - - - return true; - }, - }, - }; - - async webhook(this: IWebhookFunctions): Promise { - const bodyData = this.getBodyData() as IDataObject; - const req = this.getRequestObject(); - - const events = this.getNodeParameter('events', []) as string[]; - - const eventType = bodyData.type as string | undefined; - - return { - workflowData: [ - this.helpers.returnJsonArray(req.body) - ], - }; - } - } diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 425bd2b59..cde489567 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -110,8 +110,8 @@ "dist/nodes/Pipedrive/Pipedrive.node.js", "dist/nodes/Pipedrive/PipedriveTrigger.node.js", "dist/nodes/Postgres/Postgres.node.js", - "dist/nodes/Paypal/Paypal.node.js", - "dist/nodes/Paypal/PaypalTriger.node.js", + "dist/nodes/Paypal/PayPal.node.js", + "dist/nodes/Paypal/PayPalTrigger.node.js", "dist/nodes/Rocketchat/Rocketchat.node.js", "dist/nodes/ReadBinaryFile.node.js", "dist/nodes/ReadBinaryFiles.node.js", From 246429b09d37a82e0542b1c8f8d995c03476ea38 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Sun, 1 Dec 2019 10:59:11 -0500 Subject: [PATCH 3/4] sync upstream --- .../nodes-base/nodes/PayPal/GenericFunctions.ts | 4 ++-- packages/nodes-base/nodes/PayPal/PayPal.node.ts | 14 +++++++------- .../nodes-base/nodes/Paypal/PayPalTrigger.node.ts | 1 - packages/nodes-base/package.json | 7 +------ 4 files changed, 10 insertions(+), 16 deletions(-) diff --git a/packages/nodes-base/nodes/PayPal/GenericFunctions.ts b/packages/nodes-base/nodes/PayPal/GenericFunctions.ts index 7143b2f61..533046b95 100644 --- a/packages/nodes-base/nodes/PayPal/GenericFunctions.ts +++ b/packages/nodes-base/nodes/PayPal/GenericFunctions.ts @@ -76,7 +76,7 @@ async function getAccessToken(this: IHookFunctions | IExecuteFunctions | IExecut * Make an API request to paginated paypal endpoint * and return all results */ -export async function payPalApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any +export async function paypalApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; @@ -85,7 +85,7 @@ export async function payPalApiRequestAllItems(this: IHookFunctions | IExecuteFu query!.page_size = 1000; do { - responseData = await payPalApiRequest.call(this, endpoint, method, body, query, uri); + responseData = await paypalApiRequest.call(this, endpoint, method, body, query, uri); uri = getNext(responseData.links); returnData.push.apply(returnData, responseData[propertyName]); } while ( diff --git a/packages/nodes-base/nodes/PayPal/PayPal.node.ts b/packages/nodes-base/nodes/PayPal/PayPal.node.ts index 9f664a46e..8071525f7 100644 --- a/packages/nodes-base/nodes/PayPal/PayPal.node.ts +++ b/packages/nodes-base/nodes/PayPal/PayPal.node.ts @@ -22,8 +22,8 @@ import { } from './PaymentInteface'; import { validateJSON, - payPalApiRequest, - payPalApiRequestAllItems + paypalApiRequest, + paypalApiRequestAllItems } from './GenericFunctions'; export class PayPal implements INodeType { @@ -129,7 +129,7 @@ export class PayPal implements INodeType { body.items = itemsJson; } try { - responseData = await payPalApiRequest.call(this, '/payments/payouts', 'POST', body); + responseData = await paypalApiRequest.call(this, '/payments/payouts', 'POST', body); } catch (err) { throw new Error(`PayPal Error: ${JSON.stringify(err)}`); } @@ -139,10 +139,10 @@ export class PayPal implements INodeType { const returnAll = this.getNodeParameter('returnAll', 0) as boolean; try { if (returnAll === true) { - responseData = await payPalApiRequestAllItems.call(this, 'items', `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs); + responseData = await paypalApiRequestAllItems.call(this, 'items', `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs); } else { qs.page_size = this.getNodeParameter('limit', i) as number; - responseData = await payPalApiRequest.call(this, `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs); + responseData = await paypalApiRequest.call(this, `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs); responseData = responseData.items; } } catch (err) { @@ -153,7 +153,7 @@ export class PayPal implements INodeType { if (operation === 'get') { const payoutItemId = this.getNodeParameter('payoutItemId', i) as string; try { - responseData = await payPalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}`, 'GET', {}, qs); + responseData = await paypalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}`, 'GET', {}, qs); } catch (err) { throw new Error(`PayPal Error: ${JSON.stringify(err)}`); } @@ -161,7 +161,7 @@ export class PayPal implements INodeType { if (operation === 'cancel') { const payoutItemId = this.getNodeParameter('payoutItemId', i) as string; try { - responseData = await payPalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}/cancel`, 'POST', {}, qs); + responseData = await paypalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}/cancel`, 'POST', {}, qs); } catch (err) { throw new Error(`PayPal Error: ${JSON.stringify(err)}`); } diff --git a/packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts b/packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts index 6932f60fe..baa07951b 100644 --- a/packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts +++ b/packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts @@ -15,7 +15,6 @@ import { paypalApiRequest, upperFist } from './GenericFunctions'; -import { queryResult } from 'pg-promise'; export class PayPalTrigger implements INodeType { description: INodeTypeDescription = { diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 5500bf35c..6e7726002 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -115,14 +115,9 @@ "dist/nodes/OpenWeatherMap.node.js", "dist/nodes/Pipedrive/Pipedrive.node.js", "dist/nodes/Pipedrive/PipedriveTrigger.node.js", -<<<<<<< HEAD - "dist/nodes/Postgres/Postgres.node.js", - "dist/nodes/Paypal/PayPal.node.js", - "dist/nodes/Paypal/PayPalTrigger.node.js", -======= "dist/nodes/Postgres/Postgres.node.js", "dist/nodes/PayPal/PayPal.node.js", ->>>>>>> master + "dist/nodes/PayPal/PayPalTrigger.node.js", "dist/nodes/Rocketchat/Rocketchat.node.js", "dist/nodes/ReadBinaryFile.node.js", "dist/nodes/ReadBinaryFiles.node.js", From ee8d63f34a5f35ebfdb1a7597454671d791c9ef8 Mon Sep 17 00:00:00 2001 From: Jan Oberhauser Date: Sun, 1 Dec 2019 20:19:38 +0100 Subject: [PATCH 4/4] :zap: Smaller changes and add wildcard-event --- .../nodes/PayPal/GenericFunctions.ts | 27 ++++++++++++------ .../nodes-base/nodes/PayPal/PayPal.node.ts | 14 ++++----- .../{Paypal => PayPal}/PayPalTrigger.node.ts | 23 +++++++++------ packages/nodes-base/nodes/PayPal/paypal.png | Bin 4979 -> 1227 bytes 4 files changed, 40 insertions(+), 24 deletions(-) rename packages/nodes-base/nodes/{Paypal => PayPal}/PayPalTrigger.node.ts (89%) diff --git a/packages/nodes-base/nodes/PayPal/GenericFunctions.ts b/packages/nodes-base/nodes/PayPal/GenericFunctions.ts index 533046b95..b1b5a2091 100644 --- a/packages/nodes-base/nodes/PayPal/GenericFunctions.ts +++ b/packages/nodes-base/nodes/PayPal/GenericFunctions.ts @@ -13,8 +13,8 @@ import { IDataObject, } from 'n8n-workflow'; -export async function paypalApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any - const credentials = this.getCredentials('paypalApi'); +export async function payPalApiRequest(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('payPalApi'); const env = getEnviroment(credentials!.env as string); const tokenInfo = await getAccessToken.call(this); const headerWithAuthentication = Object.assign({ }, @@ -30,7 +30,16 @@ export async function paypalApiRequest(this: IHookFunctions | IExecuteFunctions try { return await this.helpers.request!(options); } catch (error) { - throw error.response.body; + + if (error.response.body) { + let errorMessage = error.response.body.message; + if (error.response.body.details) { + errorMessage += ` - Details: ${JSON.stringify(error.response.body.details)}`; + } + throw new Error(errorMessage); + } + + throw error; } } @@ -38,12 +47,12 @@ function getEnviroment(env: string): string { // @ts-ignore return { 'sanbox': 'https://api.sandbox.paypal.com', - 'live': 'https://api.paypal.com' + 'live': 'https://api.paypal.com', }[env]; } async function getAccessToken(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions | IWebhookFunctions): Promise { // tslint:disable-line:no-any - const credentials = this.getCredentials('paypalApi'); + const credentials = this.getCredentials('payPalApi'); if (credentials === undefined) { throw new Error('No credentials got returned!'); } @@ -66,9 +75,9 @@ async function getAccessToken(this: IHookFunctions | IExecuteFunctions | IExecut const errorMessage = error.response.body.message || error.response.body.Message; if (errorMessage !== undefined) { - throw errorMessage; + throw new Error(errorMessage); } - throw error.response.body; + throw new Error(error.response.body); } } @@ -76,7 +85,7 @@ async function getAccessToken(this: IHookFunctions | IExecuteFunctions | IExecut * Make an API request to paginated paypal endpoint * and return all results */ -export async function paypalApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any +export async function payPalApiRequestAllItems(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query?: IDataObject, uri?: string): Promise { // tslint:disable-line:no-any const returnData: IDataObject[] = []; @@ -85,7 +94,7 @@ export async function paypalApiRequestAllItems(this: IHookFunctions | IExecuteFu query!.page_size = 1000; do { - responseData = await paypalApiRequest.call(this, endpoint, method, body, query, uri); + responseData = await payPalApiRequest.call(this, endpoint, method, body, query, uri); uri = getNext(responseData.links); returnData.push.apply(returnData, responseData[propertyName]); } while ( diff --git a/packages/nodes-base/nodes/PayPal/PayPal.node.ts b/packages/nodes-base/nodes/PayPal/PayPal.node.ts index 8071525f7..2f1f00147 100644 --- a/packages/nodes-base/nodes/PayPal/PayPal.node.ts +++ b/packages/nodes-base/nodes/PayPal/PayPal.node.ts @@ -21,9 +21,9 @@ import { RecipientWallet, } from './PaymentInteface'; import { + payPalApiRequest, + payPalApiRequestAllItems, validateJSON, - paypalApiRequest, - paypalApiRequestAllItems } from './GenericFunctions'; export class PayPal implements INodeType { @@ -129,7 +129,7 @@ export class PayPal implements INodeType { body.items = itemsJson; } try { - responseData = await paypalApiRequest.call(this, '/payments/payouts', 'POST', body); + responseData = await payPalApiRequest.call(this, '/payments/payouts', 'POST', body); } catch (err) { throw new Error(`PayPal Error: ${JSON.stringify(err)}`); } @@ -139,10 +139,10 @@ export class PayPal implements INodeType { const returnAll = this.getNodeParameter('returnAll', 0) as boolean; try { if (returnAll === true) { - responseData = await paypalApiRequestAllItems.call(this, 'items', `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs); + responseData = await payPalApiRequestAllItems.call(this, 'items', `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs); } else { qs.page_size = this.getNodeParameter('limit', i) as number; - responseData = await paypalApiRequest.call(this, `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs); + responseData = await payPalApiRequest.call(this, `/payments/payouts/${payoutBatchId}`, 'GET', {}, qs); responseData = responseData.items; } } catch (err) { @@ -153,7 +153,7 @@ export class PayPal implements INodeType { if (operation === 'get') { const payoutItemId = this.getNodeParameter('payoutItemId', i) as string; try { - responseData = await paypalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}`, 'GET', {}, qs); + responseData = await payPalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}`, 'GET', {}, qs); } catch (err) { throw new Error(`PayPal Error: ${JSON.stringify(err)}`); } @@ -161,7 +161,7 @@ export class PayPal implements INodeType { if (operation === 'cancel') { const payoutItemId = this.getNodeParameter('payoutItemId', i) as string; try { - responseData = await paypalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}/cancel`, 'POST', {}, qs); + responseData = await payPalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}/cancel`, 'POST', {}, qs); } catch (err) { throw new Error(`PayPal Error: ${JSON.stringify(err)}`); } diff --git a/packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts b/packages/nodes-base/nodes/PayPal/PayPalTrigger.node.ts similarity index 89% rename from packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts rename to packages/nodes-base/nodes/PayPal/PayPalTrigger.node.ts index baa07951b..e12d7dd00 100644 --- a/packages/nodes-base/nodes/Paypal/PayPalTrigger.node.ts +++ b/packages/nodes-base/nodes/PayPal/PayPalTrigger.node.ts @@ -12,7 +12,7 @@ import { INodePropertyOptions, } from 'n8n-workflow'; import { - paypalApiRequest, + payPalApiRequest, upperFist } from './GenericFunctions'; @@ -32,7 +32,7 @@ import { outputs: ['main'], credentials: [ { - name: 'paypalApi', + name: 'payPalApi', required: true, } ], @@ -65,11 +65,17 @@ import { // Get all the events types to display them to user so that he can // select them easily async getEvents(this: ILoadOptionsFunctions): Promise { - const returnData: INodePropertyOptions[] = []; + const returnData: INodePropertyOptions[] = [ + { + name: '*', + value: '*', + description: 'Any time any event is triggered (Wildcard Event).', + } + ]; let events; try { const endpoint = '/notifications/webhooks-event-types'; - events = await paypalApiRequest.call(this, endpoint, 'GET'); + events = await payPalApiRequest.call(this, endpoint, 'GET'); } catch (err) { throw new Error(`PayPal Error: ${err}`); } @@ -88,6 +94,7 @@ import { }, }, }; + // @ts-ignore (because of request) webhookMethods = { default: { @@ -99,7 +106,7 @@ import { } const endpoint = `/notifications/webhooks/${webhookData.webhookId}`; try { - await paypalApiRequest.call(this, endpoint, 'GET'); + await payPalApiRequest.call(this, endpoint, 'GET'); } catch (err) { if (err.response && err.response.name === 'INVALID_RESOURCE_ID') { // Webhook does not exist @@ -123,7 +130,7 @@ import { }; const endpoint = '/notifications/webhooks'; try { - webhook = await paypalApiRequest.call(this, endpoint, 'POST', body); + webhook = await payPalApiRequest.call(this, endpoint, 'POST', body); } catch (e) { throw e; } @@ -141,7 +148,7 @@ import { if (webhookData.webhookId !== undefined) { const endpoint = `/notifications/webhooks/${webhookData.webhookId}`; try { - await paypalApiRequest.call(this, endpoint, 'DELETE', {}); + await payPalApiRequest.call(this, endpoint, 'DELETE', {}); } catch (e) { return false; } @@ -175,7 +182,7 @@ import { webhook_event: bodyData, }; try { - webhook = await paypalApiRequest.call(this, endpoint, 'POST', body); + webhook = await payPalApiRequest.call(this, endpoint, 'POST', body); } catch (e) { throw e; } diff --git a/packages/nodes-base/nodes/PayPal/paypal.png b/packages/nodes-base/nodes/PayPal/paypal.png index 01891251ef715748e96d060b917a35afc8a25300..64e121888023dff594148cf0ec2a606fb8edbe94 100644 GIT binary patch literal 1227 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