Cart Dynamic Discount

Adds a progressive discount based on the shopping cart.

Display estimated delivery date
PHP
/**
 * Snippet Name:     Cart Dynamic Discount
 * Snippet Author:   coding-bunny.com
 * Description:      Adds a progressive discount based on the shopping cart.
 */

// === ADD DISCOUNT HERE ===
function cbpds_get_discount_tiers() {
    return [
        50  => 5,   // Example: €50 = 5% discount
        150 => 10,
        250 => 15,
        500 => 25
    ];
}

add_action('woocommerce_cart_calculate_fees', 'cbpds_apply_cart_discount');
function cbpds_apply_cart_discount() {
    if (is_admin() && !defined('DOING_AJAX')) return;
    if (!WC()->cart) return;
    
    $cart_total = WC()->cart->get_subtotal();
    $discount_percentage = cbpds_get_discount_percentage($cart_total);
    
    if ($discount_percentage > 0) {
        $discount_amount = ($cart_total * $discount_percentage) / 100;
        $discount_label = sprintf('Discount %d%% (-€%.2f)', $discount_percentage, $discount_amount);
        
        WC()->cart->add_fee($discount_label, -$discount_amount);
    }
}

function cbpds_get_discount_percentage($total) {
    $discount = 0;
    foreach (cbpds_get_discount_tiers() as $threshold => $percentage) {
        if ($total >= $threshold) {
            $discount = $percentage;
        }
    }
    return $discount;
}

add_action('wp_ajax_cbpds_update_progress', 'cbpds_ajax_update_progress');
add_action('wp_ajax_nopriv_cbpds_update_progress', 'cbpds_ajax_update_progress');

function cbpds_ajax_update_progress() {
    if (!wp_verify_nonce($_POST['nonce'], 'cbpds_nonce')) {
        wp_die('Security check failed');
    }
    
    $cart_total = WC()->cart ? WC()->cart->get_subtotal() : 0;
    $progress_data = cbpds_calculate_progress($cart_total);
    $tiers = cbpds_get_discount_tiers();
    
    ob_start();
    ?>
    <div class="cbpds-minimal-steps">
        <?php foreach ($tiers as $tier => $discount): 
            $is_achieved = $cart_total >= $tier;
            $is_current = !$is_achieved && $progress_data['next_tier'] == $tier;
        ?>
            <div class="cbpds-minimal-step <?php echo $is_achieved ? 'achieved' : ($is_current ? 'current' : ''); ?>">
                <div class="cbpds-spend-row">
                    <span class="cbpds-label">Spend</span>
                    <span class="cbpds-amount"><?php echo $tier; ?></span>
                </div>
                <div class="cbpds-discount-row">
                    <span class="cbpds-label">Discount</span>
                    <span class="cbpds-percent"><?php echo $discount; ?>%</span>
                </div>
                <?php if ($is_achieved): ?>
                    <div class="cbpds-minimal-check"></div>
                <?php endif; ?>
            </div>
        <?php endforeach; ?>
    </div>
    
    <?php if ($progress_data['next_tier']): ?>
        <div class="cbpds-minimal-progress">
            <div class="cbpds-minimal-bar">
                <div class="cbpds-minimal-fill" style="width: <?php echo $progress_data['progress_percentage']; ?>%"></div>
            </div>
            <div class="cbpds-minimal-text">
                <?php if ($progress_data['remaining_amount'] > 0): ?>
<?php echo number_format($progress_data['remaining_amount'], 2); ?> remaining for <?php echo $tiers[$progress_data['next_tier']]; ?>% discount
                <?php endif; ?>
            </div>
        </div>
    <?php else: ?>
        <div class="cbpds-minimal-complete">
            Maximum discount reached
        </div>
    <?php endif; ?>
    <?php
    
    $html = ob_get_clean();
    
    wp_send_json_success([
        'html' => $html,
        'cart_total' => $cart_total,
        'current_discount' => $progress_data['current_discount']
    ]);
}

// === ADD HERE WHERE TO SHOW IT ===
add_action('woocommerce_after_add_to_cart_button', 'cbpds_show_minimal_progress_bar', 25);
add_action('woocommerce_before_cart_table', 'cbpds_show_minimal_progress_bar');
add_action('woocommerce_review_order_before_payment', 'cbpds_show_minimal_progress_bar');

function cbpds_show_minimal_progress_bar() {
    $cart_total = WC()->cart ? WC()->cart->get_subtotal() : 0;
    $progress_data = cbpds_calculate_progress($cart_total);
    $tiers = cbpds_get_discount_tiers();
    
    ?>
    <div class="cbpds-minimal-container" id="cbpds-progress-container">
        
        <div class="cbpds-minimal-steps">
            <?php foreach ($tiers as $tier => $discount): 
                $is_achieved = $cart_total >= $tier;
                $is_current = !$is_achieved && $progress_data['next_tier'] == $tier;
            ?>
                <div class="cbpds-minimal-step <?php echo $is_achieved ? 'achieved' : ($is_current ? 'current' : ''); ?>">
                    <div class="cbpds-spend-row">
                        <span class="cbpds-label">Spend</span>
                        <span class="cbpds-amount"><?php echo $tier; ?></span>
                    </div>
                    <div class="cbpds-discount-row">
                        <span class="cbpds-label">Discount</span>
                        <span class="cbpds-percent"><?php echo $discount; ?>%</span>
                    </div>
                    <?php if ($is_achieved): ?>
                        <div class="cbpds-minimal-check"></div>
                    <?php endif; ?>
                </div>
            <?php endforeach; ?>
        </div>
        
        <?php if ($progress_data['next_tier']): ?>
            <div class="cbpds-minimal-progress">
                <div class="cbpds-minimal-bar">
                    <div class="cbpds-minimal-fill" style="width: <?php echo $progress_data['progress_percentage']; ?>%"></div>
                </div>
                <div class="cbpds-minimal-text">
                    <?php if ($progress_data['remaining_amount'] > 0): ?>
<?php echo number_format($progress_data['remaining_amount'], 2); ?> remaining for <?php echo $tiers[$progress_data['next_tier']]; ?>% discount
                    <?php endif; ?>
                </div>
            </div>
        <?php else: ?>
            <div class="cbpds-minimal-complete">
                Maximum discount reached
            </div>
        <?php endif; ?>
        
    </div>
    
    <style>
    .cbpds-minimal-container {
        background: #f8f9fa;
        border: 1px solid #e9ecef;
        border-radius: 6px;
        padding: 16px;
        margin: 16px 0;
        font-size: 14px;
        color: #495057;
    }
    
    .cbpds-minimal-steps {
        display: flex;
        gap: 8px;
        margin-bottom: 12px;
        flex-wrap: wrap;
    }
    
    .cbpds-minimal-step {
        flex: 1;
        min-width: 100px;
        text-align: center;
        padding: 12px 8px;
        border-radius: 4px;
        position: relative;
        transition: all 0.2s ease;
        display: flex;
        flex-direction: column;
        gap: 6px;
    }
    
    .cbpds-minimal-step:not(.achieved):not(.current) {
        background: #f8f9fa;
        border: 1px solid #dee2e6;
        color: #6c757d;
    }
    
    .cbpds-minimal-step.current {
        background: #fff3cd;
        border: 1px solid #ffeaa7;
        color: #856404;
    }
    
    .cbpds-minimal-step.achieved {
        background: #d4edda;
        border: 1px solid #c3e6cb;
        color: #155724;
    }
    
    .cbpds-spend-row,
    .cbpds-discount-row {
        display: flex;
        align-items: center;
        justify-content: center;
        gap: 6px;
    }
    
    .cbpds-label {
        font-size: 10px;
        font-weight: 500;
        text-transform: uppercase;
        letter-spacing: 0.5px;
        opacity: 0.7;
        white-space: nowrap;
    }
    
    .cbpds-amount,
    .cbpds-percent {
        font-size: 12px;
        font-weight: 700;
    }
       
    .cbpds-minimal-check {
        position: absolute;
        top: -2px;
        right: -2px;
        background: #28a745;
        color: white;
        width: 16px;
        height: 16px;
        border-radius: 50%;
        font-size: 10px;
        display: flex;
        align-items: center;
        justify-content: center;
        line-height: 1;
    }
    
    .cbpds-minimal-progress {
        margin-top: 12px;
    }
    
    .cbpds-minimal-bar {
        background: #e9ecef;
        border-radius: 3px;
        height: 6px;
        overflow: hidden;
        margin-bottom: 6px;
    }
    
    .cbpds-minimal-fill {
        background: #29a745;
        height: 100%;
        border-radius: 3px;
        transition: width 0.3s ease;
    }
    
    .cbpds-minimal-text {
        text-align: center;
        font-size: 12px;
        color: #6c757d;
        font-weight: 500;
    }
    
    .cbpds-minimal-complete {
        background: #d4edda;
        color: #155724;
        padding: 10px 12px;
        border-radius: 4px;
        text-align: center;
        font-weight: 500;
        border: 1px solid #c3e6cb;
        font-size: 13px;
    }
    
    @media (max-width: 768px) {
        .cbpds-minimal-container {
            padding: 12px;
            font-size: 13px;
        }
        
        .cbpds-minimal-steps {
            flex-direction: column;
            gap: 6px;
        }
        
        .cbpds-minimal-step {
            display: flex;
            flex-direction: row;
            justify-content: space-between;
            align-items: center;
            padding: 12px;
            text-align: left;
            min-width: auto;
            gap: 8px;
        }
        
        .cbpds-spend-row,
        .cbpds-discount-row {
            justify-content: flex-start;
        }
        
        .cbpds-spend-row {
            order: 1;
        }
        
        .cbpds-discount-row {
            order: 2;
        }
        
        .cbpds-minimal-check {
            position: static;
            order: 3;
            margin-left: auto;
        }
    }
    
    @media (max-width: 480px) {
        .cbpds-minimal-container {
            margin: 12px 0;
            padding: 10px;
        }
        
        .cbpds-amount {
            font-size: 12px;
        }
        
        .cbpds-percent {
            font-size: 14px;
        }
        
        .cbpds-label {
            font-size: 9px;
        }
    }
    </style>
    
    <script>
    jQuery(document).ready(function($) {
        var cbpds_nonce = '<?php echo wp_create_nonce('cbpds_nonce'); ?>';
        var cbpds_ajax_url = '<?php echo admin_url('admin-ajax.php'); ?>';
        var cbpds_updating = false;
        
        function cbpds_update_progress() {
            if (cbpds_updating) return;
            cbpds_updating = true;
            
            var $container = $('#cbpds-progress-container');
            
            $.post(cbpds_ajax_url, {
                action: 'cbpds_update_progress',
                nonce: cbpds_nonce
            })
            .done(function(response) {
                if (response.success) {
                    $container.html(response.data.html);
                    cbpds_bind_hover_events();
                }
            })
            .fail(function() {
                console.log('CBPDS: Error updating progress bar');
            })
            .always(function() {
                cbpds_updating = false;
            });
        }
        
        function cbpds_bind_hover_events() {
            $('.cbpds-minimal-step').off('mouseenter mouseleave').hover(
                function() {
                    if (!$(this).hasClass('achieved')) {
                        $(this).css('transform', 'translateY(-1px)');
                    }
                },
                function() {
                    $(this).css('transform', 'translateY(0)');
                }
            );
        }
        
        cbpds_bind_hover_events();
        
        $(document.body).on('added_to_cart removed_from_cart updated_cart_totals updated_checkout', function() {
            setTimeout(cbpds_update_progress, 100);
        });
        
        $(document.body).on('wc_fragments_refreshed wc_fragments_loaded', function() {
            setTimeout(cbpds_update_progress, 100);
        });
        
        $(document).on('change', '.qty', function() {
            setTimeout(cbpds_update_progress, 500);
        });
        
        $('form.cart').on('submit', function() {
            setTimeout(cbpds_update_progress, 1000);
        });
        
        if ($('body').hasClass('woocommerce-cart') || $('body').hasClass('woocommerce-checkout')) {
            setInterval(cbpds_update_progress, 3000);
        }
    });
    </script>
    <?php
}

function cbpds_calculate_progress($total) {
    $tiers = cbpds_get_discount_tiers();
    $tier_keys = array_keys($tiers);
    $current_tier = 0;
    $current_discount = 0;
    $next_tier = $tier_keys[0];
    $progress_percentage = 0;
    
    foreach ($tier_keys as $tier) {
        if ($total >= $tier) {
            $current_tier = $tier;
            $current_discount = $tiers[$tier];
        }
    }
    
    foreach ($tier_keys as $tier) {
        if ($total < $tier) {
            $next_tier = $tier;
            break;
        }
    }
    
    if ($total >= max($tier_keys)) {
        $progress_percentage = 100;
        $next_tier = null;
    } else {
        $previous_tier = 0;
        foreach ($tier_keys as $tier) {
            if ($tier < $next_tier) {
                $previous_tier = $tier;
            }
        }
        
        if ($next_tier > $previous_tier) {
            $progress_percentage = (($total - $previous_tier) / ($next_tier - $previous_tier)) * 100;
        }
    }
    
    return [
        'current_tier' => $current_tier,
        'current_discount' => $current_discount,
        'next_tier' => $next_tier,
        'progress_percentage' => min(100, max(0, $progress_percentage)),
        'remaining_amount' => $next_tier ? max(0, $next_tier - $total) : 0
    ];
}

?>

How To Implement This Solution?

Leave a Reply