Build a Custom ROI Calculator on WordPress: From Basic HTML to Advanced SaaS Tool

Build a Custom ROI Calculator on WordPress

Want to build such a tool? 👇

🚀 ROI Calculator SECURE

This is what we’ll build by the end of this tutorial – a professional ROI calculator with a cool interface and a secure interface, all on WordPress!

And the best part? I Will Give you all the code snippets, you just need to copy, test, and enjoy!

Why An ROI Calculator?

I chose an ROI calculator for this tutorial simply because it’s straightforward to understand and implement. The exact same principles apply whether you’re building an calorie counter, BMR calculator or any other calculator.

I’ve built hundreds of custom tools across various industries, including PowerKit, TubeDigest, and PromoterKit.

Today, I’ll show you exactly how to build one from scratch, progressing through 5 levels from basic to a professional SaaS-quality tool.

Level 1: Basic ROI Calculator (Foundation)

Let’s start by building a standalone HTML calculator with core functionality and styling:

<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>ROI Calculator - Level 1</title>
    <style>
        body {
            font-family: 'Arial', sans-serif;
            background-color: #f4f4f4;
            margin: 0;
            padding: 20px;
        }
        
        .calculator-container {
            max-width: 500px;
            margin: 50px auto;
            background: white;
            border-radius: 15px;
            padding: 30px;
            box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
            border: 2px solid #e0e0e0;
            /* Making it look like it's trying to fit in but doesn't belong */
            transform: rotate(-0.5deg);
            transition: all 0.3s ease;
        }
        
        .calculator-container:hover {
            transform: rotate(0deg) scale(1.02);
            box-shadow: 0 15px 40px rgba(0, 0, 0, 0.15);
        }
        
        .calculator-title {
            text-align: center;
            color: #333;
            margin-bottom: 25px;
            font-size: 24px;
            font-weight: bold;
        }
        
        .input-group {
            margin-bottom: 20px;
        }
        
        .input-group label {
            display: block;
            margin-bottom: 8px;
            color: #555;
            font-weight: 600;
        }
        
        .input-group input {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 8px;
            font-size: 16px;
            box-sizing: border-box;
            transition: border-color 0.3s ease;
        }
        
        .input-group input:focus {
            outline: none;
            border-color: #4CAF50;
        }
        
        .calculate-btn {
            width: 100%;
            padding: 15px;
            background: linear-gradient(135deg, #4CAF50, #45a049);
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
            margin-bottom: 20px;
        }
        
        .calculate-btn:hover {
            background: linear-gradient(135deg, #45a049, #3d8b40);
            transform: translateY(-2px);
        }
        
        .result-container {
            background: #f8f9fa;
            border-radius: 8px;
            padding: 20px;
            text-align: center;
            min-height: 60px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        
        .result-text {
            font-size: 20px;
            font-weight: bold;
            color: #333;
        }
        
        .positive {
            color: #4CAF50;
        }
        
        .negative {
            color: #f44336;
        }
        
        .neutral {
            color: #ff9800;
        }
        
        .error {
            color: #f44336;
            font-size: 16px;
        }
    </style>
</head>
<body>
    <div class="calculator-container">
        <h2 class="calculator-title">🚀 ROI Calculator</h2>
        
        <div class="input-group">
            <label for="initial-investment">Initial Investment ($)</label>
            <input type="number" id="initial-investment" placeholder="Enter your initial investment" step="10">
        </div>
        
        <div class="input-group">
            <label for="final-value">Final Value ($)</label>
            <input type="number" id="final-value" placeholder="Enter the final value" step="10">
        </div>
        
        <div class="result-container">
            <div id="result" class="result-text">Enter values to calculate ROI</div>
        </div>
    </div>

    <script>
        function calculateROI() {
            // Get input values
            const initialInvestment = parseFloat(document.getElementById('initial-investment').value);
            const finalValue = parseFloat(document.getElementById('final-value').value);
            const resultElement = document.getElementById('result');
            
            // Validate inputs
            if (isNaN(initialInvestment) || isNaN(finalValue)) {
                resultElement.innerHTML = '<span class="error">Please enter valid numbers</span>';
                return;
            }
            
            if (initialInvestment <= 0) {
                resultElement.innerHTML = '<span class="error">Initial investment must be greater than 0</span>';
                return;
            }
            
            // Calculate ROI
            const gain = finalValue - initialInvestment;
            const roiPercentage = (gain / initialInvestment) * 100;
            
            // Determine color based on ROI
            let colorClass = 'neutral';
            let emoji = '📊';
            
            if (roiPercentage > 0) {
                colorClass = 'positive';
                emoji = '📈';
            } else if (roiPercentage < 0) {
                colorClass = 'negative';
                emoji = '📉';
            }
            
            // Display result
            resultElement.innerHTML = `
                <span class="${colorClass}">
                    ${emoji} ROI: ${roiPercentage.toFixed(2)}%<br>
                    <small style="font-size: 14px;">
                        Gain/Loss: $${gain.toFixed(2)}
                    </small>
                </span>
            `;
        }
        
        // Allow Enter key to calculate
        document.addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                calculateROI();
            }
        });
        
        // Real-time calculation when user types
        document.getElementById('initial-investment').addEventListener('input', calculateROI);
        document.getElementById('final-value').addEventListener('input', calculateROI);
    </script>
</body>
</html>

Key Features in Level 1:

  • Real-time calculation as users type
  • Input validation with clear error messages
  • Dynamic styling based on results

ROI Calculation Logic:

javascriptconst gain = finalValue - initialInvestment;
const roiPercentage = (gain / initialInvestment) * 100;

We’re using the standard formula to calculate the percentage of return on investment.

Testing Level 1:

  1. Open a new page on wordpress
  2. Add a custom HTML block
  3. Paste your code in it, and publish the page
  4. Open the page and test your new tool, which should look like this:

Level 2: WordPress Shortcode Integration

Now we’ll transform our HTML calculator into a reusable WordPress shortcode that can be placed anywhere on your site.

This helps us easily place it in multiple posts and manage it centrally, making it easy to edit the code from one place without needing to visit every page that contains the tool and make updates.

What Changed from Level 1:

We’ll add a universal shortcode that we can use on any page on our website, where we can customize using these two attributes:

  • title: Sets the title text shown at the top.
  • color: Sets the primary color for the button and ROI result.

Additionally, we added a button so the ROI calculation is only shown after you click “Calculate ROI.” This makes the tool more professional.

Complete Level 2 Code:

function roi_calculator_shortcode_2($atts) {
    // Defaults
    $atts = shortcode_atts([
        'title' => 'ROI Calculator',
        'color' => '#4CAF50'
    ], $atts);

    // Sanitize
    $title = esc_html($atts['title']);
    $color = esc_attr($atts['color']);

    ob_start(); ?>
    <style>
        .roi-calculator {
            max-width: 500px;
            margin: 30px auto;
            background: white;
            border-radius: 15px;
            padding: 30px;
            box-shadow: 0 10px 30px rgba(0,0,0,0.1);
            border: 2px solid #e0e0e0;
            transform: rotate(-0.5deg);
            transition: all 0.3s ease;
            font-family: 'Arial', sans-serif;
        }
        .roi-calculator:hover {
            transform: rotate(0deg) scale(1.02);
            box-shadow: 0 15px 40px rgba(0,0,0,0.15);
        }
        .roi-calculator .calculator-title {
            text-align: center;
            color: #333;
            margin-bottom: 25px;
            font-size: 24px;
            font-weight: bold;
        }
        .roi-calculator .input-group {
            margin-bottom: 20px;
        }
        .roi-calculator .input-group label {
            display: block;
            margin-bottom: 8px;
            color: #555;
            font-weight: 600;
        }
        .roi-calculator .input-group input {
            width: 100%;
            padding: 12px;
            border: 2px solid #ddd;
            border-radius: 8px;
            font-size: 16px;
            box-sizing: border-box;
            transition: border-color 0.3s ease;
        }
        .roi-calculator .input-group input:focus {
            outline: none;
            border-color: <?php echo $color; ?>;
        }
        .roi-calculator .calculate-btn {
            width: 100%;
            padding: 15px;
            background: <?php echo $color; ?>;
            color: white;
            border: none;
            border-radius: 8px;
            font-size: 18px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
            margin-bottom: 20px;
        }
        .roi-calculator .calculate-btn:hover {
            filter: brightness(0.9);
            transform: translateY(-2px);
        }
        .roi-calculator .result-container {
            background: #f8f9fa;
            border-radius: 8px;
            padding: 20px;
            text-align: center;
            min-height: 60px;
            display: flex;
            align-items: center;
            justify-content: center;
        }
        .roi-calculator .result-text {
            font-size: 20px;
            font-weight: bold;
            color: #333;
        }
        .roi-calculator .positive {
            color: <?php echo $color; ?>;
        }
        .roi-calculator .negative {
            color: #f44336;
        }
        .roi-calculator .neutral {
            color: #ff9800;
        }
        .roi-calculator .error {
            color: #f44336;
            font-size: 16px;
        }
    </style>
    <div class="roi-calculator">
        <h2 class="calculator-title">🚀 <?php echo $title; ?></h2>
        <div class="input-group">
            <label for="roi-initial">Initial Investment ($)</label>
            <input type="number" id="roi-initial" placeholder="Enter your initial investment" step="10">
        </div>
        <div class="input-group">
            <label for="roi-final">Final Value ($)</label>
            <input type="number" id="roi-final" placeholder="Enter the final value" step="10">
        </div>
        <button class="calculate-btn" id="roi-calc-btn">Calculate ROI</button>
        <div class="result-container">
            <div id="roi-result" class="result-text"></div>
        </div>
    </div>
    <script>
    (function(){
        const initialInput = document.getElementById('roi-initial');
        const finalInput = document.getElementById('roi-final');
        const resultElement = document.getElementById('roi-result');
        const button = document.getElementById('roi-calc-btn');
        
        function calculateROI() {
            const initialInvestment = parseFloat(initialInput.value);
            const finalValue = parseFloat(finalInput.value);

            if (isNaN(initialInvestment) || isNaN(finalValue)) {
                resultElement.innerHTML = '<span class="error">Please enter valid numbers</span>';
                return;
            }
            if (initialInvestment <= 0) {
                resultElement.innerHTML = '<span class="error">Initial investment must be greater than 0</span>';
                return;
            }
            const gain = finalValue - initialInvestment;
            const roiPercentage = (gain / initialInvestment) * 100;

            let colorClass = 'neutral';
            let emoji = '📊';

            if (roiPercentage > 0) {
                colorClass = 'positive';
                emoji = '📈';
            } else if (roiPercentage < 0) {
                colorClass = 'negative';
                emoji = '📉';
            }

            resultElement.innerHTML = `
                <span class="${colorClass}">
                    ${emoji} ROI: ${roiPercentage.toFixed(2)}%<br>
                    <small style="font-size: 14px;">
                        Gain/Loss: $${gain.toFixed(2)}
                    </small>
                </span>
            `;
        }
        // Only show result on button click
        resultElement.innerHTML = '';
        button.addEventListener('click', calculateROI);
        // Clear result if any input changes
        initialInput.addEventListener('input', function(){ resultElement.innerHTML = ''; });
        finalInput.addEventListener('input', function(){ resultElement.innerHTML = ''; });
        // Allow Enter key to trigger calculation
        document.addEventListener('keypress', function(e) {
            if (e.key === 'Enter' && (document.activeElement === initialInput || document.activeElement === finalInput)) {
                calculateROI();
            }
        });
    })();
    </script>
    <?php
    return ob_get_clean();
}
add_shortcode('roi_calculator_2', 'roi_calculator_shortcode_2');

Now, for this code, we’ll need to add it to the backend, where we can manage it from one place. This will be done by installing the Code Snippets plugin and adding the snippet to it, in this way:

  1. Log in to your WordPress admin dashboard
  2. Go to Plugins > Add New
  3. Search for “Code Snippets”

4. Once you find the one above (it should be the first one), click “Install Now” and then “Activate.”

Once installed, you’ll have a new “Snippets” menu item in your WordPress admin, so click on it and add a new snippet:

So, paste the above code in it, and don’t forget to activate the plugin:

Now, to use it, you can go to any page on your website and add the following shortcode to it:

[roi_calculator_2]

And, you can customize it based on each page, where you can change the title and color used in this way:

[roi_calculator_2 title="Investment ROI Tool" color="#2196f3"]

Which will give us something like this:

Level 3: Native-Feeling Design

At this level, we’ll update the styling to make the calculator feel more integrated with your website’s natural design, so that wherever you add it, it blends in with the page, rather than standing out.

What Changed from Level 2:

  1. Cleaner styling: Removed heavy shadows and borders
  2. Inherited typography: Uses your site’s fonts and color scheme
  3. Professional layout: Grid-based design with improved spacing

Complete Level 3 Code:

function roi_calculator_shortcode_3() {

    ob_start(); ?>
    <style>
        .roi-calculator {
            max-width: 430px;
            margin: 36px auto 32px auto;
            background: #fff;
            border-radius: 13px;
            padding: 24px 20px 20px 20px;
            box-shadow: 0 4px 20px 0 rgba(13,36,81,0.07);
            font-family: inherit;
            display: grid;
            gap: 18px;
        }
        .roi-calculator .calculator-title {
            text-align: left;
            color: var(--wp--preset--color--primary, #0d2451);
            font-size: 20px;
            font-weight: 700;
            margin-bottom: 4px;
            letter-spacing: -0.5px;
        }
        .roi-calculator .input-group {
            display: grid;
            gap: 4px;
        }
        .roi-calculator .input-group label {
            color: #23304c;
            font-weight: 600;
            font-size: 15px;
        }
        .roi-calculator .input-group input {
            width: 100%;
            padding: 10px 13px;
            border: none;
            border-radius: 7px;
            font-size: 16px;
            font-family: inherit;
            background: #f7f9fb;
            color: #212a36;
            box-shadow: 0 1px 3px 0 rgba(13,36,81,0.03);
            transition: box-shadow 0.2s, background 0.2s;
        }
        .roi-calculator .input-group input:focus {
            outline: none;
            background: #f1f6fa;
            box-shadow: 0 2px 6px 0 rgba(13,36,81,0.08);
        }
        .roi-calculator .calculate-btn {
            width: 100%;
            padding: 12px 0;
            background: var(--wp--preset--color--primary, #0d2451);
            color: #fff;
            border: none;
            border-radius: 7px;
            font-size: 17px;
            font-weight: 600;
            cursor: pointer;
            letter-spacing: 0.2px;
            transition: background 0.15s, box-shadow 0.15s;
            box-shadow: 0 1px 3px 0 rgba(13,36,81,0.10);
        }
        .roi-calculator .calculate-btn:hover, .roi-calculator .calculate-btn:focus {
            filter: brightness(0.94);
        }
        .roi-calculator .result-container {
            background: transparent;
            border-radius: 5px;
            padding: 10px 1px 6px 1px;
            text-align: left;
            min-height: 38px;
            display: flex;
            align-items: center;
            box-shadow: none;
        }
        .roi-calculator .result-text {
            font-size: 18px;
            font-weight: 600;
            color: #212a36;
            letter-spacing: -0.2px;
        }
        .roi-calculator .positive {
            color: var(--wp--preset--color--primary, #0d2451);
        }
        .roi-calculator .negative {
            color: #e04a3f;
        }
        .roi-calculator .neutral {
            color: #eab200;
        }
        .roi-calculator .error {
            color: #e04a3f;
            font-size: 16px;
            font-weight: 500;
        }
        @media (max-width: 540px) {
            .roi-calculator {
                max-width: 98vw;
                padding: 11px 5vw 12px 5vw;
            }
            .roi-calculator .calculator-title {
                font-size: 17px;
            }
            .roi-calculator .result-text {
                font-size: 16px;
            }
        }
    </style>
    <div class="roi-calculator">
        <div class="calculator-title">🚀 ROI Calculator </div>
        <div class="input-group">
            <label for="roi-initial">Initial Investment ($)</label>
            <input type="number" id="roi-initial" placeholder="E.g. 1000" step="10" autocomplete="off">
        </div>
        <div class="input-group">
            <label for="roi-final">Final Value ($)</label>
            <input type="number" id="roi-final" placeholder="E.g. 1200" step="10" autocomplete="off">
        </div>
        <button class="calculate-btn" id="roi-calc-btn">Calculate ROI</button>
        <div class="result-container">
            <div id="roi-result" class="result-text"></div>
        </div>
    </div>
    <script>
    (function(){
        const initialInput = document.getElementById('roi-initial');
        const finalInput = document.getElementById('roi-final');
        const resultElement = document.getElementById('roi-result');
        const button = document.getElementById('roi-calc-btn');
        
        function calculateROI() {
            const initialInvestment = parseFloat(initialInput.value);
            const finalValue = parseFloat(finalInput.value);

            if (isNaN(initialInvestment) || isNaN(finalValue)) {
                resultElement.innerHTML = '<span class="error">Please enter valid numbers</span>';
                return;
            }
            if (initialInvestment <= 0) {
                resultElement.innerHTML = '<span class="error">Initial investment must be greater than 0</span>';
                return;
            }
            const gain = finalValue - initialInvestment;
            const roiPercentage = (gain / initialInvestment) * 100;

            let colorClass = 'neutral';
            let emoji = '📊';

            if (roiPercentage > 0) {
                colorClass = 'positive';
                emoji = '📈';
            } else if (roiPercentage < 0) {
                colorClass = 'negative';
                emoji = '📉';
            }

            resultElement.innerHTML = `
                <span class="${colorClass}">
                    ${emoji} ROI: ${roiPercentage.toFixed(2)}%<br>
                    <small style="font-size: 14px;">
                        Gain/Loss: $${gain.toFixed(2)}
                    </small>
                </span>
            `;
        }
        // Only show result on button click
        resultElement.innerHTML = '';
        button.addEventListener('click', calculateROI);
        // Clear result if any input changes
        initialInput.addEventListener('input', function(){ resultElement.innerHTML = ''; });
        finalInput.addEventListener('input', function(){ resultElement.innerHTML = ''; });
        // Allow Enter key to trigger calculation
        document.addEventListener('keypress', function(e) {
            if (e.key === 'Enter' && (document.activeElement === initialInput || document.activeElement === finalInput)) {
                calculateROI();
            }
        });
    })();
    </script>
    <?php
    return ob_get_clean();
}

add_shortcode('roi_calculator_3', 'roi_calculator_shortcode_3');

Now, as we did above, we create a new code snippet, paste the code into it, and don’t forget to activate it. And, to use it, also go to any page on your website and add the following shortcode to it:

[roi_calculator_3]

Which will give you a native-feeling tool that fits and blends with your website perfectly, like this:

The tool in your case may look a bit different depeding on your website colors and fonts used.

Level 4: Adding Visual Charts

Visual data enhances user engagement and gives your tool a more professional appearance. In our case, we’ll be adding a doughnut chart that shows the investment breakdown.

We will use Chart.js because it’s lightweight, fast, highly customizable, and well-documented.

What Changed from Level 3:

  1. Dynamic Chart Creation: JavaScript Chart.js function to generate charts
  2. Responsive Layout: Two-column layout with results and chart
  3. Chart Destruction: Properly destroying previous charts to prevent memory leaks

Complete Level 4 Code:

function roi_calculator_shortcode_4() {
    ob_start(); ?>
    <style>
        .roi-calculator {
            background: #fff;
            border-radius: 13px;
            padding: 24px 20px 20px 20px;
            box-shadow: 0 4px 20px 0 rgba(13,36,81,0.07);
            font-family: inherit;
            display: grid;
            gap: 18px;
            max-width: 430px;
            margin: 36px auto 32px auto;
        }
        .roi-calculator .calculator-title {
            text-align: left;
            color: var(--wp--preset--color--primary, #0d2451);
            font-size: 20px;
            font-weight: 700;
            margin-bottom: 4px;
            letter-spacing: -0.5px;
        }
        .roi-calculator .input-group {
            display: grid;
            gap: 4px;
        }
        .roi-calculator .input-group label {
            color: #23304c;
            font-weight: 600;
            font-size: 15px;
        }
        .roi-calculator .input-group input {
            width: 100%;
            padding: 10px 13px;
            border: none;
            border-radius: 7px;
            font-size: 16px;
            font-family: inherit;
            background: #f7f9fb;
            color: #212a36;
            box-shadow: 0 1px 3px 0 rgba(13,36,81,0.03);
            transition: box-shadow 0.2s, background 0.2s;
        }
        .roi-calculator .input-group input:focus {
            outline: none;
            background: #f1f6fa;
            box-shadow: 0 2px 6px 0 rgba(13,36,81,0.08);
        }
        .roi-calculator .calculate-btn {
            width: 100%;
            padding: 12px 0;
            background: var(--wp--preset--color--primary, #0d2451);
            color: #fff;
            border: none;
            border-radius: 7px;
            font-size: 17px;
            font-weight: 600;
            cursor: pointer;
            letter-spacing: 0.2px;
            transition: background 0.15s, box-shadow 0.15s;
            box-shadow: 0 1px 3px 0 rgba(13,36,81,0.10);
        }
        .roi-calculator .calculate-btn:hover, .roi-calculator .calculate-btn:focus {
            filter: brightness(0.94);
        }
        /* Responsive 2-column for result/chart only */
        .roi-calc-result-columns {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 0 24px;
            align-items: flex-start;
            margin-top: 6px;
        }
        @media (max-width: 700px) {
            .roi-calc-result-columns {
                grid-template-columns: 1fr;
                gap: 18px 0;
            }
        }
        .roi-result-section {
            background: transparent;
            border-radius: 5px;
            padding: 10px 1px 6px 1px;
            text-align: left;
            min-height: 38px;
            display: flex;
            align-items: center;
            box-shadow: none;
        }
        .roi-result-section .result-text {
            font-size: 18px;
            font-weight: 600;
            color: #212a36;
            letter-spacing: -0.2px;
        }
        .roi-result-section .positive {
            color: var(--wp--preset--color--primary, #0d2451);
        }
        .roi-result-section .negative {
            color: #e04a3f;
        }
        .roi-result-section .neutral {
            color: #eab200;
        }
        .roi-result-section .error {
            color: #e04a3f;
            font-size: 16px;
            font-weight: 500;
        }
        .roi-calc-pie-wrap {
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 120px;
            width: 100%;
        }
        .roi-calc-pie-wrap canvas {
            width: 120px !important;
            height: 120px !important;
            max-width: 100%;
            background: transparent;
        }
    </style>
    <div class="roi-calculator">
        <div class="calculator-title">🚀 ROI Calculator </div>
        <div class="input-group">
            <label for="roi-initial">Initial Investment ($)</label>
            <input type="number" id="roi-initial" placeholder="E.g. 1000" step="10" autocomplete="off">
        </div>
        <div class="input-group">
            <label for="roi-final">Final Value ($)</label>
            <input type="number" id="roi-final" placeholder="E.g. 1200" step="10" autocomplete="off">
        </div>
        <button class="calculate-btn" id="roi-calc-btn">Calculate ROI</button>
        <div class="roi-calc-result-columns">
            <div class="roi-result-section">
                <div id="roi-result" class="result-text"></div>
            </div>
            <div class="roi-calc-pie-wrap">
                <canvas id="roiPieChart"></canvas>
            </div>
        </div>
    </div>
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
    <script>
    (function(){
        const initialInput = document.getElementById('roi-initial');
        const finalInput = document.getElementById('roi-final');
        const resultElement = document.getElementById('roi-result');
        const button = document.getElementById('roi-calc-btn');
        const pieCtx = document.getElementById('roiPieChart').getContext('2d');
        let roiPieChart = null;

        function getPrimaryColor() {
            let color = getComputedStyle(document.documentElement).getPropertyValue('--wp--preset--color--primary');
            if (!color || !color.trim()) color = '#0d2451';
            return color.trim();
        }

        function calculateROI() {
            const initialInvestment = parseFloat(initialInput.value);
            const finalValue = parseFloat(finalInput.value);

            if (isNaN(initialInvestment) || isNaN(finalValue)) {
                resultElement.innerHTML = '<span class="error">Please enter valid numbers</span>';
                destroyPie();
                return;
            }
            if (initialInvestment <= 0) {
                resultElement.innerHTML = '<span class="error">Initial investment must be greater than 0</span>';
                destroyPie();
                return;
            }
            const gain = finalValue - initialInvestment;
            const roiPercentage = (gain / initialInvestment) * 100;

            let colorClass = 'neutral';
            let emoji = '📊';

            if (roiPercentage > 0) {
                colorClass = 'positive';
                emoji = '📈';
            } else if (roiPercentage < 0) {
                colorClass = 'negative';
                emoji = '📉';
            }

            resultElement.innerHTML = `
                <span class="${colorClass}">
                    ${emoji} ROI: ${roiPercentage.toFixed(2)}%<br>
                    <small style="font-size: 14px;">
                        Gain/Loss: $${gain.toFixed(2)}
                    </small>
                </span>
            `;

            // Pie chart: Initial vs Gain/Loss
            drawPie(initialInvestment, gain);
        }

        function drawPie(initial, gain) {
            destroyPie();
            const brandColor = getPrimaryColor();
            const negColor = '#e04a3f';
            const neutralColor = '#eab200';

            let gainColor = gain > 0 ? brandColor : (gain < 0 ? negColor : neutralColor);

            // For pie chart: Show proportion of Initial and Gain/Loss (abs, so negative gain is still shown)
            const pieData = [
                {label: 'Initial', value: initial, color: '#cfd8e3'},
                {label: 'Gain/Loss', value: Math.abs(gain), color: gainColor}
            ];

            roiPieChart = new Chart(pieCtx, {
                type: 'pie',
                data: {
                    labels: pieData.map(d => d.label),
                    datasets: [{
                        data: pieData.map(d => d.value),
                        backgroundColor: pieData.map(d => d.color),
                        borderWidth: 0
                    }]
                },
                options: {
                    responsive: true,
                    plugins: {
                        legend: {
                            display: true,
                            position: 'bottom',
                            labels: {
                                font: {family: 'inherit'},
                                color: '#23304c',
                                boxWidth: 16,
                            }
                        },
                        tooltip: { enabled: true }
                    }
                }
            });
        }

        function destroyPie() {
            if (roiPieChart) {
                roiPieChart.destroy();
                roiPieChart = null;
            }
            // Clear chart visually
            pieCtx.clearRect(0, 0, pieCtx.canvas.width, pieCtx.canvas.height);
        }

        // Only show result on button click
        resultElement.innerHTML = '';
        destroyPie();
        button.addEventListener('click', calculateROI);
        // Clear result and chart if any input changes
        initialInput.addEventListener('input', function(){ resultElement.innerHTML = ''; destroyPie(); });
        finalInput.addEventListener('input', function(){ resultElement.innerHTML = ''; destroyPie(); });
        // Allow Enter key to trigger calculation
        document.addEventListener('keypress', function(e) {
            if (e.key === 'Enter' && (document.activeElement === initialInput || document.activeElement === finalInput)) {
                calculateROI();
            }
        });
    })();
    </script>
    <?php
    return ob_get_clean();
}
					   
add_shortcode('roi_calculator_4', 'roi_calculator_shortcode_4');

Again, as we did, we create a new code snippet, paste the code into it, and don’t forget to activate it. And, to use it, also go to any page on your website and add the following shortcode to it:

[roi_calculator_4]

Which will give you a professional tool with visual representations like this:

At this point, our calculator is becoming quite sophisticated, but still it only contains the frontend part of the tool it doesnt have any backend processing going on.

Most tools have a frontend-backend structure where the frontend (the design of the tool) communicates with the backend (where all the processing happens) creating a complete tool, like this:

I did this 50+ times with the tools I built over the past 5 years like PromoterKit, TubeDigest, & DefineWise.

If you want to learn more about how you can build professional tools with SaaS-quality features, I advise you to check my WordPress SaaS 2.0 course, where I teach you all that in details.

Level 5: Backend Processing & Security

In our final level, we’ll move from frontend-only calculations to secure backend processing with advanced features and security measures.

We’ll mainly add input sanitization to check all user input before processing, and add a simple Captcha which is a simple old fashioned way for bot detection using simple math questions.

Complete Level 5 Code:

function roi_calculator_shortcode_5() {
    $nonce = wp_create_nonce('wp_rest');
    $uid = 'roi-calc-' . wp_generate_uuid4();
    $a = rand(2, 9);
    $b = rand(2, 9);

    ob_start(); 
    ?>
    <style>
        .roi-calculator {
            background: #fff;
            border-radius: 13px;
            padding: 24px 20px 20px 20px;
            box-shadow: 0 4px 20px 0 rgba(13,36,81,0.07);
            font-family: inherit;
            display: grid;
            gap: 18px;
            max-width: 440px;
            margin: 36px auto 32px auto;
        }
        .roi-calculator .calculator-title {
            display: flex;
            align-items: center;
            gap: 8px;
            color: var(--wp--preset--color--primary, #0d2451);
            font-size: 20px;
            font-weight: 700;
            margin-bottom: 4px;
        }
        .roi-calculator .roi-sec-badge {
            background: #e6f7ef;
            color: #1b7d51;
            font-size: 13px;
            padding: 3px 8px;
            border-radius: 5px;
            font-weight: 600;
            letter-spacing: 0.1em;
            display: inline-flex;
            align-items: center;
            gap: 4px;
            margin-left: auto;
        }
        .roi-calculator .input-group {
            display: grid;
            gap: 7px;
            margin-bottom: 15px;
        }
        .roi-calculator .input-group:last-of-type {
            margin-bottom: 24px;
        }
        .roi-calculator .input-group label {
            color: #23304c;
            font-weight: 600;
            font-size: 15px;
        }
        .roi-calculator .input-group input {
            width: 100%;
            padding: 10px 13px;
            border: none;
            border-radius: 7px;
            font-size: 16px;
            background: #f7f9fb;
            color: #212a36;
            box-shadow: 0 1px 3px 0 rgba(13,36,81,0.03);
            transition: box-shadow 0.2s, background 0.2s;
        }
        .roi-calculator .input-group input:focus {
            outline: none;
            background: #f1f6fa;
            box-shadow: 0 2px 6px 0 rgba(13,36,81,0.08);
        }
        .roi-calculator .calculate-btn {
            width: 100%;
            padding: 12px 0;
            background: var(--wp--preset--color--primary, #0d2451);
            color: #fff;
            border: none;
            border-radius: 7px;
            font-size: 17px;
            font-weight: 600;
            cursor: pointer;
            letter-spacing: 0.2px;
            margin-top: 10px;
            transition: background 0.15s, box-shadow 0.15s;
            box-shadow: 0 1px 3px 0 rgba(13,36,81,0.10);
        }
        .roi-calculator .calculate-btn:disabled {
            background: #7a8da4;
            cursor: not-allowed;
        }
        .roi-calculator .calculate-btn:hover, 
        .roi-calculator .calculate-btn:focus {
            filter: brightness(0.94);
        }
        .roi-calc-result-columns {
            display: grid;
            grid-template-columns: 1fr 1fr;
            gap: 0 24px;
            align-items: flex-start;
            margin-top: 16px;
        }
        @media (max-width:700px) {
            .roi-calc-result-columns { 
                grid-template-columns: 1fr; 
                gap: 18px 0; 
            }
        }
        .roi-result-section {
            padding: 10px 1px 6px 1px;
            text-align: left;
            min-height: 38px;
            display: flex;
            align-items: flex-start;
        }
        .roi-result-section .result-text {
            font-size: 18px;
            font-weight: 600;
            color: #212a36;
            letter-spacing: -0.2px;
            line-height: 1.7;
        }
        .roi-result-section .positive { 
            color: var(--wp--preset--color--primary, #0d2451); 
        }
        .roi-result-section .negative { 
            color: #e04a3f; 
        }
        .roi-result-section .neutral { 
            color: #eab200; 
        }
        .roi-result-section .error { 
            color: #e04a3f; 
            font-size: 16px; 
            font-weight: 500; 
        }
        .roi-calc-pie-wrap {
            display: flex;
            align-items: center;
            justify-content: center;
            min-height: 120px;
            width: 100%;
        }
        .roi-calc-pie-wrap canvas {
            width: 120px !important;
            height: 120px !important;
            max-width: 100%;
            background: transparent;
        }
        .roi-loading { 
            display: inline-block; 
            margin-left: 8px; 
            width: 18px; 
            height: 18px; 
            border: 2.5px solid #e1e4e9; 
            border-radius: 50%; 
            border-top: 2.5px solid var(--wp--preset--color--primary, #0d2451); 
            animation: roi-spin 1s linear infinite; 
            vertical-align: middle;
        }
        @keyframes roi-spin { 
            100% { transform: rotate(360deg); } 
        }
    </style>

    <div class="roi-calculator" id="<?php echo esc_attr($uid); ?>">
        <div class="calculator-title">
            🚀 ROI Calculator
            <span class="roi-sec-badge" title="This calculation is secure">
                <svg width="13" height="13" style="vertical-align:-2px;" fill="none" viewBox="0 0 24 24">
                    <circle cx="12" cy="12" r="12" fill="#1b7d51"/>
                    <path d="M8 13l3 3 5-6" stroke="#fff" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"/>
                </svg> 
                SECURE
            </span>
        </div>

        <form class="roi-calc-form" autocomplete="off">
            <div class="input-group">
                <label for="<?php echo $uid; ?>-initial">Initial Investment ($)</label>
                <input type="number" name="initial" id="<?php echo $uid; ?>-initial" min="0" step="10" required>
            </div>

            <div class="input-group">
                <label for="<?php echo $uid; ?>-final">Final Value ($)</label>
                <input type="number" name="final" id="<?php echo $uid; ?>-final" min="0" step="10" required>
            </div>

            <div class="input-group">
                <label for="<?php echo $uid; ?>-years">Investment Period (years)</label>
                <input type="number" name="years" id="<?php echo $uid; ?>-years" min="0.1" step="0.1" placeholder="E.g. 2" required>
            </div>

            <div class="input-group" style="margin-bottom:18px;">
                <label>Solve: <strong><?php echo $a; ?> + <?php echo $b; ?></strong></label>
                <input type="text" name="captcha" id="<?php echo $uid; ?>-captcha" required autocomplete="off" pattern="\d+">
            </div>

            <input type="hidden" name="roi_captcha" value="<?php echo $a + $b; ?>">
            <input type="hidden" name="roi_uid" value="<?php echo $uid; ?>">
            
            <button type="submit" class="calculate-btn" id="<?php echo $uid; ?>-btn">Calculate ROI</button>
        </form>

        <div class="roi-calc-result-columns">
            <div class="roi-result-section">
                <div id="<?php echo $uid; ?>-result" class="result-text"></div>
            </div>
            <div class="roi-calc-pie-wrap">
                <canvas id="<?php echo $uid; ?>-pie"></canvas>
            </div>
        </div>
    </div>

    <script>
    (function(){
        // Load Chart.js if not already loaded
        if(typeof Chart === "undefined"){
            var s = document.createElement('script');
            s.src = "https://cdn.jsdelivr.net/npm/chart.js";
            document.head.appendChild(s);
        }
        
        const uid = <?php echo json_encode($uid); ?>;
        const form = document.querySelector('#' + uid + ' .roi-calc-form');
        const btn = document.getElementById(uid + '-btn');
        const resultEl = document.getElementById(uid + '-result');
        const pieCtx = document.getElementById(uid + '-pie').getContext('2d');
        let pieChart = null;

        function getPrimaryColor(){
            let color = getComputedStyle(document.documentElement).getPropertyValue('--wp--preset--color--primary');
            if(!color || !color.trim()) color = '#0d2451';
            return color.trim();
        }
        
        function destroyPie(){
            if(pieChart){
                pieChart.destroy();
                pieChart = null;
            }
            pieCtx.clearRect(0, 0, pieCtx.canvas.width, pieCtx.canvas.height);
        }
        
        function validateInputs(initial, final, years, captcha) {
            if(isNaN(initial) || isNaN(final) || isNaN(years) || !captcha){
                return 'Please fill all fields correctly.';
            }
            if(years <= 0){
                return 'Years must be more than 0.';
            }
            if(initial <= 0){
                return 'Initial investment must be greater than 0.';
            }
            return null;
        }

        function showError(message) {
            resultEl.innerHTML = '<span class="error">' + message + '</span>';
            btn.disabled = false;
        }

        function processFormData() {
            const initial = parseFloat(form.initial.value.replace(/[^\d.]/g, ""));
            const final = parseFloat(form.final.value.replace(/[^\d.]/g, ""));
            const years = parseFloat(form.years.value.replace(/[^\d.]/g, ""));
            const captcha = form.captcha.value.trim();
            const captchaExpected = form.roi_captcha.value.trim();
            const calc_uid = form.roi_uid.value;

            return { initial, final, years, captcha, captchaExpected, calc_uid };
        }

        function createPieChart(data) {
            pieChart = new Chart(pieCtx, {
                type: 'pie',
                data: {
                    labels: ['Initial', 'Gain/Loss'],
                    datasets: [{
                        data: [data.initial, Math.abs(data.gain)],
                        backgroundColor: [
                            '#cfd8e3',
                            data.gain >= 0 ? getPrimaryColor() : '#e04a3f'
                        ],
                        borderWidth: 0
                    }]
                },
                options: {
                    responsive: true,
                    plugins: {
                        legend: {
                            display: true,
                            position: 'bottom',
                            labels: {
                                font: { family: 'inherit' },
                                color: '#23304c',
                                boxWidth: 16
                            }
                        },
                        tooltip: { enabled: true }
                    }
                }
            });
        }
        
        form.addEventListener('submit', function(e){
            e.preventDefault();
            resultEl.innerHTML = 'Processing... <span class="roi-loading"></span>';
            destroyPie();
            btn.disabled = true;
            
            const formData = processFormData();
            const validationError = validateInputs(formData.initial, formData.final, formData.years, formData.captcha);
            
            if(validationError) {
                showError(validationError);
                return;
            }
            
            fetch('<?php echo esc_url(rest_url('blog-post-ai-tools/v1/calculate-roi')); ?>', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                    'X-WP-Nonce': '<?php echo esc_js($nonce); ?>'
                },
                body: JSON.stringify({
                    initial: formData.initial,
                    final: formData.final,
                    years: formData.years,
                    captcha: formData.captcha,
                    captcha_expected: formData.captchaExpected,
                    roi_uid: formData.calc_uid
                })
            })
            .then(response => response.json())
            .then(data => {
                btn.disabled = false;
                if(data.success !== false && !data.code){
                    resultEl.innerHTML = data.html;
                    createPieChart(data);
                } else {
                    showError(data.message || 'Calculation failed');
                    destroyPie();
                }
            })
            .catch(error => {
                showError('Unexpected error. Please try again.');
                destroyPie();
                console.error('Error:', error);
            });
        });
    })();
    </script>
    <?php
    return ob_get_clean();
}

function roi_calculator_rest_handler_5($request) {
    // Verify nonce
    if (!wp_verify_nonce($request->get_header('X-WP-Nonce'), 'wp_rest')) {
        return new WP_Error('invalid_nonce', 'Security check failed.', array('status' => 403));
    }
    
    // Get parameters
    $initial = $request->get_param('initial');
    $final = $request->get_param('final');
    $years = $request->get_param('years');
    $captcha = $request->get_param('captcha');
    $captcha_expected = $request->get_param('captcha_expected');
    $uid = $request->get_param('roi_uid');
    
    // Validate captcha
    if($captcha !== $captcha_expected) {
        return new WP_Error('captcha_failed', 'Captcha incorrect. Try again.', array('status' => 400));
    }
    
    // Additional validation
    if($initial <= 0) {
        return new WP_Error('invalid_initial', 'Initial investment must be greater than 0.', array('status' => 400));
    }
    
    if($final < 0) {
        return new WP_Error('invalid_final', 'Final value cannot be negative.', array('status' => 400));
    }
    
    if($years <= 0) {
        return new WP_Error('invalid_years', 'Investment period must be greater than 0.', array('status' => 400));
    }
    
    // Calculate ROI
    $gain = $final - $initial;
    $roi_pct = $initial > 0 ? (($gain / $initial) * 100) : 0;
    
    // Generate HTML response
    ob_start();
    ?>
    <span class="<?php echo $roi_pct >= 0 ? 'positive' : 'negative'; ?>">
        <?php echo $roi_pct >= 0 ? '📈' : '📉'; ?> ROI: <?php echo round($roi_pct, 2); ?>%
    </span>
    <div style="font-size: 15px; color:#444; margin: 7px 0 0 0; line-height:1.8;">
        Gain/Loss: <b>$<?php echo number_format($gain, 2); ?></b>
    </div>
    <?php
    $html = ob_get_clean();
    
    return array(
        'html' => $html,
        'initial' => $initial,
        'gain' => $gain,
        'roi_percent' => $roi_pct
    );
}

add_shortcode('roi_calculator_5', 'roi_calculator_shortcode_5');

add_action('rest_api_init', function() {
    register_rest_route('blog-post-ai-tools/v1', '/calculate-roi', array(
        'methods' => 'POST',
        'callback' => 'roi_calculator_rest_handler_5',
        'permission_callback' => function() {
            return true; // Public access with verification in callback
        },
        'args' => array(
            'initial' => array(
                'required' => true,
                'type' => 'number',
                'validate_callback' => function($param) {
                    return is_numeric($param) && $param > 0;
                }
            ),
            'final' => array(
                'required' => true,
                'type' => 'number',
                'validate_callback' => function($param) {
                    return is_numeric($param) && $param >= 0;
                }
            ),
            'years' => array(
                'required' => true,
                'type' => 'number',
                'validate_callback' => function($param) {
                    return is_numeric($param) && $param > 0;
                }
            ),
            'captcha' => array(
                'required' => true,
                'type' => 'string',
                'sanitize_callback' => function($param) {
                    return trim(preg_replace('/[^\d]/', '', $param));
                }
            ),
            'captcha_expected' => array(
                'required' => true,
                'type' => 'string',
                'sanitize_callback' => function($param) {
                    return trim(preg_replace('/[^\d]/', '', $param));
                }
            ),
            'roi_uid' => array(
                'required' => true,
                'type' => 'string',
                'sanitize_callback' => 'sanitize_text_field'
            )
        )
    ));
});

Again, to use it we redo all the steps we did above, create a new code snippet, paste the code into it, and don’t forget to activate it.

And, to use it, also go to any page on your website and add the following shortcode to it:

[roi_calculator_5]

Which will give you a professional tool as the one I showed you in the beginning.

If you want to learn more about adding advanced Google Recaptcha, you can go over the WordPress SaaS 2.0 course where i show in details how you can secure your tool using Recaptcha and build a production-ready tool.


This is where things get really exciting. You now have a SaaS-quality ROI tool that can:

  • Hide complex business logic from users
  • Integrate with third-party APIs securely
  • Implement advanced security measures
  • Scalable to handle hundreds of calculations

Want to Build More Advanced Tools?

That’s exactly what I teach in my WordPress SaaS 2.0 course.

We build complete SaaS applications from scratch, including the full frontend-backend configuration to have production-ready tools.


Remember: The best tools solve real problems for real people. So, do good research and find where you audience’s pain points are, and solve them using these tools!

Keep building, keep learning, and most importantly – keep helping people with your tools 🙂

Related Articles