1

I have scoured the net and many pages here on stackoverflow. I’m trying to set up a WooCommerce abandoned cart flow where an auto generated coupon in WooCommerce can be added to the cart. I have butchered various code that I’ve found. I have it pretty rough but kinda working. I’m not that familiar with PHP so struggling to get this to behave correctly.

I have the coupon auto generating in WooCommerce admin when a product is added to the cart, its also being applied on the cart page. However every time the page is refreshed more coupons are being generated and added to the cart. Also coupons are generated every time a product is added to the cart.

Is there anyway to limit the coupon so that it is only generated and applied once?

I was planning to direct to the WooCommerce shopping cart from a recreated abandoned cart flow in Klaviyo, where all products from a customers abandoned cart will be added. I was planning to append the URL with something like https://example.com/cart?coupon-code=mycoupon

Would it at all be possible to trigger the coupon creation from the URL variable (eg/ mycoupon) ?

Please let me know if this is even possible? Before I waste another day of my life on this project...lol

Heres my code so far.

function coupon_exists($coupon_code) {
    global $wpdb;

    $sql = $wpdb->prepare( "SELECT post_name FROM $wpdb->posts WHERE post_type = 'shop_coupon' AND post_name = '%s'", $coupon_code );

    $coupon_codes = $wpdb->get_results($sql);

    if (count($coupon_codes)> 0) {
        return true;
    }
    else {
        return false;
    }
} 

// Utility function that generate a non existing coupon code (as each coupon code has to be unique)
function random_coupon_code() {
    global $wpdb;
    
    // Get an array of all existing coupon codes
    $coupon_codes_check = $wpdb->get_col("SELECT post_name FROM $wpdb->posts WHERE post_type = 'shop_coupon'");
    
    for ( $i = 0; $i < 1; $i++ ) {
        $generated_code = strtolower( wp_generate_password( 15, false ) );
        
        // Check if the generated code doesn't exist yet
        if( in_array( $generated_code, $coupon_codes_check ) ) {
            $i--; // continue the loop and generate a new code
        } else {
            break; // stop the loop: The generated coupon code doesn't exist already
        }
    }
    return $generated_code;
}  

function generate_random_coupon($coupon_generated) {
    
// Set some coupon data by default
    $date_expires     = date('Y-m-d', strtotime('+1 days'));
    $discount_type    = 'fixed_cart'; // 'store_credit' doesn't exist
    $coupon_amount    = '10';

    $coupon = new WC_Coupon();
    
    // Generate a non existing coupon code name
    $coupon_code  = random_coupon_code();
    $coupon->set_code($coupon_generated);
    //the coupon discount type can be 'fixed_cart', 'percent' or 'fixed_product', defaults to 'fixed_cart'
    $coupon->set_discount_type($discount_type);
    //the discount amount, defaults to zero
    $coupon->set_amount($coupon_amount );
    $coupon->set_date_expires( $date_expires );

    //save the coupon
    $coupon->save();

    return $coupon_generated;
}


function hwn_add_programmatically_created_coupon_to_basket( $cart ) {
    
   if ( is_admin() && ! defined( 'DOING_AJAX' ) )
      return;
   if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
      return;
   $coupon_code  = random_coupon_code();
    
   if (!coupon_exists($coupon_code)) {
      generate_random_coupon($coupon_code);
   }        
   $applied_coupons  = $cart->get_applied_coupons();
   if( ! in_array($coupon_code, $applied_coupons)){
      $cart->add_discount( $coupon_code );
      wc_print_notices();
   }
   elseif( in_array($coupon_code, $applied_coupons)){
      $cart->remove_coupon( $coupon_code );
   }    
}
add_action('woocommerce_before_calculate_totals', 'hwn_add_programmatically_created_coupon_to_basket'); 

https://i.stack.imgur.com/65fZX.png

https://i.stack.imgur.com/xVGyn.png

https://i.stack.imgur.com/GX49V.png

Stoto
  • 41
  • 1
  • 8

1 Answers1

1

You can get a coupon code from the URL using the GET method. try the below code. You remove the function random_coupon_code because now you generate coupon code based on URL.

function coupon_exists( $coupon_code ) {
    global $wpdb;

    $sql = $wpdb->prepare( "SELECT post_name FROM $wpdb->posts WHERE post_type = 'shop_coupon' AND post_name = '%s'", $coupon_code );

    $coupon_codes = $wpdb->get_results($sql);

    if ( count( $coupon_codes ) > 0 ) {
        return true;
    } else {
        return false;
    }
} 

function generate_coupon($coupon_generated) {
    
    // Set some coupon data by default
    $date_expires     = date('Y-m-d', strtotime('+1 days'));
    $discount_type    = 'fixed_cart'; // 'store_credit' doesn't exist
    $coupon_amount    = '10';

    $coupon = new WC_Coupon();
    
    $coupon->set_code($coupon_generated);
    //the coupon discount type can be 'fixed_cart', 'percent' or 'fixed_product', defaults to 'fixed_cart'
    $coupon->set_discount_type($discount_type);
    //the discount amount, defaults to zero
    $coupon->set_amount($coupon_amount );
    $coupon->set_date_expires( $date_expires );

    //save the coupon
    $coupon->save();

    return $coupon_generated;
}

function hwn_add_programmatically_created_coupon_to_basket( $cart ) {
    
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;
    if ( did_action( 'woocommerce_before_calculate_totals' ) >= 2 )
        return;
    
    $coupon_code = ( isset( $_GET['coupon-code'] ) && $_GET['coupon-code'] != '' ) ? $_GET['coupon-code'] : '' ;

    if( $coupon_code == '' ){
        return;
    }

    $applied_coupons = $cart->get_applied_coupons();

    if( empty( $applied_coupons ) || ! in_array( $coupon_code, $applied_coupons ) ){
        if ( !coupon_exists( $coupon_code ) ) {
            generate_coupon( $coupon_code );
        }   
        $cart->add_discount( $coupon_code );
    }       

}
add_action('woocommerce_before_calculate_totals', 'hwn_add_programmatically_created_coupon_to_basket');

Tested and works

enter image description here

Bhautik
  • 11,125
  • 3
  • 16
  • 38
  • Thanks for your quick response, much appreciated. Sorry I'm not sure what I'm missing here, I tried your modified code but cant seem to get it to work? I added a product to the cart then used example.com/cart/?coupon_code=mycoupon but no coupon is being generated in WooCommerce admin. Do you mean to first create a coupon in Woo called mycoupon and then add that to the URL? I did try this also but still not working. I'd like a random coupon code to be generated so that it cannot be used or shared by others. Thats why I had the function random_coupon_code in the original code. – Stoto Nov 23 '21 at 08:05
  • It should work. also, you said that you are planning to append coupons to the URL. can you explain how this will work? – Bhautik Nov 23 '21 at 08:40
  • I am planning to send customer an abandoned cart email through Klaviyo, and using their cart rebuild variable so the products from the cart can be recreated -[link] https://help.klaviyo.com/hc/en-us/articles/115005255808#rebuilding-carts-from-an-abandoned-cart-flow9 At the end of this I will add my coupon variable like this - {{ organization.url|trim_slash }}/cart?wck_rebuild_cart={{ event.extra.CartRebuildKey }}&coupon-code=mycoupon I have tried this in Klaviyo and it works as expected, I'm just having trouble on the WooCommerce side. – Stoto Nov 23 '21 at 10:25
  • Ok no worries I'll try your code again to see if I've overlooked something. Just one thing. Can you please clarify that you first created a coupon in WooCommerce first called mycoupon before testing the code. I'd like the coupon to be auto generated with a random string of letters and numbers and expiry of one day so that it cannot be reused or shared. – Stoto Nov 23 '21 at 10:30
  • I have not created `mycoupon` in woocommerce first it's created based on URL. Also, I am confused if you are going to share coupons via URL means the coupon code is already fixed then why do you want to generate a random coupon code? – Bhautik Nov 23 '21 at 10:39
  • Sorry my mistake, I already had a coupon called mycoupon in Woo that I didn't notice was there. Your code is now working as expected, thanks again. The reason I need a random coupon is because I will have an expiry of one day and also a usage limit of 1 `$coupon->set_usage_limit(1);` The way it is now once `mycoupon` is created, it cannot be created again for another user, once its in the Woo database. Does this make sense, if not please let me know and I'll try explain better. – Stoto Nov 23 '21 at 11:02
  • Sorry I forgot to mention, this will be an automated workflow. That is why I need the coupon to be auto generated. I dont want to manually add a seperate coupon for each customer. Right now the coupon will only work one time when the usage is set to 1 - https://i.imgur.com/XoUwokO.jpg – Stoto Nov 23 '21 at 11:19
  • I have found a workaround by uploading coupons to Klaviyo from WooCommerce so now don't need to autogenerate the coupons anymore. Your code will now work well for me. Thanks again for your help. – Stoto Nov 26 '21 at 11:40