Resource Center

Dynamic Tracking - Click ID Capture and Storage: Node.js

 
const express = require('express');
const cookieParser = require('cookie-parser');
const app = express();

const clickIdUrlParameter = 'clickId';
const clickIdCookieName = 'pjn_clicks';
const lookBackPeriodInDays = 60;

const maxCookieAge = lookBackPeriodInDays * 24 * 60 * 60000;

app.use(cookieParser());

const PJNClicksMiddleware = (req, res, next) => {

    /**
     * Returns true if the querystring contains a click ID
     */
    const hasClickId = (req) => {
        return !!req.query[clickIdUrlParameter];
    }

    /**
     * Returns the clickId value
     */
    const getClickIdValue = (req) => {
        return req.query[clickIdUrlParameter];
    }

    /**
     * Updates the cookie
     *
     * If no previous cookie exists, a new one is created.
     * If a cookie does exist, the cookie is 1st pruned
     * to remove any expired values.  If the clickId
     * already exists in the cookie, the operation returns
     * and the new clickId is ignored.
     *
     * The cookie is stored as a string which contains an array
     * of objects.  Each object contains an id:value and a
     * maxAge: value.  On cookie value retrieval, the string
     * is converted to an array of objects.  On storage,
     * the array is converted back to a string.
     */
    const updateCookie = (req, res) => {

        let stringCookieList = getCookieValue(req);
        let parsedCookieList;

        // No previous cookie exists
        if (stringCookieList === undefined) {
            stringCookieList = JSON.stringify([{
                "id": getClickIdValue(req),
                "date": getCurrentTimeStampPlusDays(lookBackPeriodInDays),
            }]);

        // Previous cookie exists, append to it.
        } else {
            // If the ID already exists, do nothing.  Don't update ID's with new dates
            let clickIdValue = getClickIdValue(req);
            parsedCookieList = prune(JSON.parse(stringCookieList))

            if (cookieIdExists(clickIdValue, parsedCookieList)) {
                return;
            }

            // Push new ID onto list
            parsedCookieList.push({
                "id": getClickIdValue(req),
                "date": getCurrentTimeStampPlusDays(lookBackPeriodInDays),
            });
            stringCookieList = JSON.stringify(parsedCookieList);
        }
        // Set the cookie
        res.cookie(clickIdCookieName, stringCookieList, {maxAge: maxCookieAge, encode: String});
    }

    /**
     * Returns the cookie value
     */
    const getCookieValue = (req) => {
        return req.cookies[clickIdCookieName];
    }

    /**
     * Gets the current timestamp in the format YYYYMMDD
     */
    const getCurrentTimeStamp = () => {
        return convertDateToTimeStamp(new Date());
    }

    /**
     * Adds the number of days to now and returns a timestamp
     * in the format YYYYMMDD
     */
    const getCurrentTimeStampPlusDays = (days) => {
        return convertDateToTimeStamp(new Date((new Date()).setDate(new Date().getDate() + days)));
    }

    /**
     * Converts a js date to timestamp in the format YYYYMMDD
     */
    const convertDateToTimeStamp = (date) => {
        return date.toISOString().slice(0, 10).replace(/-/g, "");
    }

    /**
     * Checks if the id exists in the array of objects id field
     */
    const cookieIdExists = (id, data) => {
        let exists = false;
        data.forEach((item) => {
            if (item.id === id) {
                exists = true;
            }
        }, data)
        return exists;
    }

    /**
     * Rebuilds the data list, only keeping those items
     * which have a maxAge that has not yet expired.
     */
    const prune = (data) => {
        pruned = [];
        let current = getCurrentTimeStamp();
        for (let i = 0; i < data.length; i++) {
            if (current < data[i].date) {
                pruned.push(data[i]);
            }
        }
        return pruned;
    }

    // Main execution
    if (hasClickId(req)) {
        updateCookie(req, res);
    }
    next();
}

// use our middleware
app.use(PJNClicksMiddleware);

app.get('/', (req, res) => {
    res.send('<h1>Welcome to our Store!</h1>');
});

app.get('/item1', (req, res) => {
    res.send("<h2>Item 1</h2>");
});

app.get('/thankyou', (req, res) => {
    let program_id = 123,
        transaction_type = 1, // sale
        integration_type = 'DYNAMIC',
        promocodes = [123, 456],
        coupons = [],
        new_to_file = 1,
        amount = 500,
        orderId = 123456,
        order_output = '',
        order_items = {
            "item1":
                {
                    id: 'SKU-922320',
                    quantity: 2,
                    price: 25000.99
                },
            "item2":
                {
                    id: 'SKU-9233',
                    quantity: 1,
                    price: 189.99
                }
        };

    let
        clickData = "";
    cookieValue = req.cookies[clickIdCookieName];
    // If we have a cookie value, convert it into comma separated list
    if (cookieValue !== undefined) {
        parsedCookieList = JSON.parse(cookieValue);
        clickData = (parsedCookieList.map((item) => item.id)).join();
    }

    let x = 1;
    for (var key in order_items) {
        if (order_items.hasOwnProperty(key)) {
            order_output += '&ITEM_ID' + x + '=' + order_items[key].id;
            order_output += '&ITEM_PRICE' + x + '=' + order_items[key].price;
            order_output += '&QUANTITY' + x + '=' + order_items[key].quantity;
            x++;
        }
    }

    let
        output = '<h1>Thank You For Shopping!</h1>';
    output += '<iframe height=1 width=1 src="https://t.pepperjamnetwork.com/track?';
    output += 'PROGRAM_ID=' + program_id;
    output += '&INT=' + integration_type;
    output += '&AMOUNT=' + amount;
    output += '&TYPE=' + transaction_type;
    output += '&ORDER_ID=' + orderId;
    output += '&CLICK_ID=' + clickData;
    output += '&PROMOCODE=' + promocodes.toString();
    output += '&COUPON=' + coupons.toString();
    output += '&NEW_TO_FILE=' + new_to_file;
    output += order_output;
    output += "\"\\>"

    cookie = req.cookies[clickIdCookieName]
    res.send(output);
});

app.listen(3000);