From eaa33e0a8df439cb5563d05264b114dc9d286d18 Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Tue, 26 Nov 2019 11:59:27 -0500 Subject: [PATCH] :sparkles: payout resource done --- .../nodes/Paypal/GenericFunctions.ts | 98 ++++++++++--------- .../nodes/Paypal/PaymentDescription.ts | 63 +++++++++++- .../nodes/Paypal/PaymentInteface.ts | 2 +- .../nodes-base/nodes/Paypal/Paypal.node.ts | 44 +++++++-- 4 files changed, 150 insertions(+), 57 deletions(-) diff --git a/packages/nodes-base/nodes/Paypal/GenericFunctions.ts b/packages/nodes-base/nodes/Paypal/GenericFunctions.ts index ef17b3ae07..c70b1de23c 100644 --- a/packages/nodes-base/nodes/Paypal/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Paypal/GenericFunctions.ts @@ -14,49 +14,18 @@ import { 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 const credentials = this.getCredentials('paypalApi'); - let tokenInfo; - if (credentials === undefined) { - throw new Error('No credentials got returned!'); - } - // @ts-ignore - const env = { - 'sanbox': 'https://api.sandbox.paypal.com', - 'live': 'https://api.paypal.com' - }[credentials.env as string]; - - const data = new Buffer(`${credentials.clientId}:${credentials.secret}`).toString(BINARY_ENCODING); - let headerWithAuthentication = Object.assign({}, - { Authorization: `Basic ${data}`, 'Content-Type': 'application/x-www-form-urlencoded' }); - let options: OptionsWithUri = { - headers: headerWithAuthentication, - method, - qs: query, - uri: `${env}/v1/oauth2/token`, - body, - json: true - }; - try { - tokenInfo = 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; - } - headerWithAuthentication = Object.assign({ }, + const env = getEnviroment(credentials!.env as string); + const tokenInfo = await getAccessToken.call(this); + const headerWithAuthentication = Object.assign({ }, { Authorization: `Bearer ${tokenInfo.access_token}`, 'Content-Type': 'application/json' }); - - options = { + const options = { headers: headerWithAuthentication, method, - qs: query, + qs: query || {}, uri: uri || `${env}/v1${endpoint}`, body, json: true }; - try { return await this.helpers.request!(options); } catch (error) { @@ -69,35 +38,74 @@ export async function paypalApiRequest(this: IHookFunctions | IExecuteFunctions } } +function getEnviroment(env: string): string { + // @ts-ignore + return { + 'sanbox': 'https://api.sandbox.paypal.com', + 'live': 'https://api.paypal.com' + }[env]; +} +async function getAccessToken(this: IHookFunctions | IExecuteFunctions | IExecuteSingleFunctions | ILoadOptionsFunctions): Promise { // tslint:disable-line:no-any + const credentials = this.getCredentials('paypalApi'); + if (credentials === undefined) { + throw new Error('No credentials got returned!'); + } + const env = getEnviroment(credentials!.env as string); + const data = Buffer.from(`${credentials!.clientId}:${credentials!.secret}`).toString(BINARY_ENCODING); + const headerWithAuthentication = Object.assign({}, + { Authorization: `Basic ${data}`, 'Content-Type': 'application/x-www-form-urlencoded' }); + const options: OptionsWithUri = { + headers: headerWithAuthentication, + method: 'POST', + form: { + grant_type: 'client_credentials', + }, + uri: `${env}/v1/oauth2/token`, + json: true + }; + 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; + } +} /** - * Make an API request to paginated intercom endpoint + * Make an API request to paginated paypal endpoint * and return all results */ -export async function intercomApiRequestAllItems(this: IHookFunctions | IExecuteFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query: IDataObject = {}): 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[] = []; let responseData; - query.per_page = 60; - - let uri: string | undefined; + query!.page_size = 1000; do { responseData = await paypalApiRequest.call(this, endpoint, method, body, query, uri); - uri = responseData.pages.next; + uri = getNext(responseData.links); returnData.push.apply(returnData, responseData[propertyName]); } while ( - responseData.pages !== undefined && - responseData.pages.next !== undefined && - responseData.pages.next !== null + getNext(responseData.links) !== undefined ); return returnData; } +function getNext(links: IDataObject[]): string | undefined { + for (const link of links) { + if (link.rel === 'next') { + return link.href as string; + } + } + return undefined; +} export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any let result; diff --git a/packages/nodes-base/nodes/Paypal/PaymentDescription.ts b/packages/nodes-base/nodes/Paypal/PaymentDescription.ts index c8cef397db..00c994ddeb 100644 --- a/packages/nodes-base/nodes/Paypal/PaymentDescription.ts +++ b/packages/nodes-base/nodes/Paypal/PaymentDescription.ts @@ -21,8 +21,18 @@ export const payoutOpeations = [ { name: 'Get', value: 'get', + description: 'Show payout item details', + }, + { + name: 'Get All', + value: 'getAll', description: 'Show payout batch details', }, + { + name: 'Delete', + value: 'delete', + description: 'Cancels an unclaimed payout item, by ID.', + }, ], default: 'create', description: 'The operation to perform.', @@ -276,7 +286,7 @@ export const payoutFields = [ }, /* -------------------------------------------------------------------------- */ -/* payout:get */ +/* payout:getAll */ /* -------------------------------------------------------------------------- */ { @@ -290,7 +300,7 @@ export const payoutFields = [ 'payout', ], operation: [ - 'get', + 'getAll', ], }, }, @@ -300,14 +310,14 @@ export const payoutFields = [ displayName: 'Return All', name: 'returnAll', type: 'boolean', - required: false, + default: false, displayOptions: { show: { resource: [ 'payout', ], operation: [ - 'get', + 'getAll', ], }, }, @@ -328,7 +338,7 @@ export const payoutFields = [ 'payout', ], operation: [ - 'get', + 'getAll', ], returnAll: [ false, @@ -337,4 +347,47 @@ export const payoutFields = [ }, description: 'If all results should be returned or only up to a given limit.', }, + +/* -------------------------------------------------------------------------- */ +/* payout:get */ +/* -------------------------------------------------------------------------- */ +{ + displayName: 'Payout Item Id', + name: 'payoutItemId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'payout', + ], + operation: [ + 'get', + ], + }, + }, + description: 'The ID of the payout item for which to show details.', +}, + +/* -------------------------------------------------------------------------- */ +/* payout:delete */ +/* -------------------------------------------------------------------------- */ + +{ + displayName: 'Payout Item Id', + name: 'payoutItemId', + type: 'string', + required: true, + displayOptions: { + show: { + resource: [ + 'payout', + ], + operation: [ + 'delete', + ], + }, + }, + description: 'The ID of the payout item to cancel.', +}, ] as INodeProperties[]; diff --git a/packages/nodes-base/nodes/Paypal/PaymentInteface.ts b/packages/nodes-base/nodes/Paypal/PaymentInteface.ts index 07db0ebd2e..f2d11710a8 100644 --- a/packages/nodes-base/nodes/Paypal/PaymentInteface.ts +++ b/packages/nodes-base/nodes/Paypal/PaymentInteface.ts @@ -13,7 +13,7 @@ export enum RecipientWallet { export interface IAmount { currency?: string; - value?: string; + value?: number; } export interface ISenderBatchHeader { diff --git a/packages/nodes-base/nodes/Paypal/Paypal.node.ts b/packages/nodes-base/nodes/Paypal/Paypal.node.ts index fa29233f48..3f18788a91 100644 --- a/packages/nodes-base/nodes/Paypal/Paypal.node.ts +++ b/packages/nodes-base/nodes/Paypal/Paypal.node.ts @@ -22,6 +22,7 @@ import { import { validateJSON, paypalApiRequest, + paypalApiRequestAllItems } from './GenericFunctions'; export class PayPal implements INodeType { @@ -69,8 +70,8 @@ export class PayPal implements INodeType { const items = this.getInputData(); const returnData: IDataObject[] = []; const length = items.length as unknown as number; - let qs: IDataObject; let responseData; + let qs: IDataObject = {}; for (let i = 0; i < length; i++) { const resource = this.getNodeParameter('resource', 0) as string; const operation = this.getNodeParameter('operation', 0) as string; @@ -100,10 +101,10 @@ export class PayPal implements INodeType { const payoutItem: IItem = {}; const amount: IAmount = {}; amount.currency = o.currency as string; - amount.value = o.receiverValue as string; + amount.value = parseFloat(o.amount as string); payoutItem.amount = amount; payoutItem.note = o.note as string || ''; - payoutItem.receiver = o.receiver as string; + payoutItem.receiver = o.receiverValue as string; payoutItem.recipient_type = o.recipientType as RecipientType; payoutItem.recipient_wallet = o.recipientWallet as RecipientWallet; payoutItem.sender_item_id = o.senderItemId as string || ''; @@ -111,14 +112,45 @@ export class PayPal implements INodeType { }); body.items = payoutItems; } else { - throw new Error('You must have at least one item.') + throw new Error('You must have at least one item.'); } } else { const itemsJson = validateJSON(this.getNodeParameter('itemsJson', i) as string); body.items = itemsJson; } try { - responseData = await paypalApiRequest.call(this, '/payouts', 'POST', body); + responseData = await paypalApiRequest.call(this, '/payments/payouts', 'POST', body); + } catch (err) { + throw new Error(`Paypal Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'get') { + const payoutItemId = this.getNodeParameter('payoutItemId', i) as string; + try { + responseData = await paypalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}`, 'GET', {}, qs); + } catch (err) { + throw new Error(`Paypal Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'getAll') { + const payoutBatchId = this.getNodeParameter('payoutBatchId', i) as string; + const returnAll = this.getNodeParameter('returnAll', 0) as boolean; + try { + if (returnAll === true) { + 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 = responseData.items; + } + } catch (err) { + throw new Error(`Paypal Error: ${JSON.stringify(err)}`); + } + } + if (operation === 'delete') { + const payoutItemId = this.getNodeParameter('payoutItemId', i) as string; + try { + responseData = await paypalApiRequest.call(this,`/payments/payouts-item/${payoutItemId}/cancel`, 'POST', {}, qs); } catch (err) { throw new Error(`Paypal Error: ${JSON.stringify(err)}`); } @@ -130,6 +162,6 @@ export class PayPal implements INodeType { returnData.push(responseData as IDataObject); } } - return [this.helpers.returnJsonArray({})]; + return [this.helpers.returnJsonArray(returnData)]; } }