UNPKG

n8n-nodes-sahabat-xpert

Version:

n8n community node for Sahabat Xpert - Multi-platform business automation with Bizapp integration

1,039 lines (1,038 loc) 48.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BizappWoocommerceApi = void 0; const n8n_workflow_1 = require("n8n-workflow"); class BizappWoocommerceApi { constructor() { this.methods = { loadOptions: { async getProductSkus() { try { const credentials = await this.getCredentials('sahabatXpertCredentials'); const serviceType = credentials.serviceType; if (serviceType !== 'bizapp') { throw new Error(`Product SKUs can only be fetched when serviceType is 'bizapp', but found '${serviceType}'. Please configure credentials with Bizapp E-commerce service type.`); } const baseUrl = 'https://woo.bizapp.my'; let secretKey = credentials.bizappSecretKey; if (!secretKey.startsWith('-')) { secretKey = '-' + secretKey; } const url = `${baseUrl}/v2/getproductlist/${secretKey}`; const response = await this.helpers.request({ method: 'GET', url, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, json: true, }); if (response && response.productinfo && Array.isArray(response.productinfo)) { return response.productinfo.map((product) => ({ name: `${product.productsku} - ${product.productname}`, value: product.productsku, })); } return []; } catch (error) { return []; } }, }, }; this.description = { displayName: 'Sahabat Xpert', name: 'sahabatXpert', icon: 'file:ecommerce.svg', group: ['transform'], version: 1, description: 'Sahabat Xpert - Multi-service integration for e-commerce, payment, shipping and CRM operations with Bizapp', defaults: { name: 'Sahabat Xpert', }, inputs: ["main"], outputs: ["main"], credentials: [ { name: 'sahabatXpertCredentials', required: true, }, ], properties: [ { displayName: 'Service', name: 'service', type: 'options', noDataExpression: true, options: [ { name: 'E-commerce (Bizapp)', value: 'ecommerce', description: 'Bizapp e-commerce operations', }, { name: 'Payment Processing', value: 'payment', description: 'Payment gateway operations', }, { name: 'Shipping Management', value: 'shipping', description: 'Shipping and logistics operations', }, { name: 'CRM Integration', value: 'crm', description: 'Customer relationship management', }, ], default: 'ecommerce', }, { displayName: 'E-commerce Operation', name: 'ecommerceOperation', type: 'options', noDataExpression: true, options: [ { name: 'Get Product List', value: 'getProductList', description: 'Retrieve list of products from Bizapp API', }, { name: 'Submit Order', value: 'submitOrder', description: 'Submit order to Bizapp API', }, ], default: 'getProductList', displayOptions: { show: { service: ['ecommerce'], }, }, }, { displayName: 'Payment Operation', name: 'paymentOperation', type: 'options', noDataExpression: true, options: [ { name: 'Process Payment', value: 'processPayment', description: 'Process a payment transaction', }, { name: 'Refund Payment', value: 'refundPayment', description: 'Refund a payment transaction', }, { name: 'Check Payment Status', value: 'checkPaymentStatus', description: 'Check the status of a payment', }, ], default: 'processPayment', displayOptions: { show: { service: ['payment'], }, }, }, { displayName: 'Shipping Operation', name: 'shippingOperation', type: 'options', noDataExpression: true, options: [ { name: 'Create Shipment', value: 'createShipment', description: 'Create a new shipment', }, { name: 'Track Shipment', value: 'trackShipment', description: 'Track an existing shipment', }, { name: 'Get Shipping Rates', value: 'getShippingRates', description: 'Get shipping rates for delivery', }, ], default: 'createShipment', displayOptions: { show: { service: ['shipping'], }, }, }, { displayName: 'CRM Operation', name: 'crmOperation', type: 'options', noDataExpression: true, options: [ { name: 'Create Contact', value: 'createContact', description: 'Create a new contact in CRM', }, { name: 'Update Contact', value: 'updateContact', description: 'Update an existing contact', }, { name: 'Get Contact', value: 'getContact', description: 'Retrieve contact information', }, ], default: 'createContact', displayOptions: { show: { service: ['crm'], }, }, }, { displayName: 'SKU Filter', name: 'skuFilter', type: 'string', default: '', placeholder: 'Enter SKU to filter (optional)', description: 'Filter products by specific SKU. Leave empty to get all products.', displayOptions: { show: { ecommerceOperation: ['getProductList'], service: ['ecommerce'], }, }, }, { displayName: 'Include Stock Info', name: 'includeStock', type: 'boolean', default: true, description: 'Whether to include stock information in the response', displayOptions: { show: { ecommerceOperation: ['getProductList'], service: ['ecommerce'], }, }, }, { displayName: 'Customer Name', name: 'customerName', type: 'string', default: '', required: true, placeholder: 'John Doe', description: 'Full name of the customer', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Customer Address', name: 'customerAddress', type: 'string', default: '', required: false, typeOptions: { rows: 3, }, placeholder: '123 Main Street, City, State 12345 (Optional - will auto fill "Tiada alamat" if empty)', description: 'Complete shipping address (optional)', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Customer Phone', name: 'customerPhone', type: 'string', default: '', required: true, placeholder: '+1234567890', description: 'Customer phone number', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Customer Email', name: 'customerEmail', type: 'string', default: '', required: true, placeholder: 'customer@example.com', description: 'Customer email address', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Total Price', name: 'totalPrice', type: 'number', default: 0, required: true, placeholder: '99.99', description: 'Total order amount', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Products', name: 'products', type: 'fixedCollection', typeOptions: { multipleValues: true, }, default: {}, displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, options: [ { name: 'productValues', displayName: 'Product', values: [ { displayName: 'SKU', name: 'sku', type: 'options', typeOptions: { loadOptionsMethod: 'getProductSkus', }, default: '', required: true, description: 'Select product SKU from available products', }, { displayName: 'Quantity', name: 'quantity', type: 'number', default: 1, required: true, placeholder: '1', description: 'Product quantity', }, ], }, ], }, { displayName: 'Shipping Cost', name: 'shippingCost', type: 'number', default: 0, placeholder: '5.99', description: 'Shipping cost (optional)', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Order Notes', name: 'orderNotes', type: 'string', default: '', typeOptions: { rows: 2, }, placeholder: 'Special delivery instructions...', description: 'Additional order notes (optional)', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Order ID', name: 'orderId', type: 'string', default: '', placeholder: '1234567 (Max 7 digits - will auto-generate if empty)', description: 'WooCommerce Order ID (maximum 7 digits). Leave empty to auto-generate.', typeOptions: { maxLength: 7, }, displayOptions: { show: {}, }, }, { displayName: 'Website URL', name: 'websiteUrl', type: 'string', default: '', placeholder: 'https://sahabatxpert.my', description: 'Website URL (optional)', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Payment Gateway Name', name: 'paymentMethod', type: 'string', default: '', placeholder: 'cod, paypal, stripe, fpx, etc.', description: 'Payment gateway name (text input, case insensitive)', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Payment Transaction ID', name: 'paymentTransactionId', type: 'string', default: '', placeholder: 'txn_1234567890', description: 'Payment gateway transaction ID (optional)', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Payment Type', name: 'paymentGatewayId', type: 'options', default: '', options: [ { name: 'Cash on Delivery', value: 'cod', }, { name: 'Online Payment', value: 'online', }, ], description: 'Select payment type (optional)', displayOptions: { show: { ecommerceOperation: ['submitOrder'], service: ['ecommerce'], }, }, }, { displayName: 'Amount', name: 'paymentAmount', type: 'number', default: 0, placeholder: '100.00', description: 'Payment amount', displayOptions: { show: { paymentOperation: ['processPayment', 'refundPayment'], service: ['payment'], }, }, }, { displayName: 'Transaction ID', name: 'transactionId', type: 'string', default: '', placeholder: 'txn_1234567890', description: 'Transaction ID for refund or status check', displayOptions: { show: { paymentOperation: ['refundPayment', 'checkPaymentStatus'], service: ['payment'], }, }, }, { displayName: 'Customer Email', name: 'paymentCustomerEmail', type: 'string', default: '', placeholder: 'customer@example.com', description: 'Customer email for payment', displayOptions: { show: { paymentOperation: ['processPayment'], service: ['payment'], }, }, }, { displayName: 'Sender Address', name: 'senderAddress', type: 'string', default: '', placeholder: 'Sender full address', description: 'Full sender address', displayOptions: { show: { shippingOperation: ['createShipment', 'getShippingRates'], service: ['shipping'], }, }, }, { displayName: 'Recipient Address', name: 'recipientAddress', type: 'string', default: '', placeholder: 'Recipient full address', description: 'Full recipient address', displayOptions: { show: { shippingOperation: ['createShipment', 'getShippingRates'], service: ['shipping'], }, }, }, { displayName: 'Package Weight (kg)', name: 'packageWeight', type: 'number', default: 1, placeholder: '1.5', description: 'Package weight in kilograms', displayOptions: { show: { shippingOperation: ['createShipment', 'getShippingRates'], service: ['shipping'], }, }, }, { displayName: 'Tracking Number', name: 'trackingNumber', type: 'string', default: '', placeholder: 'TRK123456789', description: 'Tracking number for shipment', displayOptions: { show: { shippingOperation: ['trackShipment'], service: ['shipping'], }, }, }, { displayName: 'Contact Name', name: 'contactName', type: 'string', default: '', placeholder: 'John Doe', description: 'Full name of the contact', displayOptions: { show: { crmOperation: ['createContact', 'updateContact'], service: ['crm'], }, }, }, { displayName: 'Contact Email', name: 'contactEmail', type: 'string', default: '', placeholder: 'john@example.com', description: 'Email address of the contact', displayOptions: { show: { crmOperation: ['createContact', 'updateContact'], service: ['crm'], }, }, }, { displayName: 'Contact Phone', name: 'contactPhone', type: 'string', default: '', placeholder: '+60123456789', description: 'Phone number of the contact', displayOptions: { show: { crmOperation: ['createContact', 'updateContact'], service: ['crm'], }, }, }, { displayName: 'Contact ID', name: 'contactId', type: 'string', default: '', placeholder: 'contact_123', description: 'ID of the contact to update or retrieve', displayOptions: { show: { crmOperation: ['updateContact', 'getContact'], service: ['crm'], }, }, }, ], }; } async execute() { const items = this.getInputData(); const returnData = []; for (let i = 0; i < items.length; i++) { try { const credentials = await this.getCredentials('sahabatXpertCredentials'); const service = this.getNodeParameter('service', i); const serviceType = credentials.serviceType; const serviceMapping = { 'ecommerce': 'bizapp', 'payment': 'payment', 'shipping': 'shipping', 'crm': 'crm' }; const expectedServiceType = serviceMapping[service]; if (serviceType !== expectedServiceType) { throw new Error(`Service mismatch: Selected service '${service}' requires serviceType '${expectedServiceType}' in credentials, but found '${serviceType}'`); } let apiKey = ''; let baseUrl = ''; switch (service) { case 'ecommerce': apiKey = credentials.bizappSecretKey; baseUrl = 'https://woo.bizapp.my'; break; case 'payment': apiKey = credentials.paymentApiKey; baseUrl = 'https://api.payment-gateway.com'; break; case 'shipping': apiKey = credentials.shippingToken; baseUrl = 'https://api.shipping-provider.com'; break; case 'crm': apiKey = credentials.crmToken; baseUrl = credentials.crmBaseUrl || 'https://api.crm-system.com'; break; default: throw new Error(`Unsupported service: ${service}`); } const timeout = 30000; if (service === 'ecommerce') { const ecommerceOperation = this.getNodeParameter('ecommerceOperation', i); if (!apiKey.startsWith('-')) { apiKey = '-' + apiKey; } if (ecommerceOperation === 'getProductList') { const skuFilter = this.getNodeParameter('skuFilter', i, ''); const includeStock = this.getNodeParameter('includeStock', i, true); const url = `${baseUrl}/v2/getproductlist/${apiKey}`; const response = await this.helpers.request({ method: 'GET', url, headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', }, timeout: 30000, json: true, }); let filteredData = response; if (skuFilter && response.productinfo) { filteredData = { ...response, productinfo: response.productinfo.filter((product) => product.productsku.toLowerCase().includes(skuFilter.toLowerCase())) }; } returnData.push({ json: { success: true, data: filteredData, timestamp: new Date().toISOString(), api_endpoint: 'getproductlist', sku_filter: skuFilter, include_stock: includeStock, }, pairedItem: { item: i }, }); } else if (ecommerceOperation === 'submitOrder') { const customerName = this.getNodeParameter('customerName', i); let customerAddress = this.getNodeParameter('customerAddress', i); const customerPhone = this.getNodeParameter('customerPhone', i); const customerEmail = this.getNodeParameter('customerEmail', i); const totalPrice = this.getNodeParameter('totalPrice', i); const products = this.getNodeParameter('products', i, {}); const shippingCost = this.getNodeParameter('shippingCost', i, 0); const orderNotes = this.getNodeParameter('orderNotes', i, ''); const orderId = this.getNodeParameter('orderId', i, ''); const websiteUrl = this.getNodeParameter('websiteUrl', i, ''); const paymentMethod = this.getNodeParameter('paymentMethod', i, ''); const paymentTransactionId = this.getNodeParameter('paymentTransactionId', i, ''); const paymentGatewayId = this.getNodeParameter('paymentGatewayId', i, ''); let formattedPhone = customerPhone.replace(/\D/g, ''); if (formattedPhone.startsWith('60')) { formattedPhone = '+' + formattedPhone; } else if (formattedPhone.startsWith('0')) { formattedPhone = '+6' + formattedPhone; } else if (formattedPhone.length >= 9 && formattedPhone.length <= 10) { formattedPhone = '+60' + formattedPhone; } else { formattedPhone = customerPhone; } let formattedEmail = customerEmail.trim().toLowerCase(); if (!formattedEmail.includes('@')) { formattedEmail = formattedEmail + '@gmail.com'; } else if (formattedEmail.endsWith('@')) { formattedEmail = formattedEmail + 'gmail.com'; } else if (!formattedEmail.includes('.')) { formattedEmail = formattedEmail + '.com'; } if (!customerAddress || customerAddress.trim() === '') { customerAddress = 'Tiada alamat'; } let finalOrderId = orderId; if (finalOrderId && finalOrderId.trim() !== '') { const numericOrderId = finalOrderId.replace(/\D/g, ''); if (numericOrderId.length > 7) { throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: `Order ID cannot exceed 7 digits. Provided: ${numericOrderId} (${numericOrderId.length} digits)`, description: 'Please provide an Order ID with maximum 7 numeric digits.', httpCode: '400', }); } finalOrderId = numericOrderId; } if (!finalOrderId || finalOrderId.trim() === '') { finalOrderId = (Math.floor(Math.random() * 9000000) + 1000000).toString(); } const formattedTotalPrice = totalPrice.toFixed(2); const formattedShippingCost = shippingCost.toFixed(2); const formattedPaymentMethod = paymentMethod; const formData = { name: customerName, address: customerAddress, hpno: formattedPhone, email: formattedEmail, sellingprice: formattedTotalPrice, postageprice: formattedShippingCost, note: orderNotes, woo_url: websiteUrl || BizappWoocommerceApi.generateSafeUrl(), woo_orderid: finalOrderId, woo_paymentgateway: formattedPaymentMethod, }; if (paymentTransactionId) { formData.woo_payment_txn = paymentTransactionId; } if (paymentGatewayId) { formData.woo_paymentgateway_id = paymentGatewayId; } if (products.productValues && Array.isArray(products.productValues)) { products.productValues.forEach((product, index) => { formData[`products_info[${index}][sku]`] = product.sku; formData[`products_info[${index}][quantity]`] = product.quantity.toString(); }); } const apiTimestamp = Date.now(); const queryParams = { api_name: 'WOO_TRACK_SAVE_ORDER_MULTIPLE_NEW_BYSKU', secretkey: apiKey, TX: apiTimestamp.toString(), }; const url = `${baseUrl}/v2/wooapi.php?${new URLSearchParams(queryParams).toString()}`; const response = await this.helpers.request({ method: 'POST', url, headers: { 'Content-Type': 'application/x-www-form-urlencoded', }, form: formData, timeout: 30000, json: true, }); const isSuccess = response && response.status === 'success'; const bizappOrderId = response && response.result && response.result[0] ? response.result[0].ID : null; const errorMessage = response && response.error_message ? response.error_message : null; if (!isSuccess) { const errorMsg = errorMessage || 'Order submission failed in Bizapp system'; throw new n8n_workflow_1.NodeApiError(this.getNode(), { message: `Bizapp API Error: ${errorMsg}`, description: 'The order was sent to Bizapp but was not processed successfully. Please check the order details and try again.', httpCode: '400', }); } returnData.push({ json: { success: true, bizapp_status: response.status, bizapp_order_id: bizappOrderId, n8n_order_id: finalOrderId, data: response, timestamp: new Date().toISOString(), api_endpoint: 'wooapi.php', formatted_data: { original_phone: customerPhone, formatted_phone: formattedPhone, original_email: customerEmail, formatted_email: formattedEmail, original_address: this.getNodeParameter('customerAddress', i), final_address: customerAddress, formatted_total_price: formattedTotalPrice, formatted_shipping_cost: formattedShippingCost, }, }, pairedItem: { item: i }, }); } } else if (service === 'payment') { const paymentOperation = this.getNodeParameter('paymentOperation', i); if (paymentOperation === 'processPayment') { const amount = this.getNodeParameter('paymentAmount', i); const customerEmail = this.getNodeParameter('paymentCustomerEmail', i); const paymentData = { amount: amount, currency: 'MYR', customer_email: customerEmail, timestamp: new Date().toISOString(), }; returnData.push({ json: { success: true, service: 'payment', operation: 'processPayment', data: paymentData, transaction_id: `txn_${Date.now()}`, status: 'completed', }, pairedItem: { item: i }, }); } else if (paymentOperation === 'refundPayment') { const amount = this.getNodeParameter('paymentAmount', i); const transactionId = this.getNodeParameter('transactionId', i); returnData.push({ json: { success: true, service: 'payment', operation: 'refundPayment', original_transaction_id: transactionId, refund_amount: amount, refund_id: `ref_${Date.now()}`, status: 'refunded', timestamp: new Date().toISOString(), }, pairedItem: { item: i }, }); } else if (paymentOperation === 'checkPaymentStatus') { const transactionId = this.getNodeParameter('transactionId', i); returnData.push({ json: { success: true, service: 'payment', operation: 'checkPaymentStatus', transaction_id: transactionId, status: 'completed', amount: 100.00, currency: 'MYR', timestamp: new Date().toISOString(), }, pairedItem: { item: i }, }); } } else if (service === 'shipping') { const shippingOperation = this.getNodeParameter('shippingOperation', i); if (shippingOperation === 'createShipment') { const senderAddress = this.getNodeParameter('senderAddress', i); const recipientAddress = this.getNodeParameter('recipientAddress', i); const packageWeight = this.getNodeParameter('packageWeight', i); returnData.push({ json: { success: true, service: 'shipping', operation: 'createShipment', tracking_number: `TRK${Date.now()}`, sender_address: senderAddress, recipient_address: recipientAddress, package_weight: packageWeight, status: 'created', estimated_delivery: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000).toISOString(), timestamp: new Date().toISOString(), }, pairedItem: { item: i }, }); } else if (shippingOperation === 'trackShipment') { const trackingNumber = this.getNodeParameter('trackingNumber', i); returnData.push({ json: { success: true, service: 'shipping', operation: 'trackShipment', tracking_number: trackingNumber, status: 'in_transit', location: 'Kuala Lumpur Distribution Center', estimated_delivery: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toISOString(), timestamp: new Date().toISOString(), }, pairedItem: { item: i }, }); } else if (shippingOperation === 'getShippingRates') { const senderAddress = this.getNodeParameter('senderAddress', i); const recipientAddress = this.getNodeParameter('recipientAddress', i); const packageWeight = this.getNodeParameter('packageWeight', i); returnData.push({ json: { success: true, service: 'shipping', operation: 'getShippingRates', sender_address: senderAddress, recipient_address: recipientAddress, package_weight: packageWeight, rates: [ { service: 'Standard', price: 15.00, delivery_days: '3-5' }, { service: 'Express', price: 25.00, delivery_days: '1-2' }, { service: 'Next Day', price: 35.00, delivery_days: '1' }, ], timestamp: new Date().toISOString(), }, pairedItem: { item: i }, }); } } else if (service === 'crm') { const crmOperation = this.getNodeParameter('crmOperation', i); if (crmOperation === 'createContact') { const contactName = this.getNodeParameter('contactName', i); const contactEmail = this.getNodeParameter('contactEmail', i); const contactPhone = this.getNodeParameter('contactPhone', i); returnData.push({ json: { success: true, service: 'crm', operation: 'createContact', contact_id: `contact_${Date.now()}`, name: contactName, email: contactEmail, phone: contactPhone, status: 'active', created_at: new Date().toISOString(), }, pairedItem: { item: i }, }); } else if (crmOperation === 'updateContact') { const contactId = this.getNodeParameter('contactId', i); const contactName = this.getNodeParameter('contactName', i); const contactEmail = this.getNodeParameter('contactEmail', i); const contactPhone = this.getNodeParameter('contactPhone', i); returnData.push({ json: { success: true, service: 'crm', operation: 'updateContact', contact_id: contactId, name: contactName, email: contactEmail, phone: contactPhone, status: 'updated', updated_at: new Date().toISOString(), }, pairedItem: { item: i }, }); } else if (crmOperation === 'getContact') { const contactId = this.getNodeParameter('contactId', i); returnData.push({ json: { success: true, service: 'crm', operation: 'getContact', contact_id: contactId, name: 'John Doe', email: 'john@example.com', phone: '+60123456789', status: 'active', created_at: '2024-01-01T00:00:00.000Z', last_updated: new Date().toISOString(), }, pairedItem: { item: i }, }); } } else { throw new Error(`Unsupported service: ${service}`); } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error occurred'; if (this.continueOnFail()) { returnData.push({ json: { error: errorMessage, }, pairedItem: { item: i }, }); continue; } throw error; } } return [returnData]; } static generateSafeUrl() { const subdomains = ['shop', 'store', 'market', 'ecom', 'retail', 'sales', 'buy', 'mall']; const domains = ['example.com', 'test.com', 'demo.org', 'sample.net']; const randomSubdomain = subdomains[Math.floor(Math.random() * subdomains.length)]; const randomDomain = domains[Math.floor(Math.random() * domains.length)]; return `https://${randomSubdomain}.${randomDomain}`; } } exports.BizappWoocommerceApi = BizappWoocommerceApi;