/**
  * template method that determines the algorithm to send data to and from the shipping
  * server. This method should be called from the implementing classes calculate shipping
  * function.
  */
 protected final function calculate_rate()
 {
     $services_to_use = $this->filter_services();
     $this->has_error = false;
     // canada post will return all services you have chosen in your online account in the response.
     // most configurations happen on canada post site. Therefore any other services that do something
     // similar, we don't want to take the approach that there are services to loop through here.
     if ($services_to_use) {
         foreach ($services_to_use as $current_service) {
             if (!fflcommerce_shipping::show_shipping_calculator() && !(defined('FFLCOMMERCE_CHECKOUT') && FFLCOMMERCE_CHECKOUT)) {
                 $request = '';
                 $this->set_error_message('Please proceed to checkout to get shipping estimates');
             } else {
                 // create request input for shipping service
                 $request = $this->create_mail_request($current_service);
             }
             if ($request) {
                 // send to shipping server and get xml back
                 $post_response = $this->send_to_shipping_server($request);
                 // convert xml into an array
                 $xml_response = $this->convert_xml_to_array($post_response);
                 // sums up the rates from flattened array, and generates amounts.
                 $rate = $this->retrieve_rate_from_response($xml_response);
                 if ($this->has_error()) {
                     fflcommerce_log($xml_response, 'fflcommerce_calculable_shipping');
                 }
                 $this->add_rate($rate, $current_service);
             }
         }
     } else {
         if (!fflcommerce_shipping::show_shipping_calculator() && !(defined('FFLCOMMERCE_CHECKOUT') && FFLCOMMERCE_CHECKOUT)) {
             $request = '';
             $this->set_error_message('Please proceed to checkout to get shipping estimates');
         } else {
             // create request input for shipping service
             $request = $this->create_mail_request();
         }
         if ($request) {
             // send to shipping server and get xml back
             $post_response = $this->send_to_shipping_server($request);
             // convert xml into an array
             $xml_response = $this->convert_xml_to_array($post_response);
             // services are obtained from response
             $services = $this->get_services_from_response($xml_response);
             if (empty($services)) {
                 fflcommerce_log($xml_response, 'fflcommerce_calculable_shipping');
             }
             foreach ($services as $current_service) {
                 $rate = $this->retrieve_rate_from_response($xml_response, $current_service);
                 $this->add_rate($rate, $current_service);
             }
         }
     }
     // service returned an error since no rates were calculated
     if (($this->rates == NULL || !$this->rates) && !$this->has_error()) {
         $this->has_error = true;
     }
 }
    public function format_option_for_display($item)
    {
        $options = FFLCommerce_Base::get_options();
        if (!isset($item['id'])) {
            return '';
        }
        // ensure we have an id to work with
        $display = "";
        // each item builds it's output into this and it's returned for echoing
        $class = "";
        if (isset($item['class'])) {
            $class = $item['class'];
        }
        // display a tooltip if there is one in it's own table data element before the item to display
        $display .= '<td class="fflcommerce-tooltips">';
        if (!empty($item['tip'])) {
            $display .= '<a href="#" tip="' . esc_attr($item['tip']) . '" class="tips" tabindex="99"></a>';
        }
        $display .= '</td><td class="forminp">';
        $disabled = '';
        $disabledItems = array();
        if (isset($item['extra']) && isset($item['extra']['disabled'])) {
            if ($item['extra']['disabled'] === true) {
                $disabled = ' disabled';
            } else {
                if (is_array($item['extra']['disabled'])) {
                    $disabledItems = $item['extra']['disabled'];
                }
            }
        }
        /*
         *  work off the option type and format output for display for each type
         */
        switch ($item['type']) {
            case 'user_defined':
                if (isset($item['display'])) {
                    if (is_callable($item['display'], true)) {
                        $display .= call_user_func($item['display']);
                    }
                }
                break;
            case 'default_gateway':
                $id = $item['id'];
                $display .= '<select id="' . $id . '" class="fflcommerce-input fflcommerce-select ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $id . ']"' . $disabled . ' >';
                $gateways = fflcommerce_payment_gateways::get_available_payment_gateways();
                foreach ($gateways as $slug => $gateway) {
                    $display .= '<option value="' . esc_attr($slug) . '" ' . selected($options->get($id), $slug, false) . disabled(in_array($id, $disabledItems, false)) . ' />' . $gateway->title . '</option>';
                }
                $display .= '</select>';
                ?>
				<script type="text/javascript">
					/*<![CDATA[*/
					jQuery(function($){
						$("#<?php 
                echo $id;
                ?>
").select2({ width: '250px' });
					});
					/*]]>*/
				</script>
				<?php 
                break;
            case 'gateway_options':
                foreach (fflcommerce_payment_gateways::payment_gateways() as $gateway) {
                    $gateway->admin_options();
                }
                break;
            case 'shipping_options':
                foreach (fflcommerce_shipping::get_all_methods() as $shipping_method) {
                    $shipping_method->admin_options();
                }
                break;
            case 'tax_rates':
                $display .= $this->format_tax_rates_for_display($item);
                break;
            case 'single_select_page':
                $page_setting = (int) $options->get($item['id']);
                $args = array('name' => FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']', 'id' => $item['id'], 'sort_order' => 'ASC', 'echo' => 0, 'selected' => $page_setting);
                if (isset($item['extra'])) {
                    $args = wp_parse_args($item['extra'], $args);
                }
                $display .= wp_dropdown_pages($args);
                $parts = explode('<select', $display);
                $id = $item['id'];
                $display = $parts[0] . '<select id="' . $id . '" class="' . $class . '"' . $parts[1];
                ?>
				<script type="text/javascript">
					/*<![CDATA[*/
					jQuery(function($){
						$("#<?php 
                echo $id;
                ?>
").select2({ width: '250px' });
					});
					/*]]>*/
				</script>
				<?php 
                break;
            case 'single_select_country':
                $country_setting = (string) $options->get($item['id']);
                $add_empty = false;
                if (isset($item['options']['add_empty']) && $item['options']['add_empty']) {
                    $add_empty = true;
                }
                if (strstr($country_setting, ':')) {
                    $temp = explode(':', $country_setting);
                    $country = current($temp);
                    $state = end($temp);
                } else {
                    $country = $country_setting;
                    $state = '*';
                }
                $id = $item['id'];
                $display .= '<select id="' . $id . '" class="single_select_country ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"' . $disabled . '>';
                $display .= fflcommerce_countries::country_dropdown_options($country, $state, true, false, false, $add_empty);
                $display .= '</select>';
                ?>
				<script type="text/javascript">
					/*<![CDATA[*/
					jQuery(function($){
						$("#<?php 
                echo $id;
                ?>
").select2({ width: '500px' });
					});
					/*]]>*/
				</script>
				<?php 
                break;
            case 'multi_select_countries':
                $countries = fflcommerce_countries::get_countries();
                $selections = (array) $options->get($item['id']);
                $display .= '<select multiple="multiple" id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-select ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . '][]"' . $disabled . '>';
                foreach ($countries as $key => $val) {
                    $display .= '<option value="' . esc_attr($key) . '" ' . selected(in_array($key, $selections), true, false) . disabled(in_array($key, $disabledItems, false)) . ' />' . $val . '</option>';
                }
                $display .= '</select>';
                $id = $item['id'];
                ?>
				<script type="text/javascript">
					/*<![CDATA[*/
					jQuery(function($){
						$("#<?php 
                echo $id;
                ?>
").select2({ width: '500px' });
					});
					/*]]>*/
				</script>
				<?php 
                break;
            case 'button':
                if (isset($item['extra'])) {
                    $display .= '<a id="' . $item['id'] . '" class="button ' . $class . '" href="' . esc_attr($item['extra']) . '">' . esc_attr($item['desc']) . '</a>';
                }
                $item['desc'] = '';
                // temporarily remove it so it doesn't display twice
                break;
            case 'decimal':
                // decimal numbers are positive or negative 0-9 inclusive, may include decimal
                $display .= '<input	id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-text ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"
					type="number" step="any" size="20" value="' . esc_attr($options->get($item['id'])) . '"' . $disabled . ' />';
                break;
            case 'integer':
                // integer numbers are positive or negative 0-9 inclusive
            // integer numbers are positive or negative 0-9 inclusive
            case 'natural':
                // natural numbers are positive 0-9 inclusive
                $display .= '<input id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-text ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"
					type="number" size="20" value="' . esc_attr($options->get($item['id'])) . '"' . $disabled . ' />';
                break;
            case 'text':
                // any character sequence
                $display .= '<input id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-text ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"
					type="text" size="20" value="' . esc_attr($options->get($item['id'])) . '"' . $disabled . ' />';
                break;
            case 'midtext':
                $display .= '<input id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-text ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"
					type="text" size="40" value="' . esc_attr($options->get($item['id'])) . '"' . $disabled . ' />';
                break;
            case 'longtext':
                $display .= '<input id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-text ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"
					type="text" size="80" value="' . esc_attr($options->get($item['id'])) . '"' . $disabled . ' />';
                break;
            case 'email':
                $display .= '<input id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-text fflcommerce-email ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"
					type="text" size="40" value="' . esc_attr($options->get($item['id'])) . '"' . $disabled . ' />';
                break;
            case 'codeblock':
            case 'textarea':
                $cols = '60';
                if (isset($item['choices'])) {
                    $ta_options = $item['choices'];
                    if (isset($ta_options['cols'])) {
                        $cols = $ta_options['cols'];
                    }
                }
                $ta_value = stripslashes($options->get($item['id']));
                $display .= '<textarea id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-textarea ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']" cols="' . $cols . '" rows="4"' . $disabled . '>' . esc_textarea($ta_value) . '</textarea>';
                break;
            case "radio":
                // default to horizontal display of choices ( 'horizontal' may or may not be defined )
                if (!isset($item['extra']) || !in_array('vertical', $item['extra'])) {
                    $display .= '<div class="fflcommerce-radio-horz">';
                    foreach ($item['choices'] as $option => $name) {
                        $display .= '<input class="fflcommerce-input fflcommerce-radio ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"
							id="' . $item['id'] . '[' . $option . ']" type="radio" value="' . $option . '" ' . checked($options->get($item['id']), $option, false) . disabled(in_array($option, $disabledItems), true, false) . '
							/><label for="' . $item['id'] . '[' . $option . ']">' . $name . '</label>';
                    }
                    $display .= '</div>';
                } else {
                    if (isset($item['extra']) && in_array('vertical', $item['extra'])) {
                        $display .= '<ul class="fflcommerce-radio-vert">';
                        foreach ($item['choices'] as $option => $name) {
                            $display .= '<li><input class="fflcommerce-input fflcommerce-radio ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"
							id="' . $item['id'] . '[' . $option . ']" type="radio" value="' . $option . '" ' . checked($options->get($item['id']), $option, false) . disabled(in_array($option, $disabledItems), true, false) . '
							/><label for="' . $item['id'] . '[' . $option . ']">' . $name . '</label></li>';
                        }
                        $display .= '</ul>';
                    }
                }
                break;
            case 'checkbox':
                $display .= '<span class="fflcommerce-container"><input id="' . $item['id'] . '" type="checkbox" class="fflcommerce-input fflcommerce-checkbox ' . $class . '"
					name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']" ' . checked($options->get($item['id']), 'yes', false) . $disabled . '
					/><label for="' . $item['id'] . '">' . $item['name'] . '</label></span>';
                break;
            case 'multicheck':
                $multi_stored = $options->get($item['id']);
                // default to horizontal display of choices ( 'horizontal' may or may not be defined )
                if (!isset($item['extra']) || !in_array('vertical', $item['extra'])) {
                    $display .= '<div class="fflcommerce-multi-checkbox-horz ' . $class . '">';
                    foreach ($item['choices'] as $key => $option) {
                        $display .= '<input id="' . $item['id'] . '_' . $key . '" class="fflcommerce-input" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . '][' . $key . ']"
							type="checkbox" ' . checked($multi_stored[$key], true, false) . disabled(in_array($key, $disabledItems, false)) . ' /> <label for="' . $item['id'] . '_' . $key . '">' . $option . '</label>';
                    }
                    $display .= '</div>';
                } else {
                    if (isset($item['extra']) && in_array('vertical', $item['extra'])) {
                        $display .= '<ul class="fflcommerce-multi-checkbox-vert ' . $class . '">';
                        foreach ($item['choices'] as $key => $option) {
                            $display .= '<li><input id="' . $item['id'] . '_' . $key . '" class="fflcommerce-input" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . '][' . $key . ']"
							type="checkbox" ' . checked($multi_stored[$key], true, false) . disabled(in_array($key, $disabledItems, false)) . ' /> <label for="' . $item['id'] . '_' . $key . '">' . $option . '</label></li>';
                        }
                        $display .= '</ul>';
                    }
                }
                break;
            case 'range':
                $display .= '<input id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-range ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']"
					type="range" min="' . $item['extra']['min'] . '" max="' . $item['extra']['max'] . '" step="' . $item['extra']['step'] . '"
					value="' . $options->get($item['id']) . '"' . $disabled . ' />';
                break;
            case 'number':
                $display .= '<input id="' . $item['id'] . '" class="fflcommerce-input ' . $class . '" name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']" type="number" value="' . $options->get($item['id']) . '"';
                if (isset($item['extra']['min'])) {
                    $display .= ' min="' . $item['extra']['min'] . '"';
                }
                if (isset($item['extra']['max'])) {
                    $display .= ' max="' . $item['extra']['max'] . '"';
                }
                if (isset($item['extra']['step'])) {
                    $display .= ' step="' . $item['extra']['step'] . '"';
                }
                $display .= $disabled . ' />';
                break;
            case 'select':
                $multiple = !empty($item['multiple']) && $item['multiple'] == true ? 'multiple="multiple"' : "";
                $brckt = "";
                $width = 250;
                $selections = (array) $options->get($item['id']);
                if ($item['multiple']) {
                    $brckt = "[]";
                    $width = 500;
                }
                $display .= '<select id="' . $item['id'] . '" class="fflcommerce-input fflcommerce-select ' . $class . '"
					name="' . FFLCOMMERCE_OPTIONS . '[' . $item['id'] . ']' . $brckt . '"' . $multiple . $disabled . ' >';
                foreach ($item['choices'] as $value => $label) {
                    if (is_array($label)) {
                        $display .= '<optgroup label="' . $value . '">';
                        foreach ($label as $subValue => $subLabel) {
                            $display .= '<option value="' . esc_attr($subValue) . '" ' . selected(in_array(esc_attr($subValue), $selections), true, false) . disabled(in_array($subValue, $disabledItems), true, false) . ' />' . $subLabel . '</option>';
                        }
                        $display .= '</optgroup>';
                    } else {
                        $display .= '<option value="' . esc_attr($value) . '" ' . selected(in_array(esc_attr($value), $selections), true, false) . disabled(in_array($value, $disabledItems), true, false) . ' />' . $label . '</option>';
                    }
                }
                $display .= '</select>';
                $id = $item['id'];
                ?>
				<script type="text/javascript">
					/*<![CDATA[*/
					jQuery(function($){
						$("#<?php 
                echo $id;
                ?>
").select2({ width: '<?php 
                echo $width;
                ?>
px' });
					});
					/*]]>*/
				</script>
				<?php 
                break;
            default:
                fflcommerce_log("UNKOWN _type_ in Options parsing");
                fflcommerce_log($item);
        }
        if ($item['type'] != 'tab') {
            if (empty($item['desc'])) {
                $explain_value = '';
            } else {
                $explain_value = $item['desc'];
            }
            $display .= '<div class="fflcommerce-explain"><small>' . $explain_value . '</small></div></td>';
        }
        return $display;
    }
 /**
  * Validate the checkout
  */
 public function validate_checkout()
 {
     if (fflcommerce_cart::is_empty()) {
         fflcommerce::add_error(sprintf(__('Sorry, your session has expired. <a href="%s">Return to homepage &rarr;</a>', 'fflcommerce'), home_url()));
     }
     // Process Discount Codes
     if (!empty($_POST['coupon_code'])) {
         $coupon = sanitize_title($_POST['coupon_code']);
         fflcommerce_cart::add_discount($coupon);
     }
     foreach (fflcommerce_cart::get_coupons() as $coupon) {
         fflcommerce_cart::is_valid_coupon($coupon);
     }
     // Checkout fields
     $this->posted['shipping_method'] = '';
     $this->posted['shipping_service'] = '';
     if (isset($_POST['shipping_method'])) {
         $shipping_method = fflcommerce_clean($_POST['shipping_method']);
         $shipping_data = explode(':', $shipping_method);
         $this->posted['shipping_method'] = $shipping_data[0];
         $this->posted['shipping_service'] = $shipping_data[1];
     }
     $this->posted['shiptobilling'] = isset($_POST['shiptobilling']) ? fflcommerce_clean($_POST['shiptobilling']) : '';
     $this->posted['payment_method'] = isset($_POST['payment_method']) ? fflcommerce_clean($_POST['payment_method']) : '';
     $this->posted['order_comments'] = isset($_POST['order_comments']) ? fflcommerce_clean($_POST['order_comments']) : '';
     $this->posted['terms'] = isset($_POST['terms']) ? fflcommerce_clean($_POST['terms']) : '';
     $this->posted['create_account'] = isset($_POST['create_account']) ? fflcommerce_clean($_POST['create_account']) : '';
     $this->posted['account_username'] = isset($_POST['account_username']) ? fflcommerce_clean($_POST['account_username']) : '';
     $this->posted['account_password'] = isset($_POST['account_password']) ? fflcommerce_clean($_POST['account_password']) : '';
     $this->posted['account_password_2'] = isset($_POST['account_password_2']) ? fflcommerce_clean($_POST['account_password_2']) : '';
     if (fflcommerce_cart::get_total(false) == 0) {
         $this->posted['payment_method'] = 'no_payment';
     }
     // establish customer billing and shipping locations
     if (fflcommerce_cart::ship_to_billing_address_only()) {
         $this->posted['shiptobilling'] = 'true';
     }
     $country = isset($_POST['billing_country']) ? fflcommerce_clean($_POST['billing_country']) : '';
     $state = isset($_POST['billing_state']) ? fflcommerce_clean($_POST['billing_state']) : '';
     $allowed_countries = FFLCommerce_Base::get_options()->get('fflcommerce_allowed_countries');
     if ($allowed_countries === 'specific') {
         $specific_countries = FFLCommerce_Base::get_options()->get('fflcommerce_specific_allowed_countries');
         if (!in_array($country, $specific_countries)) {
             fflcommerce::add_error(__('Invalid billing country.', 'fflcommerce'));
             return;
         }
     }
     if (fflcommerce_countries::country_has_states($country)) {
         $states = fflcommerce_countries::get_states($country);
         if (!in_array($state, array_keys($states))) {
             fflcommerce::add_error(__('Invalid billing state.', 'fflcommerce'));
             return;
         }
     }
     $postcode = isset($_POST['billing_postcode']) ? fflcommerce_clean($_POST['billing_postcode']) : '';
     $ship_to_billing = FFLCommerce_Base::get_options()->get('fflcommerce_ship_to_billing_address_only') == 'yes';
     fflcommerce_customer::set_location($country, $state, $postcode);
     if (FFLCommerce_Base::get_options()->get('fflcommerce_calc_shipping') == 'yes') {
         if ($ship_to_billing || !empty($_POST['shiptobilling'])) {
             fflcommerce_customer::set_shipping_location($country, $state, $postcode);
         } else {
             $country = isset($_POST['shipping_country']) ? fflcommerce_clean($_POST['shipping_country']) : '';
             $state = isset($_POST['shipping_state']) ? fflcommerce_clean($_POST['shipping_state']) : '';
             $postcode = isset($_POST['shipping_postcode']) ? fflcommerce_clean($_POST['shipping_postcode']) : '';
             if ($allowed_countries === 'specific') {
                 $specific_countries = FFLCommerce_Base::get_options()->get('fflcommerce_specific_allowed_countries');
                 if (!in_array($country, $specific_countries)) {
                     fflcommerce::add_error(__('Invalid shipping country.', 'fflcommerce'));
                     return;
                 }
             }
             if (fflcommerce_countries::country_has_states($country)) {
                 $states = fflcommerce_countries::get_states($country);
                 if (!in_array($state, array_keys($states))) {
                     fflcommerce::add_error(__('Invalid shipping state.', 'fflcommerce'));
                     return;
                 }
             }
             fflcommerce_customer::set_shipping_location($country, $state, $postcode);
         }
     }
     // Billing Information
     foreach ($this->billing_fields as $field) {
         $field = apply_filters('fflcommerce_billing_field', $field);
         $this->posted[$field['name']] = isset($_POST[$field['name']]) ? fflcommerce_clean($_POST[$field['name']]) : '';
         // Format
         if (isset($field['format'])) {
             switch ($field['format']) {
                 case 'postcode':
                     $this->posted[$field['name']] = strtolower(str_replace(' ', '', $this->posted[$field['name']]));
                     break;
             }
         }
         // Required
         if ($field['name'] == 'billing_state' && fflcommerce_customer::has_valid_shipping_state()) {
             $field['required'] = false;
         }
         if (isset($field['required']) && $field['required'] && empty($this->posted[$field['name']])) {
             fflcommerce::add_error($field['label'] . __(' (billing) is a required field.', 'fflcommerce'));
         }
         if ($field['name'] == 'billing_euvatno') {
             $vatno = isset($this->posted['billing_euvatno']) ? $this->posted['billing_euvatno'] : '';
             $vatno = str_replace(' ', '', $vatno);
             $country = fflcommerce_tax::get_customer_country();
             // strip any country code from the beginning of the number
             if (strpos($vatno, $country) === 0) {
                 $vatno = substr($vatno, strlen($country));
             }
             if ($vatno != '') {
                 $url = 'http://isvat.appspot.com/' . $country . '/' . $vatno . '/';
                 $httpRequest = curl_init();
                 curl_setopt($httpRequest, CURLOPT_FAILONERROR, true);
                 curl_setopt($httpRequest, CURLOPT_RETURNTRANSFER, true);
                 curl_setopt($httpRequest, CURLOPT_HEADER, false);
                 curl_setopt($httpRequest, CURLOPT_URL, $url);
                 $result = curl_exec($httpRequest);
                 curl_close($httpRequest);
                 if ($result === 'false') {
                     fflcommerce_log('EU VAT validation error with URL: ' . $url);
                     fflcommerce::add_error($field['label'] . __(' (billing) is not a valid VAT Number.  Leave it blank to disable VAT validation. (VAT may be charged depending on your location)', 'fflcommerce'));
                 } else {
                     $this->valid_euvatno = fflcommerce_countries::get_base_country() != fflcommerce_tax::get_customer_country() && fflcommerce_countries::is_eu_country(fflcommerce_tax::get_customer_country());
                 }
             }
         }
         // Validation
         if (isset($field['validate']) && !empty($this->posted[$field['name']])) {
             switch ($field['validate']) {
                 case 'phone':
                     if (!fflcommerce_validation::is_phone($this->posted[$field['name']])) {
                         fflcommerce::add_error($field['label'] . __(' (billing) is not a valid number.', 'fflcommerce'));
                     }
                     break;
                 case 'email':
                     if (!fflcommerce_validation::is_email($this->posted[$field['name']])) {
                         fflcommerce::add_error($field['label'] . __(' (billing) is not a valid email address.', 'fflcommerce'));
                     }
                     break;
                 case 'postcode':
                     if (!fflcommerce_validation::is_postcode($this->posted[$field['name']], $_POST['billing_country'])) {
                         fflcommerce::add_error($field['label'] . __(' (billing) is not a valid postcode/ZIP.', 'fflcommerce'));
                     } else {
                         $this->posted[$field['name']] = fflcommerce_validation::format_postcode($this->posted[$field['name']], $_POST['billing_country']);
                     }
                     break;
             }
         }
     }
     // Shipping Information
     if (fflcommerce_shipping::is_enabled() && !fflcommerce_cart::ship_to_billing_address_only() && empty($this->posted['shiptobilling'])) {
         foreach ($this->shipping_fields as $field) {
             $field = apply_filters('fflcommerce_shipping_field', $field);
             if (isset($_POST[$field['name']])) {
                 $this->posted[$field['name']] = fflcommerce_clean($_POST[$field['name']]);
             } else {
                 $this->posted[$field['name']] = '';
             }
             // Format
             if (isset($field['format'])) {
                 switch ($field['format']) {
                     case 'postcode':
                         $this->posted[$field['name']] = strtolower(str_replace(' ', '', $this->posted[$field['name']]));
                         break;
                 }
             }
             // Required
             if ($field['name'] == 'shipping_state' && fflcommerce_customer::has_valid_shipping_state()) {
                 $field['required'] = false;
             }
             if (isset($field['required']) && $field['required'] && empty($this->posted[$field['name']])) {
                 fflcommerce::add_error($field['label'] . __(' (shipping) is a required field.', 'fflcommerce'));
             }
             // Validation
             if (isset($field['validate']) && !empty($this->posted[$field['name']])) {
                 switch ($field['validate']) {
                     case 'postcode':
                         if (!fflcommerce_validation::is_postcode($this->posted[$field['name']], $country)) {
                             fflcommerce::add_error($field['label'] . __(' (shipping) is not a valid postcode/ZIP.', 'fflcommerce'));
                         } else {
                             $this->posted[$field['name']] = fflcommerce_validation::format_postcode($this->posted[$field['name']], $country);
                         }
                         break;
                 }
             }
         }
     }
     if ($this->must_register && empty($this->posted['create_account'])) {
         fflcommerce::add_error(__('Sorry, you must agree to creating an account', 'fflcommerce'));
     }
     if ($this->must_register || empty($user_id) && $this->posted['create_account']) {
         if (!$this->show_signup) {
             fflcommerce::add_error(__('Sorry, the shop owner has disabled guest purchases.', 'fflcommerce'));
         }
         if (empty($this->posted['account_username'])) {
             fflcommerce::add_error(__('Please enter an account username.', 'fflcommerce'));
         }
         if (empty($this->posted['account_password'])) {
             fflcommerce::add_error(__('Please enter an account password.', 'fflcommerce'));
         }
         if ($this->posted['account_password_2'] !== $this->posted['account_password']) {
             fflcommerce::add_error(__('Passwords do not match.', 'fflcommerce'));
         }
         // Check the username
         if (!validate_username($this->posted['account_username'])) {
             fflcommerce::add_error(__('Invalid email/username.', 'fflcommerce'));
         } elseif (username_exists($this->posted['account_username'])) {
             fflcommerce::add_error(__('An account is already registered with that username. Please choose another.', 'fflcommerce'));
         }
         // Check the e-mail address
         if (email_exists($this->posted['billing_email'])) {
             fflcommerce::add_error(__('An account is already registered with your email address. Please login.', 'fflcommerce'));
         }
     }
     // Terms
     if (!isset($_POST['update_totals']) && empty($this->posted['terms']) && fflcommerce_get_page_id('terms') > 0) {
         fflcommerce::add_error(__('You must accept our Terms &amp; Conditions.', 'fflcommerce'));
     }
     if (fflcommerce_cart::needs_shipping()) {
         // Shipping Method
         $available_methods = fflcommerce_shipping::get_available_shipping_methods();
         if (!isset($available_methods[$this->posted['shipping_method']])) {
             fflcommerce::add_error(__('Invalid shipping method.', 'fflcommerce'));
         }
     }
 }
Exemple #4
0
 /**
  * Successful payment processing
  *
  * @param array $posted
  */
 function successful_request($posted)
 {
     $posted = stripslashes_deep($posted);
     // 'custom' holds post ID (Order ID)
     if (!empty($posted['custom']) && !empty($posted['txn_type']) && !empty($posted['invoice'])) {
         $accepted_types = array('cart', 'instant', 'express_checkout', 'web_accept', 'masspay', 'send_money', 'subscr_payment');
         $order = new fflcommerce_order((int) $posted['custom']);
         // Sandbox fix
         if (isset($posted['test_ipn']) && $posted['test_ipn'] == 1 && strtolower($posted['payment_status']) == 'pending') {
             $posted['payment_status'] = 'completed';
         }
         $merchant = $this->testmode == 'no' ? $this->email : $this->testemail;
         if ($order->status !== 'completed') {
             // We are here so lets check status and do actions
             switch (strtolower($posted['payment_status'])) {
                 case 'completed':
                     if (!in_array(strtolower($posted['txn_type']), $accepted_types)) {
                         // Put this order on-hold for manual checking
                         $order->update_status('on-hold', sprintf(__('PayPal Validation Error: Unknown "txn_type" of "%s" for Order ID: %s.', 'fflcommerce'), $posted['txn_type'], $posted['custom']));
                         exit;
                     }
                     if ($order->get_order_number() !== $posted['invoice']) {
                         // Put this order on-hold for manual checking
                         $order->update_status('on-hold', sprintf(__('PayPal Validation Error: Order Invoice Number does NOT match PayPal posted invoice (%s) for Order ID: .', 'fflcommerce'), $posted['invoice'], $posted['custom']));
                         exit;
                     }
                     // Validate Amount
                     if (number_format((double) $order->order_total, $this->decimals, '.', '') != $posted['mc_gross']) {
                         // Put this order on-hold for manual checking
                         $order->update_status('on-hold', sprintf(__('PayPal Validation Error: Payment amounts do not match initial order (gross %s).', 'fflcommerce'), $posted['mc_gross']));
                         exit;
                     }
                     if (strcasecmp(trim($posted['business']), trim($merchant)) != 0) {
                         // Put this order on-hold for manual checking
                         $order->update_status('on-hold', sprintf(__('PayPal Validation Error: Payment Merchant email received does not match PayPal Gateway settings. (%s)', 'fflcommerce'), $posted['business']));
                         exit;
                     }
                     if (!in_array($posted['mc_currency'], apply_filters('fflcommerce_multi_currencies_available', array(FFLCommerce_Base::get_options()->get('fflcommerce_currency'))))) {
                         // Put this order on-hold for manual checking
                         $order->update_status('on-hold', sprintf(__('PayPal Validation Error: Payment currency received (%s) does not match Shop currency.', 'fflcommerce'), $posted['mc_currency']));
                         exit;
                     }
                     $order->add_order_note(__('PayPal Standard payment completed', 'fflcommerce'));
                     $order->payment_complete();
                     fflcommerce_log('PAYPAL: IPN payment completed for Order ID: ' . $posted['custom']);
                     break;
                 case 'denied':
                 case 'expired':
                 case 'failed':
                 case 'voided':
                     // Failed order
                     $order->update_status('failed', sprintf(__('Payment %s via IPN.', 'fflcommerce'), strtolower($posted['payment_status'])));
                     fflcommerce_log("PAYPAL: failed order with status = " . strtolower($posted['payment_status']) . "for Order ID: " . $posted['custom']);
                     break;
                 case 'refunded':
                 case 'reversed':
                 case 'chargeback':
                     fflcommerce_log("PAYPAL: payment status type - '" . $posted['payment_status'] . "' - not supported for Order ID: " . $posted['custom']);
                     break;
             }
         }
         exit;
     } else {
         fflcommerce_log("PAYPAL: function 'successful_request' -- empty initial required values -- EXITING!\n'posted' values = " . print_r($posted, true));
     }
 }
Exemple #5
0
 /**
  * Check WorldPay origins and payment response passwords
  * Used to log and send emails for possible security errors
  */
 private function validate_response_origins_and_passwords()
 {
     $header_ip = $_SERVER['REMOTE_ADDR'];
     $header_host = gethostbyaddr($header_ip);
     $callbackPW = $this->get_post('callbackPW');
     $validated = false;
     $error = array();
     if (strpos($header_host, 'worldpay.com') !== false) {
         if (!empty($this->response_pass) && !empty($callbackPW)) {
             if ($this->response_pass == $callbackPW) {
                 $validated = true;
                 /* both passwords match */
             } else {
                 $error['validate_payment_password_error'] = sprintf(__('Your shop payment response password: \'%s\', WorldPay payment response password: \'%s\'.  The passwords for Payment Response Password from your FFL Commerce WorldPay gateway settings and your WorldPay Merchant account do NOT match.', 'fflcommerce'), $callbackPW, $this->response_pass);
                 fflcommerce_log($error['validate_payment_password_error'], 'WorldPay Gateway');
             }
         } elseif (empty($this->response_pass) && empty($callbackPW)) {
             $validated = true;
             /* skip check if no passwords supplied */
         } else {
             $error['validate_payment_password_missing'] = sprintf(__('Your shop payment response password: \'%s\', WorldPay payment response password: \'%s\'.  If you are using a Payment Response Password, make sure it is entered in BOTH the WorldPay Gateway settings in FFL Commerce AND in your WorldPay Merchant Account.', 'fflcommerce'), $callbackPW, $this->response_pass);
             fflcommerce_log($error['validate_payment_password_missing'], 'WorldPay Gateway');
         }
     } else {
         $error['validate_origin_error'] = sprintf(__('The Payment response came from IP: %s and Domain: %s -- and this does not appear to be a WorldPay domain.', 'fflcommerce'), $header_ip, $header_host);
         fflcommerce_log($error['validate_origin_error'], 'WorldPay Gateway');
     }
     if ($this->receive_err_log == 'yes' && !$validated) {
         $info = sprintf(__('Order #%s ', 'fflcommerce'), $this->get_post('cartId'));
         $this->email_worldpay_error_logs($error, $_POST, $info);
     }
     return $validated;
     /* currently we don't actually use this */
 }
Exemple #6
0
 /**
  *  Successful Payment!
  */
 public function successful_request($response, $order)
 {
     switch (strtoupper($response['OrderStatusCode'])) {
         case 'ACCEPTED':
             $order->add_order_note(__('Payment Authorized', 'fflcommerce'));
             fflcommerce_log("FuturePay: payment authorized for Order ID: " . $order->id);
             $order->payment_complete();
             break;
         case 'DECLINED':
             // Hold order
             $order->update_status('on-hold', sprintf(__('Payment %s via FuturePay.', 'fflcommerce'), strtolower($response['OrderStatusCode'])));
             fflcommerce_log("FUTUREPAY: declined order for Order ID: " . $order->id);
             break;
         default:
             // Hold order
             $order->update_status('on-hold', sprintf(__('Payment %s via FuturePay.', 'fflcommerce'), strtolower($response['OrderStatusCode'])));
             fflcommerce_log("FUTUREPAY: failed order for Order ID: " . $order->id);
             break;
     }
 }
 /**
  * Lists attributes in a html table
  *
  * @return string HTML code with attributes list.
  **/
 public function list_attributes()
 {
     // Check that we have some attributes that are visible
     if (!($this->has_attributes() || $this->has_dimensions() || $this->has_weight())) {
         return false;
     }
     // Start the html output
     $html = '<table class="shop_attributes">';
     // Output weight if we have it
     if (self::get_options()->get('fflcommerce_enable_weight') == 'yes' && $this->get_weight()) {
         $html .= '<tr><th>' . __('Weight', 'fflcommerce') . '</th><td>' . $this->get_weight() . self::get_options()->get('fflcommerce_weight_unit') . '</td></tr>';
     }
     // Output dimensions if we have it
     if (self::get_options()->get('fflcommerce_enable_dimensions') == 'yes') {
         if ($this->get_length()) {
             $html .= '<tr><th>' . __('Length', 'fflcommerce') . '</th><td>' . $this->get_length() . self::get_options()->get('fflcommerce_dimension_unit') . '</td></tr>';
         }
         if ($this->get_width()) {
             $html .= '<tr><th>' . __('Width', 'fflcommerce') . '</th><td>' . $this->get_width() . self::get_options()->get('fflcommerce_dimension_unit') . '</td></tr>';
         }
         if ($this->get_height()) {
             $html .= '<tr><th>' . __('Height', 'fflcommerce') . '</th><td>' . $this->get_height() . self::get_options()->get('fflcommerce_dimension_unit') . '</td></tr>';
         }
     }
     $attributes = $this->get_attributes();
     foreach ($attributes as $attr) {
         // If attribute is invisible skip
         if (empty($attr['visible'])) {
             continue;
         }
         // Get Title & Value from attribute array
         $name = $this->attribute_label('pa_' . $attr['name']);
         $value = null;
         if ((bool) $attr['is_taxonomy']) {
             // Get the taxonomy terms
             $product_terms = wp_get_object_terms($this->ID, 'pa_' . sanitize_title($attr['name']), array('orderby' => 'slug'));
             if (is_wp_error($product_terms)) {
                 fflcommerce_log("product::list_attributes() - Attribute for invalid taxonomy = " . $attr['name']);
                 continue;
             }
             // Convert them into a array to be imploded
             $terms = array();
             foreach ($product_terms as $term) {
                 $terms[] = '<span class="val_' . $term->slug . '">' . $term->name . '</span>';
             }
             $value = apply_filters('fflcommerce_product_attribute_value_taxonomy', implode(', ', $terms), $terms, $attr);
         } else {
             $value = apply_filters('fflcommerce_product_attribute_value_custom', wptexturize($attr['value']), $attr);
         }
         // Generate the remaining html
         $html .= "\n\t\t\t<tr class=\"attr_" . $attr['name'] . "\">\n\t\t\t\t<th>{$name}</th>\n\t\t\t\t<td>{$value}</td>\n\t\t\t</tr>";
     }
     $html .= '</table>';
     return $html;
 }
 /**
  * Checks for a valid postcode for a country
  *
  * @param   string  postcode
  * @param  string  country
  * @return  boolean
  */
 public static function is_postcode($postcode, $country)
 {
     if (strlen(trim(preg_replace('/[\\s\\-A-Za-z0-9]/', '', $postcode))) > 0) {
         return false;
     }
     $country = strtoupper(trim($country));
     $postcode = strtoupper(trim($postcode));
     // Assume that unknown countries has proper postcodes
     if (!isset(self::$postcodes[$country]) && $country !== 'GB') {
         return true;
     }
     if (FFLCommerce_Base::get_options()->get('fflcommerce_enable_postcode_validating') == 'yes') {
         switch ($country) {
             case 'GB':
                 fflcommerce_log("VALIDATE POSTCODE: country = GB");
                 return self::is_GB_postcode($postcode);
             default:
                 $regex = '/^' . self::$postcodes[$country] . '$/';
                 fflcommerce_log("VALIDATE POSTCODE: country = " . $country . " & regex = " . $regex);
                 $match = preg_match($regex, $postcode);
                 if ($match !== 1) {
                     return false;
                 }
         }
     }
     return true;
 }