The Foxy forums are on the move!

We're in the process of moving our forums over to a new system, and so these forums are now read-only.
If you have a question about your store in the meantime, please don't hesitate to reach out to us via email.

State Sales Tax override

jacobdubailjacobdubail Member
in Help edited August 2012
Hey guys,

I've been informed that wholesalers don't have to pay sales tax in Massachusetts. Whether or not this is the case, is there anyway to override sales tax during checkout for specific customers?

I'm passing a custom field to the cart for the customers Role.

Thanks,
Jacob
Tagged:
Comments
  • sparkwebsparkweb Member, Integration Developer, FoxyShop, Order Desk
    edited August 2012
    Usually the best way to do this is to hijack the SSO and on your endpoint right before checkout, check for the role and if it should be non-taxable you remove the products from the cart and re-add them with a non-tax category.
  • jacobdubailjacobdubail Member
    edited October 2012
    Thanks David,

    Sorry to drop the thread for a month.

    Here's what I'm attempting to do at checkout as I'm not exactly sure how to go about doing the SSO hijacking.

    1. Detect role based on custom field.

    2. loop through the products, add them to a temp array and set the category to no_tax, then remove the product from the fc_json object.
    var products = []; 
    for ( var i = 0; i < fc_json.product_count; i++ ) {
     fc_json.products[i].category = "no_tax";
     products[i] = fc_json.products[i];
     fc_json.products.remove(i);
    }
    

    3. I'm not exactly sure what to do next. I've tried
    FC.checkout.updateTaxes()
    
    , but that doesn't quite work.

    Am I approaching this the wrong way?

    Thanks again!

    -Jacob

  • sparkwebsparkweb Member, Integration Developer, FoxyShop, Order Desk
    That doesn't work because you are just affecting the local fc_json object, not what's actually in the cart. You'd have to run JSON queries to actually remove them from the cart then update the checkout page so it's a lot better to do it in the SSO stage before you get to the checkout.

    Assuming you are using FoxyShop look at the ssoendpoint.php file for a template on exactly how to interact with the products. There's an action hook at the top of that page so you can easily do it all in your own file and then use the hook to do the hijacking.
  • Thanks David!

    I'll give that a shot this afternoon and report back.

    -Jacob
  • I'm getting closer...

    Here's what I have so far:
    add_action('foxyshop_sso_endpoint', 'my_sso_endpoint');
    function my_sso_endpoint() {
    
    	if ( is_user_logged_in() ) {
    		$user      = new WP_User( get_current_user_id() );
    		
    		foreach ( $user->roles as $role ) {
    		
    			if ( $role == 'retailer' || $role == 'distributor' || $role == 'administrator' ) {
    				// hijack cart contents and change category
    				if (isset($_GET['fcsid']) && isset($_GET['timestamp'])) {
    					
    					global $foxyshop_settings;
    					global $current_user;
    					
    					$ch = curl_init();
    					if ( !defined('FOXYSHOP_CURL_CONNECTTIMEOUT') ) define('FOXYSHOP_CURL_CONNECTTIMEOUT', 10);
    					if ( !defined('FOXYSHOP_CURL_TIMEOUT') ) define('FOXYSHOP_CURL_TIMEOUT', 15);
    					curl_setopt( $ch, CURLOPT_URL, "https://"; . esc_attr($foxyshop_settings['domain']) . "/cart?fcsid=" . $_GET['fcsid'] . "&output=json" );
    					curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
    					curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, FOXYSHOP_CURL_CONNECTTIMEOUT );
    					curl_setopt( $ch, CURLOPT_TIMEOUT, FOXYSHOP_CURL_TIMEOUT );
    					if ( defined('FOXYSHOP_CURL_SSL_VERIFYPEER') ) curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, FOXYSHOP_CURL_SSL_VERIFYPEER );
    					
    					$curlout      = trim(curl_exec($ch));
    					if ($curlout) {
    						$response  = json_decode($curlout, true);						
    						foreach( $response['products'] as $product ) {
    							$product['category'] = 'no_tax';
    						}
    					}
    				
    				}
    			} else {
    				return;
    			}
    		}		
    	}
    }
    

    I'm not seeing how I can remove the product, or change the category. The code is definitely being run, but not with the expected outcome. Am I close?

    -J
  • sparkwebsparkweb Member, Integration Developer, FoxyShop, Order Desk
    You actually have to remove the product and re-add it. So as you loop through, on each product you'd do this:

    CURL call to delete it (cart=update&id= the $product &quantity=0
    CURL call to add it back.

    Here's some code I'm working on for something else that might get you close:
    //Each Product
    $product_vars = array("name", "code", "image", "url", "quantity", "quantity_min", "quantity_max", "price", "weight", "shipto", "category", "sub_frequency");
    $product_subs = array("sub_startdate", "sub_nextdate", "sub_enddate");
    
    foreach ($fc->products as $product) {
    
    	$url = "https://"; . $foxycart_domain . "/cart?fcsid=" . $fcsid . "&output=json";
    	$code = $product->code;
    	foreach ($product as $key => $val) {
    
    		//Base Names
    		if (in_array($key, $product_vars)) {
    			$url .= "&" . urlencode(htmlspecialchars($key)) . "=" . urlencode(htmlspecialchars($val)) . get_verification($key, $val, $code);
    
    		//Sub
    		} elseif (in_array($key, $product_subs) && $val != "0000-00-00") {
    			$url .= "&" . $key . "=" . urlencode(htmlspecialchars($val)) . get_verification($key, $val, $code);
    
    		//Options
    		} elseif ($key == "options") {
    			foreach ($val as $option_key => $option_val) {
    				$url .= "&" . urlencode(htmlspecialchars($option_key)) . "=" . urlencode(htmlspecialchars($option_val)) . get_verification($option_key, $option_val, $code);
    			}
    		}
    
    	}
    
    	//Add Item
    	$ch = curl_init();
    	curl_setopt($ch, CURLOPT_URL, $url);
    	curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    	$curlout = trim(curl_exec($ch));
    
    }
    
    //Function
    function get_verification($var_name, $var_value, $var_code) {
    	global $api_key;
    	$encodingval = htmlspecialchars($var_code) . htmlspecialchars($var_name) . htmlspecialchars($var_value);
    	return '||'.hash_hmac('sha256', $encodingval, $api_key).($var_value == "--OPEN--" ? "||open" : "");
    }
    

    NOTE that this uses a get_verification function so make sure to set the $api_key. This is probably a little rough, but this should add the products in that you are taking out.

  • I'm so close.

    I'm successfully removing the products, and I was pretty sure the add product code was right as I copied your code... but now I'm getting an empty cart error at checkout, which leads me to believe that I'm not adding the products back correctly.

    Does anything jump out as being obviously wrong?

    Thanks again for all of your help.

    -Jacob
    function my_sso_endpoint() {
      global $foxyshop_settings, $current_user;
    
      if ( is_user_logged_in() ) {
        $user      = new WP_User( get_current_user_id() );
        
        foreach ( $user->roles as $role ) {
        
          if ( $role == 'retailer' || $role == 'distributor' || $role == 'administrator' ) {
            // hijack cart contents and change category
            if (isset($_GET['fcsid']) && isset($_GET['timestamp'])) {
              
              $ch = curl_init();
              if ( !defined('FOXYSHOP_CURL_CONNECTTIMEOUT') ) define('FOXYSHOP_CURL_CONNECTTIMEOUT', 10);
              if ( !defined('FOXYSHOP_CURL_TIMEOUT') ) define('FOXYSHOP_CURL_TIMEOUT', 15);
              curl_setopt( $ch, CURLOPT_URL, "https://"; . esc_attr($foxyshop_settings['domain']) . "/cart?fcsid=" . $_GET['fcsid'] . "&output=json" );
              curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
              curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, FOXYSHOP_CURL_CONNECTTIMEOUT );
              curl_setopt( $ch, CURLOPT_TIMEOUT, FOXYSHOP_CURL_TIMEOUT );
              if ( defined('FOXYSHOP_CURL_SSL_VERIFYPEER') ) curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, FOXYSHOP_CURL_SSL_VERIFYPEER );
              
              $curlout        = trim(curl_exec($ch));
              if ($curlout) {
    
                $response     = json_decode($curlout, true);   
                $product_vars = array("name", "id", "width", "height", "length", "code", "image", "url", "quantity", "quantity_min", "quantity_max", "price", "weight", "shipto", "category", "sub_frequency");
                $product_subs = array("sub_startdate", "sub_nextdate", "sub_enddate");
    
                foreach( $response['products'] as $product ) {
    
                  $url  = "https://"; . esc_attr($foxyshop_settings['domain']) . "/cart?fcsid=" . $_GET['fcsid'] . "&output=json";
                  $code = $product['code'];
                  $name = $product['name'];
                  $pid  = $product['id'];
    
    
                  //Remove Product From Cart
                  $ch = curl_init();
                  if (!defined('FOXYSHOP_CURL_CONNECTTIMEOUT')) define('FOXYSHOP_CURL_CONNECTTIMEOUT', 10);
                  if (!defined('FOXYSHOP_CURL_TIMEOUT')) define('FOXYSHOP_CURL_TIMEOUT', 15);
                  curl_setopt($ch, CURLOPT_URL, "https://"; . esc_attr($foxyshop_settings['domain']) . "/cart?fcsid=" . $_GET['fcsid'] . "&cart=update&quantity=0&id=".$pid);
                  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                  curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, FOXYSHOP_CURL_CONNECTTIMEOUT);
                  curl_setopt($ch, CURLOPT_TIMEOUT, FOXYSHOP_CURL_TIMEOUT);
                  if (defined('FOXYSHOP_CURL_SSL_VERIFYPEER')) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FOXYSHOP_CURL_SSL_VERIFYPEER);
                  $curlout = trim(curl_exec($ch));
    
    
                  
    
    
                  foreach ($product as $key => $val) {
    
                    // reset the category to no_tax
                    if ( $key == 'category' ) $val = 'no_tax';
                 
                    //Base Names
                    if (in_array($key, $product_vars)) {
                      $url .= "&" . urlencode(htmlspecialchars($key)) . "=" . urlencode(htmlspecialchars($val)) . get_verification($key, $val, $code);
                 
                    //Sub
                    } elseif (in_array($key, $product_subs) && $val != "0000-00-00") {
                      $url .= "&" . $key . "=" . urlencode(htmlspecialchars($val)) . get_verification($key, $val, $code);
                 
                    //Options
                    } elseif ($key == "options") {
                      foreach ($val as $option_key => $option_val) {
                        $url .= "&" . urlencode(htmlspecialchars($option_key)) . "=" . urlencode(htmlspecialchars($option_val)) . get_verification($option_key, $option_val, $code);
                      }
                    }
                 
                  } // as key => val
                 
                  //Add Item
                  $ch = curl_init();
                  curl_setopt($ch, CURLOPT_URL, $url);
                  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                  $curlout = trim(curl_exec($ch));
                   
                } // end foreach
                   
                  
              } // end curlout
              
            
            } // if fcsid and timestamp
          } else {
            return;
          }
        } // end foreach role   
      } // if is logged in
    }
    //Function
    function get_verification($var_name, $var_value, $var_code) {
      global $foxyshop_settings;
      $api_key = $foxyshop_settings['api_key'];
      $encodingval = htmlspecialchars($var_code) . htmlspecialchars($var_name) . htmlspecialchars($var_value);
      return '||'.hash_hmac('sha256', $encodingval, $api_key).($var_value == "--OPEN--" ? "||open" : "");
    }
    
  • sparkwebsparkweb Member, Integration Developer, FoxyShop, Order Desk
    Echo out your $curlout and see what it is returning. That will give you the error. Something I discovered which may or may not be a big deal:
    if (in_array($key, $product_vars)) {
    if ($key == "price") $val = $product->price_each;
    if ($key == "weight") $val = $product->weight_each;
    $url .= "&" . urlencode(htmlspecialchars($key)) . "=" . urlencode(htmlspecialchars($val)) . get_verification($key, $val, $code);
    
  • Good idea. I'll echo it out in a hidden div. Unfortunately, this is a live store so have to be careful. I'll report back.

    Thanks again!

    -Jacob
  • sparkwebsparkweb Member, Integration Developer, FoxyShop, Order Desk
    There's no price in that long crazy url.

    Might be a good idea to set up a test store to do your testing on?

    price=||127dddd6a02813d1a8195ff21958a4f4a12d9c61790ea5ac8ddef1a9fd8d9471

    No weight for that matter. I think it might have to do with the last bit of code I sent. Double check all that. I think without a price it won't add the product.
  • Okay! Got it working.
    add_action('foxyshop_sso_endpoint', 'my_sso_endpoint');
    function my_sso_endpoint() {
      global $foxyshop_settings, $current_user;
    
      if ( is_user_logged_in() ) {
        $user      = new WP_User( get_current_user_id() );
        
        foreach ( $user->roles as $role ) {
        
          if ( $role == 'retailer' || $role == 'distributor' ) {
            // hijack cart contents and change category
            if (isset($_GET['fcsid']) && isset($_GET['timestamp'])) {
              
              $ch = curl_init();
              if ( !defined('FOXYSHOP_CURL_CONNECTTIMEOUT') ) define('FOXYSHOP_CURL_CONNECTTIMEOUT', 10);
              if ( !defined('FOXYSHOP_CURL_TIMEOUT') ) define('FOXYSHOP_CURL_TIMEOUT', 15);
              curl_setopt( $ch, CURLOPT_URL, "https://"; . esc_attr($foxyshop_settings['domain']) . "/cart?fcsid=" . $_GET['fcsid'] . "&output=json" );
              curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
              curl_setopt( $ch, CURLOPT_CONNECTTIMEOUT, FOXYSHOP_CURL_CONNECTTIMEOUT );
              curl_setopt( $ch, CURLOPT_TIMEOUT, FOXYSHOP_CURL_TIMEOUT );
              if ( defined('FOXYSHOP_CURL_SSL_VERIFYPEER') ) curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, FOXYSHOP_CURL_SSL_VERIFYPEER );
              
              $curlout        = trim(curl_exec($ch));
              if ($curlout) {
              
    /* added for troubleshooting. need to see what's being passed around.
    					$file = STYLESHEETPATH.'/datafeed.xml';
    					$fh   = fopen($file, 'a') or die("Couldn't open $file for writing!"); 
    					fwrite($fh, $url);
    					fwrite($fh, $curlout);
    					fclose($fh);
    /**/
    
                $response     = json_decode($curlout, true);   
                $product_vars = array("name", "id", "width", "height", "length", "code", "image", "url", "quantity", "quantity_max", "price_each", "price", "weight_each", "weight", "shipto", "category"); //"sub_frequency"
                $product_subs = array("sub_startdate", "sub_nextdate", "sub_enddate");
    
                foreach( $response['products'] as $product ) {
    
                  $url  = "https://"; . esc_attr($foxyshop_settings['domain']) . "/cart?fcsid=" . $_GET['fcsid'] . "&output=json";
                  $code = $product['code'];
                  $name = $product['name'];
                  $pid  = $product['id'];
    
    
    					//Remove Product From Cart
    /**/              
    					$ch = curl_init();
    					if (!defined('FOXYSHOP_CURL_CONNECTTIMEOUT')) define('FOXYSHOP_CURL_CONNECTTIMEOUT', 10);
    					if (!defined('FOXYSHOP_CURL_TIMEOUT')) define('FOXYSHOP_CURL_TIMEOUT', 15);
    					curl_setopt($ch, CURLOPT_URL, "https://"; . esc_attr($foxyshop_settings['domain']) . "/cart?fcsid=" . $_GET['fcsid'] . "&cart=update&quantity=0&id=".$pid);
    					curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    					curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, FOXYSHOP_CURL_CONNECTTIMEOUT);
    					curl_setopt($ch, CURLOPT_TIMEOUT, FOXYSHOP_CURL_TIMEOUT);
    					if (defined('FOXYSHOP_CURL_SSL_VERIFYPEER')) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FOXYSHOP_CURL_SSL_VERIFYPEER);
    					$curlout = trim(curl_exec($ch));
    /**/
    
                  
    
    
    				foreach ($product as $key => $val) {
                               
    					//Base Names
    					if (in_array($key, $product_vars)) {
    						if ($key == "price_each")    $key = "price";
    						if ($key == "weight_each")   $key = "weight";
    						if ($key == "category")      $val = "no_tax";
    //						if ($key == "sub_frequency") $val = "0d";
    						$url .= "&" . urlencode(htmlspecialchars($key)) . "=" . urlencode(htmlspecialchars($val)) . get_verification($key, $val, $code);
    					
    					//Sub - had to comment this out. notes in the thread below
    //					} elseif (in_array($key, $product_subs) && $val != "0000-00-00") { 
    //						$url .= "&" . $key . "=" . urlencode(htmlspecialchars($val)) . get_verification($key, $val, $code);
    					
    					//Options
    					} elseif ($key == "options") {
    						foreach ($val as $option_key => $option_val) {
    							$url .= "&" . urlencode(htmlspecialchars($option_key)) . "=" . urlencode(htmlspecialchars($option_val)) . get_verification($option_key, $option_val, $code);
    						}
    					}
    	                           
    				} // as key => val
                 
                  //Add Item
                  $ch = curl_init();
                  curl_setopt($ch, CURLOPT_URL, $url);
                  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
                  $curloutAdd = trim(curl_exec($ch));
                  
    /* added for troubleshooting. need to see what's being passed around.		
    					$file = STYLESHEETPATH.'/datafeed.xml';
    					$fh   = fopen($file, 'a') or die("Couldn't open $file for writing!"); 
    					fwrite($fh, $url);
    					fwrite($fh, $curloutAdd);
    					fclose($fh);
    /**/
                   
                } // end foreach
                   
                  
              } // end curlout
              
            
            } // if fcsid and timestamp
          } else {
            return;
          }
        } // end foreach role   
      } // if is logged in
    }
    //Function
    function get_verification($var_name, $var_value, $var_code) {
      global $foxyshop_settings;
      $api_key = $foxyshop_settings['api_key'];
      $encodingval = htmlspecialchars($var_code) . htmlspecialchars($var_name) . htmlspecialchars($var_value);
      return '||'.hash_hmac('sha256', $encodingval, $api_key).($var_value == "--OPEN--" ? "||open" : "");
    }
    


    I had to comment out the Product Subs handler. The code would work perfectly the first time I go to checkout. If I cancel and continue shopping, then go back to the cart, I'd lose all my products. The error had to do with the sub_frequency and sub_start/enddate. If I don't se the sub_frequency to something other than an empty string, I get an error. If I set it to "0d", the sub_start/enddate freaks out because it's set to 00000000, which isn't a date in the future.

    Fortunately, we're not yet using subscriptions, so I was able to comment this out for the time being until I have more time to troubleshoot.

    Hopefully this can help someone else in the future.

    Thanks to @sparkweb for all of his hand-holding! Couldn't have done any of this without his help and the amazing powers of Foxycart/Foxy-Shop!

    -Jacob
  • sparkwebsparkweb Member, Integration Developer, FoxyShop, Order Desk
    Awesome! That's great....

    You might want to look at the price and price_each thing again. It kind of looks like you might be passing in two price variables. I think you should just remove price and weight because your login has price_each and weight_each doing the trick.

    Great work. This is really helpful code.
  • Thanks David.

    Before I added these two lines
    if ($key == "price_each")    $key = "price";
    if ($key == "weight_each")   $key = "weight";
    
    The products weren't being added successfully.

    When I printed the output to file, I noticed that the url only had a price_each, but not a price variable.

    Are you recommending I remove price and weight from the $product_vars array? Or that I reconfigure the snippet above? I tried a handful of variations, but this was the only thing I could get to work. Definitely open to suggestions, though.

    Thanks again,
    Jacob
  • sparkwebsparkweb Member, Integration Developer, FoxyShop, Order Desk
    Well, from my quick look at this, it seems like it would end up with two price variables. One for the real price (which we don't want) and one from the price_each (which is turned into price) which we do want. If price_each (faked to price) is the second one, the first one will be ignored. But it seems like it would be better to skip the original price altogether.
  • Yeah, looks like we just ran into our first big error... the price is turned into price_each, so if I try to buy 10 items, the item price is set to 10 times the original price. It looks like the price (not price_each) is being use for the item price, which throws off the calculation big time. So, I tried removing price, but now the products don't get re-added to the cart. Confused!!!

    WIll report back when I get it working.

    -J
  • Got it fixed. You had the right idea from the start...
    if ($key == "price") $val = $product["price_each"];
    

    -J
  • fc_adamfc_adam FoxyCart Team
    @jacobdubail,

    Looks like you and @sparkweb have developed a nice little script here! If you have the time, we certainly love to list it on our snippets section if you like to add it in: http://wiki.foxycart.com/snippets/start
  • Thanks @fc_adam! @sparkweb definitely deserves the credit on this one. I'll get it posted to the snippets area in the morning.

    -Jacob
  • brettbrett FoxyCart Team
    Wow guys. Awesome. We'll get to a point where you don't have to do this, but nice solution in the meantime. Thanks for sharing it @jacobdubail !
Sign In or Register to comment.