added create a payout batch

This commit is contained in:
Ricardo Espinoza 2019-11-23 19:36:47 -05:00
parent 52acbe390a
commit 08c95f989c
7 changed files with 611 additions and 11 deletions

View file

@ -4,9 +4,9 @@ import {
} from 'n8n-workflow';
export class PaypalApi implements ICredentialType {
export class PayPalApi implements ICredentialType {
name = 'paypalApi';
displayName = 'Paypal API';
displayName = 'PayPal API';
properties = [
{
displayName: 'Client ID',
@ -20,5 +20,21 @@ export class PaypalApi implements ICredentialType {
type: 'string' as NodePropertyTypes,
default: '',
},
{
displayName: 'Enviroment',
name: 'env',
type: 'options' as NodePropertyTypes,
default: 'live',
options: [
{
name: 'Sanbox',
value: 'sanbox'
},
{
name: 'Live',
value: 'live'
},
]
},
];
}

View file

@ -713,4 +713,4 @@ export const ecomOrderFields = [
default: 100,
description: 'How many results to return.',
},
] as INodeProperties[];
] as INodeProperties[];

View file

@ -0,0 +1,110 @@
import { OptionsWithUri } from 'request';
import {
IExecuteFunctions,
IHookFunctions,
ILoadOptionsFunctions,
IExecuteSingleFunctions,
BINARY_ENCODING
} from 'n8n-core';
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<any> { // 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({ },
{ Authorization: `Bearer ${tokenInfo.access_token}`, 'Content-Type': 'application/json' });
options = {
headers: headerWithAuthentication,
method,
qs: query,
uri: uri || `${env}/v1${endpoint}`,
body,
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
* and return all results
*/
export async function intercomApiRequestAllItems(this: IHookFunctions | IExecuteFunctions, propertyName: string, endpoint: string, method: string, body: any = {}, query: IDataObject = {}): Promise<any> { // tslint:disable-line:no-any
const returnData: IDataObject[] = [];
let responseData;
query.per_page = 60;
let uri: string | undefined;
do {
responseData = await paypalApiRequest.call(this, endpoint, method, body, query, uri);
uri = responseData.pages.next;
returnData.push.apply(returnData, responseData[propertyName]);
} while (
responseData.pages !== undefined &&
responseData.pages.next !== undefined &&
responseData.pages.next !== null
);
return returnData;
}
export function validateJSON(json: string | undefined): any { // tslint:disable-line:no-any
let result;
try {
result = JSON.parse(json!);
} catch (exception) {
result = '';
}
return result;
}

View file

@ -0,0 +1,340 @@
import { INodeProperties } from "n8n-workflow";
export const payoutOpeations = [
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: [
'payout',
],
},
},
options: [
{
name: 'Create',
value: 'create',
description: 'Create a batch payout',
},
{
name: 'Get',
value: 'get',
description: 'Show payout batch details',
},
],
default: 'create',
description: 'The operation to perform.',
},
] as INodeProperties[];
export const payoutFields = [
/* -------------------------------------------------------------------------- */
/* payout:create */
/* -------------------------------------------------------------------------- */
{
displayName: 'Sender Batch ID',
name: 'senderBatchId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: [
'payout',
],
operation: [
'create',
],
},
},
default: '',
description: 'A sender-specified ID number. Tracks the payout in an accounting system.',
},
{
displayName: 'JSON Parameters',
name: 'jsonParameters',
type: 'boolean',
default: false,
description: '',
displayOptions: {
show: {
resource: [
'payout'
],
operation: [
'create',
]
},
},
},
{
displayName: 'Items',
name: 'itemsUi',
placeholder: 'Add Item',
type: 'fixedCollection',
displayOptions: {
show: {
resource: [
'payout',
],
operation: [
'create',
],
jsonParameters: [
false
]
},
},
typeOptions: {
multipleValues: true,
},
default: {},
options: [
{
name: 'itemsValues',
displayName: 'Item',
values: [
{
displayName: 'Recipient Type',
name: 'recipientType',
type: 'options',
options: [
{
name: 'Phone',
value: 'phone',
description: 'The unencrypted phone number',
},
{
name: 'Email',
value: 'email',
description: 'The unencrypted email. Value is a string of up to 127 single-byte characters.',
},
{
name: 'PayPal ID',
value: 'paypalId',
description: 'The encrypted PayPal account number.',
},
],
default: 'email',
description: 'The ID type that identifies the recipient of the payment.',
},
{
displayName: 'Receiver Value',
name: 'receiverValue',
type: 'string',
required: true,
default: '',
description: 'The receiver of the payment. Corresponds to the recipient_type value in the request. Max value of up to 127 single-byte characters.',
},
{
displayName: 'Currency',
name: 'currency',
type: 'options',
options: [
{
name: 'Australian dollar',
value: 'AUD'
},
{
name: 'Brazilian real',
value: 'BRL'
},
{
name: 'Canadian dollar',
value: 'CAD'
},
{
name: 'Czech koruna',
value: 'CZK'
},
{
name: 'Danish krone',
value: 'DKK'
},
{
name: 'Euro',
value: 'EUR'
},
{
name: 'United States dollar',
value: 'USD'
}
],
default: 'USD',
description: 'Currency',
},
{
displayName: 'Amount',
name: 'amount',
type: 'string',
required: true,
default: '',
description: 'The value, which might be',
},
{
displayName: 'Note',
name: 'note',
type: 'string',
required: false,
default: '',
description: 'The sender-specified note for notifications. Supports up to 4000 ASCII characters and 1000 non-ASCII characters.',
},
{
displayName: 'Sender Item ID',
name: 'senderItemId',
type: 'string',
default: '',
description: 'The sender-specified ID number. Tracks the payout in an accounting system.',
},
{
displayName: 'Recipient Wallet',
name: 'recipientWallet',
type: 'options',
options: [
{
name: 'PayPal',
value: 'paypal',
description: 'PayPal Wallet',
},
{
name: 'Venmo',
value: 'venmo',
description: 'Venmo Wallet',
},
],
default: 'paypal',
description: 'The recipient wallet',
},
]
},
],
},
{
displayName: 'Items',
name: 'itemsJson',
type: 'json',
typeOptions: {
alwaysOpenEditWindow: true,
},
description: 'An array of individual payout items.',
displayOptions: {
show: {
resource: [
'payout'
],
operation: [
'create',
],
jsonParameters: [
true,
]
},
},
},
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
default: {},
displayOptions: {
show: {
resource: [
'payout',
],
operation: [
'create',
],
},
},
options: [
{
displayName: 'Email Subject',
name: 'emailSubject',
type: 'string',
default: '',
description: 'The subject line for the email that PayPal sends when payment for a payout item completes. The subject line is the same for all recipients. Value is an alphanumeric string of up to 255 single-byte characters.',
},
{
displayName: 'Email Message',
name: 'emailMessage',
type: 'string',
default: '',
description: 'The email message that PayPal sends when the payout item completes. The message is the same for all recipients.',
},
{
displayName: 'Note',
name: 'note',
type: 'string',
default: '',
description: 'The payouts and item-level notes are concatenated in the email. The maximum combined length of the notes is 1000 characters.',
},
],
},
/* -------------------------------------------------------------------------- */
/* payout:get */
/* -------------------------------------------------------------------------- */
{
displayName: 'Payout Batch Id',
name: 'payoutBatchId',
type: 'string',
required: true,
displayOptions: {
show: {
resource: [
'payout',
],
operation: [
'get',
],
},
},
description: 'The ID of the payout for which to show details.',
},
{
displayName: 'Return All',
name: 'returnAll',
type: 'boolean',
required: false,
displayOptions: {
show: {
resource: [
'payout',
],
operation: [
'get',
],
},
},
description: 'If all results should be returned or only up to a given limit.',
},
{
displayName: 'Limit',
name: 'limit',
type: 'number',
typeOptions: {
maxValue: 1000,
minValue: 1
},
default: 100,
displayOptions: {
show: {
resource: [
'payout',
],
operation: [
'get',
],
returnAll: [
false,
],
},
},
description: 'If all results should be returned or only up to a given limit.',
},
] as INodeProperties[];

View file

@ -0,0 +1,38 @@
import { IDataObject } from "n8n-workflow";
export enum RecipientType {
email = 'EMAIL',
phone = 'PHONE',
paypalId = 'PAYPAL_ID',
}
export enum RecipientWallet {
paypal = 'PAYPAL',
venmo = 'VENMO',
}
export interface IAmount {
currency?: string;
value?: string;
}
export interface ISenderBatchHeader {
sender_batch_id?: string;
email_subject?: string;
email_message?: string;
note?: string;
}
export interface IItem {
recipient_type?: RecipientType;
amount?: IAmount;
note?: string;
receiver?: string;
sender_item_id?: string;
recipient_wallet?: RecipientWallet;
}
export interface IPaymentBatch {
sender_batch_header?: ISenderBatchHeader;
items?: IItem[];
}

View file

@ -8,18 +8,33 @@ import {
INodeExecutionData,
INodeType,
} from 'n8n-workflow';
import {
payoutOpeations,
payoutFields,
} from './PaymentDescription';
import {
IPaymentBatch,
ISenderBatchHeader,
IItem, IAmount,
RecipientType,
RecipientWallet,
} from './PaymentInteface';
import {
validateJSON,
paypalApiRequest,
} from './GenericFunctions';
export class Paypal implements INodeType {
export class PayPal implements INodeType {
description: INodeTypeDescription = {
displayName: 'Paypal',
name: 'Paypal',
displayName: 'PayPal',
name: 'paypal',
icon: 'file:paypal.png',
group: ['output'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Consume Paypal API',
description: 'Consume PayPal API',
defaults: {
name: 'Paypal',
name: 'PayPal',
color: '#356ae6',
},
inputs: ['main'],
@ -30,10 +45,91 @@ export class Paypal implements INodeType {
required: true,
}
],
properties: [],
properties: [
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Payout',
value: 'payout',
description: 'Use the Payouts API to make payments to multiple PayPal or Venmo recipients. The Payouts API is a fast, convenient way to send commissions, rebates, rewards, and general disbursements. You can send up to 15,000 payments per call. If you integrated the Payouts API before September 1, 2017, you receive transaction reports through Mass Payments Reporting.',
},
],
default: 'payout',
description: 'Resource to consume.',
},
...payoutOpeations,
...payoutFields,
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
const items = this.getInputData();
const returnData: IDataObject[] = [];
const length = items.length as unknown as number;
let qs: IDataObject;
let responseData;
for (let i = 0; i < length; i++) {
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
if (resource === 'payout') {
if (operation === 'create') {
const body: IPaymentBatch = {};
const header: ISenderBatchHeader = {};
const jsonActive = this.getNodeParameter('jsonParameters', i) as boolean;
const senderBatchId = this.getNodeParameter('senderBatchId', i) as string;
const additionalFields = this.getNodeParameter('additionalFields', i) as IDataObject;
header.sender_batch_id = senderBatchId;
if (additionalFields.emailSubject) {
header.email_subject = additionalFields.emailSubject as string;
}
if (additionalFields.emailMessage) {
header.email_message = additionalFields.emailMessage as string;
}
if (additionalFields.note) {
header.note = additionalFields.note as string;
}
body.sender_batch_header = header;
if (!jsonActive) {
const payoutItems: IItem[] = [];
const itemsValues = (this.getNodeParameter('itemsUi', i) as IDataObject).itemsValues as IDataObject[];
if (itemsValues && itemsValues.length > 0) {
itemsValues.forEach( o => {
const payoutItem: IItem = {};
const amount: IAmount = {};
amount.currency = o.currency as string;
amount.value = o.receiverValue as string;
payoutItem.amount = amount;
payoutItem.note = o.note as string || '';
payoutItem.receiver = o.receiver as string;
payoutItem.recipient_type = o.recipientType as RecipientType;
payoutItem.recipient_wallet = o.recipientWallet as RecipientWallet;
payoutItem.sender_item_id = o.senderItemId as string || '';
payoutItems.push(payoutItem);
});
body.items = payoutItems;
} else {
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);
} catch (err) {
throw new Error(`Paypal Error: ${JSON.stringify(err)}`);
}
}
}
if (Array.isArray(responseData)) {
returnData.push.apply(returnData, responseData as IDataObject[]);
} else {
returnData.push(responseData as IDataObject);
}
}
return [this.helpers.returnJsonArray({})];
}
}

View file

@ -50,7 +50,7 @@
"dist/credentials/OpenWeatherMapApi.credentials.js",
"dist/credentials/PipedriveApi.credentials.js",
"dist/credentials/Postgres.credentials.js",
"dist/credentials/Paypal.credentials.js",
"dist/credentials/PayPalApi.credentials.js",
"dist/credentials/Redis.credentials.js",
"dist/credentials/RocketchatApi.credentials.js",
"dist/credentials/SlackApi.credentials.js",
@ -110,7 +110,7 @@
"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/Rocketchat/Rocketchat.node.js",
"dist/nodes/ReadBinaryFile.node.js",
"dist/nodes/ReadBinaryFiles.node.js",