3

I am coding a WordPress plugin that goes on top of WooCommerce. This plugin basically allows WooCommerce to allocate store credits to a logged-in customer whenever he makes an order later marked as Completed after payment. These store credits can then be used to offset the cost of a future order that the customer makes. As an example, if I buy $10 worth of items, I might get a $1 store credit. I can use any amount of my store credit to offset future purchases, e.g. I might choose to use $0.50 of my credits to offset my next order by $0.50.

I have had no problem finding the hooks to use in WP-Admin to allocate customers their store credits, but I'm having trouble adding hooks to the front-end to allow customers to use their store credits in an order.

Currently, I have the following function hooked to the woocommerce_checkout_order_processed action, to record how much store credit the customer is intending to use on an order:

function woocommerce_checkout_order_processed($order_id) {
    $offset_amt = floatval($_POST['use-store-credit']);
    // This line is a glorified update_post_meta call.
    Helpers\Order::set_offset_cost_amount($order_id,$offset_amt);
}

But I think saving it as a meta key seperate from the workings of WP_Order or WP_Cart is a bad idea, because once the order is submitted, I will have to hook onto every page the order is rendered (including the pages in WP-Admin) and manually modify the order total, because the WP_Order will record the total without the store credit offset.

I think hooking this store credit offset as a discount or as a negative fee would be better, but I don't know which hooks to use or where in the process I should do that. Should I hook it on the cart before checkout, or should I hook it onto the order after checkout?

2 Answers 2

3

I would suggest adding a "virtual" coupon to the order which would represent the store credit.

Coupons are applied on the cart page before checkout, this is where I would hook in.

You can use something like the woocommerce_cart_subtotal filter as per my example below:

function royal_woocommerce_filter_checkout_for_coupons( $subtotal, $compound, $cart ) {     

        // Your logic to get store credit value for a user will go here
        $store_credit = 20;

        // We only need to add a store credit coupon if they have store credit
        if($store_credit > 0){

            // Setup our virtual coupon
            $coupon_name = 'store-credit';
            $coupon = array($coupon_name => $store_credit);

            // Apply the store credit coupon to the cart & update totals
            $cart->applied_coupons = array($coupon_name);
            $cart->set_discount_total($store_credit);
            $cart->set_total( $cart->get_subtotal() - $store_credit);
            $cart->coupon_discount_totals = $coupon;
        }

    return $subtotal; 
}

add_filter( 'woocommerce_cart_subtotal', 'royal_woocommerce_filter_checkout_for_coupons', 10, 3 );
3
  • Thanks for the response! I added a field above the Apply Coupons field in the cart page for users to key in the amount of store credits they want to use, then stored the credits in the session variable. Then I used your hook to apply that as a coupon. I'm concerned that the woocommerce_cart_subtotal may not be future-proof, since it is quite an unusual hack. What do you think are the odds of a future WooCommerce plugin breaking this?
    – John Doe
    Commented Sep 28, 2018 at 10:17
  • 1
    There may be better filters to use but I don't really think it will matter much, so long as you are manipulating the cart data on the cart page to apply your coupon. With regards to being future proof, I don't see the woocommerce_cart_subtotal being changed or taken out, its integral into the cart/checkout process and the same applies to coupons - all in all it should be pretty reliable going forward. What I would suggest looking into is a scenario where actual coupons need to be used as well, in conjunction with your store credits. The code will need to be tweaked to handle that...
    – Matt Royal
    Commented Sep 28, 2018 at 13:10
  • Thanks! I tweaked my code so that it doesn't use coupons. Instead, the data is saved in WC()->session and shown through hooks onto the basket and checkout totals section. By the way, I used to do MMA too! Really enjoyed it. But I was often on the side that got their ass kicked.
    – John Doe
    Commented Sep 29, 2018 at 12:05
7

The proper way to add a virtual coupon programmatically, without interfering with regular coupons:

add_action('woocommerce_before_calculate_totals', function(WC_Cart $cart) {
  $cart->applied_coupons = array_diff($cart->applied_coupons, ['store-credit']);

  // add your conditions for applying the virtual coupon
  $cart->applied_coupons[] = 'store-credit';
});

// specify discount data, here give 10% discount
add_filter('woocommerce_get_shop_coupon_data', function($false, $data, $coupon) {
  switch($data) {
    case 'store-credit':
      $coupon->set_virtual(true);
      $coupon->set_discount_type('percent');
      $coupon->set_amount(10);
      return $coupon;
  }
}, 10, 3);

// Optional: remove the 'Coupon:' label
add_filter('woocommerce_cart_totals_coupon_html', function($coupon_html, $coupon, $discount_amount_html) {
  if (in_array($coupon->get_code(), ['store-credit']))
    return $discount_amount_html;
  
  return $coupon_html;
}, 10, 3);

// Optional: set a custom label
add_filter('woocommerce_cart_totals_coupon_label', function($label, $coupon) {
  switch ($coupon->get_code()) {
    case 'store-credit':
      return 'Store Credit';
  }
  return $label;
}, 10, 3);
1
  • This is a great way to do it (using WooCommerce' "virtual" coupon feature), and should be the accepted answer. Commented Apr 29, 2021 at 6:16

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.