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: 1 px solid #e9ecef;
border - radius : 6 px ;
padding: 16 px ;
margin: 16 px 0 ;
font - size : 14 px ;
color: #495057;
}
. cbpds - minimal - steps {
display: flex ;
gap: 8 px ;
margin - bottom : 12 px ;
flex - wrap : wrap ;
}
. cbpds - minimal - step {
flex: 1 ;
min - width : 100 px ;
text - align : center ;
padding: 12 px 8 px ;
border - radius : 4 px ;
position: relative ;
transition: all 0 . 2 s ease ;
display: flex ;
flex - direction : column ;
gap: 6 px ;
}
. cbpds - minimal - step : not ( . achieved ) : not ( . current ) {
background: #f8f9fa;
border: 1 px solid #dee2e6;
color: #6c757d;
}
. cbpds - minimal - step . current {
background: #fff3cd;
border: 1 px solid #ffeaa7;
color: #856404;
}
. cbpds - minimal - step . achieved {
background: #d4edda;
border: 1 px solid #c3e6cb;
color: #155724;
}
. cbpds - spend - row ,
. cbpds - discount - row {
display: flex ;
align - items : center ;
justify - content : center ;
gap: 6 px ;
}
. cbpds - label {
font - size : 10 px ;
font - weight : 500 ;
text - transform : uppercase ;
letter - spacing : 0 . 5 px ;
opacity: 0 . 7 ;
white - space : nowrap ;
}
. cbpds - amount ,
. cbpds - percent {
font - size : 12 px ;
font - weight : 700 ;
}
. cbpds - minimal - check {
position: absolute ;
top: - 2 px ;
right: - 2 px ;
background: #28a745;
color: white ;
width: 16 px ;
height: 16 px ;
border - radius : 50 %;
font - size : 10 px ;
display: flex ;
align - items : center ;
justify - content : center ;
line - height : 1 ;
}
. cbpds - minimal - progress {
margin - top : 12 px ;
}
. cbpds - minimal - bar {
background: #e9ecef;
border - radius : 3 px ;
height: 6 px ;
overflow: hidden ;
margin - bottom : 6 px ;
}
. cbpds - minimal - fill {
background: #29a745;
height: 100 %;
border - radius : 3 px ;
transition: width 0 . 3 s ease ;
}
. cbpds - minimal - text {
text - align : center ;
font - size : 12 px ;
color: #6c757d;
font - weight : 500 ;
}
. cbpds - minimal - complete {
background: #d4edda;
color: #155724;
padding: 10 px 12 px ;
border - radius : 4 px ;
text - align : center ;
font - weight : 500 ;
border: 1 px solid #c3e6cb;
font - size : 13 px ;
}
@ media ( max - width : 768 px ) {
. cbpds - minimal - container {
padding: 12 px ;
font - size : 13 px ;
}
. cbpds - minimal - steps {
flex - direction : column ;
gap: 6 px ;
}
. cbpds - minimal - step {
display: flex ;
flex - direction : row ;
justify - content : space - between ;
align - items : center ;
padding: 12 px ;
text - align : left ;
min - width : auto ;
gap: 8 px ;
}
. 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 : 480 px ) {
. cbpds - minimal - container {
margin: 12 px 0 ;
padding: 10 px ;
}
. cbpds - amount {
font - size : 12 px ;
}
. cbpds - percent {
font - size : 14 px ;
}
. cbpds - label {
font - size : 9 px ;
}
}
</ 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
] ;
}
?>