@progtechbr/n8n-nodes-rdsme
Version:
Send events through API to RD Station Marketing para Ecommerce
609 lines (567 loc) • 16.8 kB
text/typescript
import {
IExecuteFunctions,
INodeExecutionData,
IDataObject,
INodeType,
INodeTypeDescription,
NodeConnectionType,
} from 'n8n-workflow';
import { ecommerceIdentifiers, orderPlacedIdentifiers, orderExplicitIdentifiers } from './Fields/IdentifierFields'
import { ecommerceLineItems } from './Fields/LineItemsFields';
import { ecommerceShippingFields } from './Fields/ShippingFields';
import { ecommerceTrackingFields } from './Fields/TrackingFields';
import { appendLegalBaseOption } from './Fields/AddicionalFields';
import { buildLineItems } from './Utils/PayloadBuilders';
import { validateEmail } from './Utils/Validations';
import { isEmpty } from 'validator';
import { rdStationApiRequest } from './Utils/GenericFunctions';
import {
ECOMMERCE_EVENT_TYPE_MAP,
EVENT_FAMILY,
LEGAL_BASES_OBJECT,
} from './Utils/GenericConstants';
export class RdsmEcommerce implements INodeType {
description: INodeTypeDescription = {
name: 'rdsmEcommerce',
displayName: 'RD Station Marketing para Ecommerce',
icon: 'file:RdsmEcommerce.svg',
group: ['transform'],
version: 1,
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
description: 'Insert ecommerce events to RD Station Marketing through API',
// usableAsTool: true,
defaults: {
name: 'RD Station Marketing para Ecommerce',
},
inputs: ['main' as NodeConnectionType],
outputs: ['main' as NodeConnectionType],
credentials: [
{
name: 'rdsmEcommerceOAuth2Api',
required: true,
}
],
properties: [
/* Start Default Fields */
{
displayName: 'Resource',
name: 'resource',
type: 'options',
options: [
{
name: 'Ecommerce Event',
value: 'ecommerceEvents',
}
],
default: 'ecommerceEvents',
description: 'Create a new ecommerce event',
noDataExpression: true,
required: true,
},
{
displayName: 'Operation',
name: 'operation',
type: 'options',
displayOptions: {
show: {
resource: ['ecommerceEvents'],
},
},
options: [
{
name: 'New Ecommerce Event',
value: 'newEcommerceEvent',
description: 'Register a new ecommerce event',
action: 'Register a new ecommerce event',
},
],
default: 'newEcommerceEvent',
noDataExpression: true,
},
{
displayName: 'Ecommerce Event Type',
name: 'eventType',
type: 'options',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
},
},
options: [
{
name: 'Cart Abandoned',
value: 'ecommerceCartAbandoned',
description: 'Cart abandoned event',
},
{
name: 'Checkout Started',
value: 'ecommerceCheckoutStarted',
description: 'Checkout started event',
},
{
name: 'Order Cancelled',
value: 'ecommerceOrderCancelled',
description: 'Order cancelled event',
},
{
name: 'Order Fulfilled',
value: 'ecommerceOrderFulfilled',
description: 'Order fulfilled event',
},
{
name: 'Order Paid',
value: 'ecommerceOrderPaid',
description: 'Order paid event',
},
{
name: 'Order Placed',
value: 'ecommerceOrderPlaced',
description: 'Order placed event',
},
{
name: 'Order Refunded',
value: 'ecommerceOrderRefunded',
description: 'Order refunded event',
},
{
name: 'Shipment Delivered',
value: 'ecommerceShipmentDelivered',
description: 'Shipment delivered event',
},
],
default: 'ecommerceCartAbandoned',
description: 'Ecommerce event type for lead conversion',
noDataExpression: true,
required: true,
},
/* End Default Fields */
/* Start Payload Fields */
...ecommerceIdentifiers, // Identifier Fields
{
displayName: 'Lead Email',
name: 'email',
type: 'string',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
},
},
default: '',
placeholder: 'lead@email.com',
description: 'Primary email for the lead',
required: true,
},
{
displayName: 'Lead Name',
name: 'name',
type: 'string',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
},
},
default: '',
description: 'Lead\'s fullname',
},
{
displayName: 'Lead Mobile Phone',
name: 'mobilePhone',
type: 'string',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
},
},
default: '',
description: 'Lead\'s mobile phone',
},
{
displayName: 'Lead Personal Phone',
name: 'personalPhone',
type: 'string',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
},
},
default: '',
description: 'Lead\'s personal phone',
},
{
displayName: 'Checkout URL',
name: 'checkoutUrl',
type: 'string',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
eventType: ['ecommerceCheckoutStarted', 'ecommerceCartAbandoned'],
},
},
default: '',
description: 'Checkout URL - HTTP or HTTPS address',
required: true,
},
{
displayName: 'Order URL',
name: 'orderUrl',
type: 'string',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
eventType: ['ecommerceOrderPlaced', 'ecommerceOrderPaid'],
},
},
default: '',
description: 'Order URL - HTTP or HTTPS address',
},
...orderPlacedIdentifiers, // Order Placed Identifiers
...orderExplicitIdentifiers, // Order Explicit Identifiers
{
displayName: 'Cancel Reason',
name: 'cancelReason',
type: 'options',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
eventType: ['ecommerceOrderCancelled'],
},
},
options: [
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
name: 'Customer changed or cancelled the order',
value: 'customer',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
name: 'Fraud order',
value: 'fraud',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
name: 'Other reason',
value: 'other',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
name: 'Refused payment',
value: 'declined',
},
{
// eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased
name: 'Unavailable items',
value: 'inventory',
},
],
default: 'other',
description: 'Cancellation reason for order cancelled event',
},
{
displayName: 'Currency',
name: 'currency',
type: 'options',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
},
hide: {
eventType: [
'ecommerceOrderCancelled',
'ecommerceOrderFulfilled',
'ecommerceShipmentDelivered',
],
},
},
options: [
{
name: 'ARS',
value: 'ARS',
description: 'Argentine Peso',
},
{
name: 'BRL',
value: 'BRL',
description: 'Brazilian Real',
},
{
name: 'CAD',
value: 'CAD',
description: 'Canadian Dollar',
},
{
name: 'CLP',
value: 'CLP',
description: 'Chilean Peso',
},
{
name: 'COP',
value: 'COP',
description: 'Colombian Peso',
},
{
name: 'EUR',
value: 'EUR',
description: 'Euro',
},
{
name: 'MXN',
value: 'MXN',
description: 'Mexican Peso',
},
{
name: 'PEN',
value: 'PEN',
description: 'Peruvian Nuevo Sol',
},
{
name: 'USD',
value: 'USD',
description: 'United States Dollar',
},
{
name: 'UYU',
value: 'UYU',
description: 'Uruguayan Peso',
},
],
default: 'BRL',
description: 'Currency of the store',
noDataExpression: true,
required: true,
},
{
displayName: 'Price',
name: 'price',
type: 'number',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
},
hide: {
eventType: [
'ecommerceOrderCancelled',
'ecommerceOrderFulfilled',
'ecommerceShipmentDelivered',
],
},
},
default: 0,
typeOptions: {
minValue: 0,
},
description: 'Price of the checkout/order',
required: true,
},
...ecommerceShippingFields, // Shipping Fields
...ecommerceTrackingFields, // Tracking Fields
{
displayName: 'Total Items',
name: 'totalItems',
type: 'number',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
},
hide: {
eventType: [
'ecommerceOrderCancelled',
'ecommerceOrderFulfilled',
'ecommerceShipmentDelivered',
],
},
},
default: 0,
typeOptions: {
minValue: 0,
},
description: 'Total items of products in the checkout',
required: true,
},
...ecommerceLineItems, // LineItems Fields
{
displayName: 'Additional Fields',
name: 'additionalFields',
type: 'collection',
placeholder: 'Add Field',
displayOptions: {
show: {
operation: ['newEcommerceEvent'],
resource: ['ecommerceEvents'],
},
},
default: {},
options: [
{
...appendLegalBaseOption,
description:
'Whether to include the phrase “This message was sent automatically with n8n” to the end of the message',
},
],
},
],
};
async execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]> {
// const items = this.getInputData();
const returnData: INodeExecutionData[] = [];
let responseData;
const resource = this.getNodeParameter('resource', 0) as string;
const operation = this.getNodeParameter('operation', 0) as string;
if (resource == 'ecommerceEvents' && operation == 'newEcommerceEvent') {
const eventType = this.getNodeParameter('eventType', 0) as string;
const eventFamily = EVENT_FAMILY;
const event = ECOMMERCE_EVENT_TYPE_MAP[eventType];
const additionalFields = this.getNodeParameter('additionalFields', 0) as IDataObject;
const appendLegalBase = additionalFields.apppendLegalBase as boolean;
const leadEmail = validateEmail(this, this.getNodeParameter('email', 0), 'Lead Email') as string;
const leadName = (this.getNodeParameter('name', 0) as string).trim();
const mobilePhone = (this.getNodeParameter('mobilePhone', 0 ) as string).trim();
const personalPhone = (this.getNodeParameter('personalPhone', 0 ) as string).trim();
const legalBases = LEGAL_BASES_OBJECT;
let identifier;
let specificPayload: any = {};
const isCartAbandoned = eventType == 'ecommerceCartAbandoned';
const isCheckoutStarted = eventType == 'ecommerceCheckoutStarted';
const isOrderPlaced = eventType == 'ecommerceOrderPlaced';
const isOrderPaid = eventType == 'ecommerceOrderPaid';
const isOrderRefunded = eventType == 'ecommerceOrderRefunded';
const isOrderCancelled = eventType == 'ecommerceOrderCancelled';
const isOrderFulfilled = eventType == 'ecommerceOrderFulfilled';
const isShipmentDelivered = eventType == 'ecommerceShipmentDelivered';
const basePayload: IDataObject = {
email: leadEmail,
...(!isEmpty(leadName) && { name: leadName }),
...(!isEmpty(mobilePhone) && { mobile_phone: mobilePhone }),
...(!isEmpty(personalPhone) && { personal_phone: personalPhone }),
...(appendLegalBase && { legal_bases: legalBases }),
};
if (
isCartAbandoned ||
isCheckoutStarted ||
isOrderPlaced ||
isOrderPaid ||
isOrderRefunded
) {
let lineItems: any;
let lineItemsBuilded: any;
const currency = (this.getNodeParameter('currency', 0 ) as string).trim();
const price = this.getNodeParameter('price', 0) as number;
const totalItems = this.getNodeParameter('totalItems', 0) as number;
if(!isOrderRefunded) {
lineItems = this.getNodeParameter('lineItemsUi', 0);
lineItemsBuilded = buildLineItems(lineItems);
// console.info('lineItemsBuilded', lineItemsBuilded);
}
if (isCartAbandoned || isCheckoutStarted) {
const checkoutUrl = (this.getNodeParameter('checkoutUrl', 0) as string).trim();
specificPayload = {
checkout_url: checkoutUrl,
currency: currency,
price: price,
total_items: totalItems,
line_items: lineItemsBuilded,
};
if(isCartAbandoned) {
identifier = (this.getNodeParameter('identifierCartAbandoned', 0) as string).trim();
} else if(isCheckoutStarted) {
identifier = (this.getNodeParameter('identifierCheckoutStarted', 0) as string).trim();
}
specificPayload.identifier = identifier;
}
if (isOrderPlaced || isOrderPaid || isOrderRefunded) {
identifier = (this.getNodeParameter('identifierOrder', 0) as string).trim();
specificPayload = {
currency: currency,
price: price,
total_items: totalItems,
...(!isOrderRefunded && { line_items: lineItemsBuilded }),
};
if (isOrderPlaced || isOrderPaid) {
const orderUrl = (this.getNodeParameter('orderUrl', 0) as string).trim();
const shippingPrice = this.getNodeParameter('shippingPrice', 0) as number;
const shippingCity = (this.getNodeParameter('shippingCity', 0) as string).trim();
const shippingState = (this.getNodeParameter('shippingState', 0) as string).trim();
const shippingCountry = (this.getNodeParameter('shippingCountry', 0) as string).trim();
if(!isEmpty(orderUrl)) {
specificPayload.order_url = orderUrl;
}
if(shippingPrice > 0) {
specificPayload.shipping_price = shippingPrice;
}
if(!isEmpty(shippingCity)) {
specificPayload.shipping_city = shippingCity;
}
if(!isEmpty(shippingState)) {
specificPayload.shipping_state = shippingState;
}
if(!isEmpty(shippingCountry)) {
specificPayload.shipping_country = shippingCountry;
}
}
if(isOrderPlaced) {
const cartIdentifier = (this.getNodeParameter('cartIdentifier', 0) as string).trim();
const checkoutIdentifier = (this.getNodeParameter('checkoutIdentifier', 0) as string).trim();
if(!isEmpty(cartIdentifier)) {
specificPayload.cart_identifier = cartIdentifier;
}
if(!isEmpty(checkoutIdentifier)) {
specificPayload.checkout_identifier = checkoutIdentifier;
}
}
specificPayload.identifier = identifier;
}
}
if (isOrderCancelled) {
identifier = (this.getNodeParameter('identifierOrder', 0) as string).trim();
const cancelReason = (this.getNodeParameter('cancelReason', 0) as string).trim();
specificPayload = {
identifier,
...(!isEmpty(cancelReason) && { cancel_reason: cancelReason }),
};
}
if (isOrderFulfilled || isShipmentDelivered) {
identifier = (this.getNodeParameter('identifierShipment', 0) as string).trim();
const orderIdentifier = (this.getNodeParameter('orderIdentifierExplicit', 0) as string).trim();
if(isOrderFulfilled) {
const trackingCompany = (this.getNodeParameter('trackingCompany', 0) as string).trim();
const trackingCode = (this.getNodeParameter('trackingCode', 0) as string).trim();
const trackingUrl = (this.getNodeParameter('trackingUrl', 0) as string).trim();
specificPayload = {
...(!isEmpty(trackingCompany) && { tracking_company: trackingCompany }),
...(!isEmpty(trackingCode) && { tracking_code: trackingCode }),
...(!isEmpty(trackingUrl) && { tracking_url: trackingUrl }),
};
}
specificPayload.identifier = identifier;
specificPayload.order_identifier = orderIdentifier;
}
const payload: IDataObject = {
...basePayload,
...specificPayload
};
const body: IDataObject = {
event_type: event,
event_family: eventFamily,
payload: payload,
};
// console.info('body', body);
responseData = await rdStationApiRequest.call(this, 'POST', `/events?event_type=${event}`, body);
const executionData = this.helpers.constructExecutionMetaData(
this.helpers.returnJsonArray(responseData as IDataObject),
{ itemData: { item: 0 } },
);
returnData.push(...executionData);
}
return this.prepareOutputData(returnData);
};
};