import { IHookFunctions, IWebhookFunctions, } from 'n8n-core'; import { IDataObject, ILoadOptionsFunctions, INodePropertyOptions, INodeType, INodeTypeDescription, IWebhookResponseData, } from 'n8n-workflow'; import { payPalApiRequest, upperFist } from './GenericFunctions'; export class PayPalTrigger implements INodeType { description: INodeTypeDescription = { displayName: 'PayPal Trigger', name: 'payPalTrigger', icon: 'file:paypal.svg', 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[] = [ { 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'); } 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), ], }; } }