Since Shopsite hasn't (as yet) added support for the new EU VAT rules on digital goods, here is the tax api script I rolled back in January to cope with the EU VAT changes that came in force then. Thousands of micro businesses are struggling with this ruling, and either forced to stop selling to the EU, pay for an expensive third party solution, or switch to selling only via a marketplace portal, so I hope this helps someone. My implementation is quite limited, as I only sell digital downloads, but I hope it's useful anyhow. Note that to be compliant, you'll need to store and do your own geo-location on the purchaser's IP address, which is fortunately already available in the ShopSite order data. I'm doing the geolocation in my order processing when I download the orders, getting it from ipinfo.io It's an http get with format 'http://ipinfo.io/' + ip + '/country', which returns the 2-character ISO country code. One unexpected bonus of this api script is that the word "Tax" now changes to the more expected word "VAT" in the checkout, just for EU customers.
Note also that this particular script will only be useful to a digital business based in the EU as it only calculates EU VAT. If you are in the US, you'll need something that incorporates this plus your regular US local taxes, and will have to find your own solution. It's a testament to ShopSite's flexible framework that this was straightforward to implement without requiring changes. Here it is anyhow:
- Code: Select all
#!/usr/local/bin/php -d max_input_vars=3000
<?php
// DPG: 1/1/2015: Tax API for new EU VAT. Digital B2C sales now require VAT rate per EU country.
// DPG: Based on http://www.sitekickr.com/snippets/php/shopsite-custom-shipping-module
// DPG: Thread http://support.shopsite.com/forums/viewtopic.php?f=1&t=6283&p=27619
// DPG: Put in public_html/cgi-bin/sc directory with 755 access.
// DPG: Specify name of file in ShopSite/Merchandising/Tax API
// DPG: Uses the same techniques as the Order API and Shipping API
// DPG: 4/11/2015 Added support for coupons
// note - you need a CLI-enabled php binary, the usual /usr/bin/php may not work.
// Ask your host about this
// Define the EU countries and tax rates. No help from ShopSite here, so we need to
// keep this updated manually when countries and tax rates change. The supplied country
// appears to be ISO 2-char codes, not the strings in the "States and Countries" screen.
// Test e.g. gdatax.php?country=GB&item_total=1&p1item_num=1&p1amount=15
$euTax_array = array(
'AT' => 20, // Austria
'BE' => 21, // Belgium
'BG' => 20, // Bulgaria
'HR' => 25, // Croatia
'CY' => 19, // Cyprus
'CZ' => 21, // Czech Republic
'DK' => 25, // Denmark
'EE' => 20, // Estonia
'FI' => 24, // Finland
'FR' => 20, // France
'DE' => 19, // Germany
'GR' => 23, // Greece
'HU' => 27, // Hungary
'IE' => 23, // Ireland
'IT' => 22, // Italy
'LV' => 21, // Latvia
'LT' => 21, // Lithuania
'LU' => 17, // Luxembourg
'MT' => 18, // Malta
'NL' => 21, // Netherlands
'PL' => 23, // Poland
'PT' => 23, // Portugal
'RO' => 24, // Romania
'SK' => 20, // Slovakia
'SI' => 22, // Slovenia
'ES' => 21, // Spain
'SE' => 25, // Sweden
'GB' => 20); // United Kingdom
function MailDebug ()
{ // Function to mail me debug and error messages plus the shopsite params
global $params, $dbgmsg, $errmsg;
$eparams = var_export($params,true);
$message = "This is a message from the MyCompanyName Tax API script:
Debug:
$dbgmsg
Errors:
$errmsg
Shopsite params:
$eparams
End of message.";
mail("MyEmailAddress","Tax API message",$message, "From: MyCompanyName Tax API <MyEmailAddress>");
}
function Dbg ($s)
{ // Function to add a new dbg msg to global $dbgmsg
global $dbgmsg;
$dbgmsg = $dbgmsg.$s."\n";
}
function Err ($s)
{ // Function to add a new error msg to global $errmsg
global $errmsg;
$errmsg = $errmsg.$s."\n";
}
// Following needed whenever outputting data, avoids output compression
ini_set('zlib.output_compression', 'Off');
// Here's the difference, we need to pull parameters from STDIN
$query_string = trim(fgets(STDIN));
parse_str($query_string, $params);
// We'll load these into request variables, so we can test this script from a browser
if (!isset($_REQUEST['country'])) {
$_REQUEST['country'] = $params['country'];
$_REQUEST['item_total'] = $params['item_total'];
$_REQUEST['coupon_total'] = $params['coupon_total'];
for($i = 1; $i <= $params['item_total']; $i++) {
$_REQUEST['p' . $i . 'item_num'] = $params['p' . $i . 'item_num'];
$_REQUEST['p' . $i . 'amount'] = $params['p' . $i . 'amount'];
}
for($i = 1; $i <= $params['coupon_total']; $i++) {
$_REQUEST['c' . $i . 'amount'] = $params['c' . $i . 'amount'];
}
}
// If country is in the EU, use that country's VAT rate, else zero for everyone else
$taxRate = 0;
$isEU = array_key_exists($_REQUEST['country'], $euTax_array);
if ($isEU) {
$taxRate = $euTax_array[$_REQUEST['country']] / 100.0;
Dbg ("EU Tax rate set: " . $_REQUEST['country'] . " $taxRate");
}
// Calculate the total tax
$total_tax = 0;
$tax_name = "";
$productCount = $_REQUEST['item_total'];
$couponCount = $_REQUEST['coupon_total'];
if ($isEU) {
$tax_name = "tax_name=VAT\n"; // Change tax name to VAT if EU country
for($i = 1; $i <= $productCount; $i++) {
$total_tax += ($_REQUEST['p' . $i . 'amount'] * $taxRate);
}
for($i = 1; $i <= $couponCount; $i++) {
$total_tax += ($_REQUEST['c' . $i . 'amount'] * $taxRate);
}
}
// Return the tax information to ShopSite
$taxOut =
"taxapi_status=success\n" .
sprintf ("total_tax=%0.2f\n", $total_tax) .
$tax_name .
"product_count=$productCount\n";
for($i = 1; $i <= $productCount; $i++) {
$taxOut = $taxOut . 'p' . $i . 'item_num=' . $_REQUEST["p" . $i . "item_num"] . "\n";
$taxOut = $taxOut . 'p' . $i . 'tax_rate=' . $taxRate . "\n";
}
// Dbg ($taxOut);
// MailDebug ();
echo $taxOut;
?>