Latest News: (loading..)

iofast

Members
  • Content count

    127
  • Joined

  • Last visited

1 Follower

About iofast

  • Birthday January 1

Profile Information

  1. Here is the MVS version for reference with code to filter out garbage on their "stg" server. <?php /* $Id$ osCommerce, Open Source E-Commerce Solutions http://www.oscommerce.com Copyright (c) 2010 osCommerce Copyright (c) 2013 Jared De Blander Released under the GNU General Public License Notes --------------- # ™ = TM # ® = RM Known issues 1. Stamps.com insurance is being calculated vs the original subtotal and not the discounted subtotal 2. Stamps.com insurance is limited to $5k where our OSC code is not limited. Above $5k the values will be wrong. */ define('USPS_CLASS_INCLUDE_DATE_TIME', date(DATE_RFC822) . ' : ' . rand(1000000,9999999)); class usps { var $code, $title, $description, $icon, $enabled; function usps() { global $order; $this->code = 'usps'; $this->title = MODULE_SHIPPING_USPS_TEXT_TITLE; $this->description = MODULE_SHIPPING_USPS_TEXT_DESCRIPTION; } function sort_order($vendors_id='1') { $sort_order = @[member='constant'] ('MODULE_SHIPPING_USPS_SORT_ORDER_' . $vendors_id); if (isset ($sort_order)) { $this->sort_order = $sort_order; } else { $this->sort_order = '-'; } return $this->sort_order; } function tax_class($vendors_id='1') { $this->tax_class = constant('MODULE_SHIPPING_USPS_TAX_CLASS_' . $vendors_id); return $this->tax_class; } function enabled($vendors_id='1') { $this->enabled = false; $status = @[member='constant']('MODULE_SHIPPING_USPS_STATUS_' . $vendors_id); if (isset ($status) && $status != '') { $this->enabled = (($status == 'True') ? true : false); } if (($this->enabled == true) && ((int)constant('MODULE_SHIPPING_USPS_ZONE_' . $vendors_id) > 0)) { $check_flag = false; $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . (int)constant('MODULE_SHIPPING_USPS_ZONE_' . $vendors_id) . "' and zone_country_id = '" . $this->delivery_country_id . "' order by zone_id"); while ($check = tep_db_fetch_array($check_query)) { if ($check['zone_id'] < 1) { $check_flag = true; break; } elseif ($check['zone_id'] == $this->delivery_zone_id) { $check_flag = true; break; } } if ($check_flag == false) { $this->enabled = false; } } return $this->enabled; } function zones($vendors_id='1') { if ( ($this->enabled == true) && ((int)constant('MODULE_SHIPPING_USPS_ZONE_' . $vendors_id) > 0) ) { $check_flag = false; $check_query = tep_db_query("select zone_id from " . TABLE_ZONES_TO_GEO_ZONES . " where geo_zone_id = '" . (int)constant('MODULE_SHIPPING_USPS_ZONE_' . $vendors_id) . "' and zone_country_id = '" . $this->delivery_country_id . "' order by zone_id"); while ($check = tep_db_fetch_array($check_query)) { if ($check['zone_id'] < 1) { $check_flag = true; break; } elseif ($check['zone_id'] == $this->delivery_zone_id) { $check_flag = true; break; } } if ($check_flag == false) { $this->enabled = false; } } return $this->enabled; } function stamps_com_insurance_rate($insured_amount, $international = false){ # Insurance rates are linear after $200. Prior to this they are non-linear # comment out next line to enable stamps.com insurance. comment it to enable. # return 0; # $200+ International rate variables $intl_200_base_rate = 5.04; $intl_200_multilplier = 0.99; # $200+ Domestic rate variables $dom_200_base_rate = 4.36; $dom_200_multilplier = 0.99; if($insured_amount < 200){ if($international){ # International table lookup for < $200 if ($insured_amount > 100) return 4.05; if ($insured_amount > 50) return 3.06; return 2.07; } else { # Domestic table lookup for < $200 if ($insured_amount > 100) return 2.61; if ($insured_amount > 50) return 2.11; return 1.66; } }else{ $multiplier = ( (int) ($insured_amount / 100) ) - 2; if($international){ # International calculation for > $200 return $intl_200_base_rate + $intl_200_multilplier * $multiplier; }else{ # Domestic calculation for > $200 return $dom_200_base_rate + $dom_200_multilplier * $multiplier; } } } function quote($method = '', $module = '', $vendors_id = '1') { global $order, $shipping_weight, $shipping_num_boxes, $currencies, $shipping; $iInfo = ''; $methods = array(); $shipping_weight = ($shipping_weight < 0.0625 ? 0.0625 : $shipping_weight); $this->pounds = (int)$shipping_weight; $this->ounces = ceil(round(16 * ($shipping_weight - $this->pounds))); $vendors_data_query = tep_db_query(" SELECT handling_charge, handling_per_box, vendor_country, vendors_zipcode FROM " . TABLE_VENDORS . " WHERE vendors_id = '" . (int)$vendors_id . "' "); $vendors_data = tep_db_fetch_array($vendors_data_query); $uspsQuote = $this->_getQuote($vendors_id, $vendors_data); #var_dump($uspsQuote); # error_log("quote=".print_r($uspsQuote, true)); $country_name = tep_get_countries($vendors_data['vendor_country'], true); $handling_charge = $vendors_data['handling_charge']; $handling_per_box = $vendors_data['handling_per_box']; if ($handling_charge > $handling_per_box*$shipping_num_boxes) { $handling = $handling_charge; } else { $handling = $handling_per_box*$shipping_num_boxes; } if (isset($uspsQuote['Number'])) return false; if ($order->delivery['country']['iso_code_2'] == 'US') { $dExtras = array(); $dOptions = explode(", ", constant( 'MODULE_SHIPPING_USPS_DMST_SERVICES_' . $vendors_id)); foreach ($dOptions as $key => $val) { if(strlen($dOptions[$key]) > 1) { if ($dOptions[$key+1] == 'C' || $dOptions[$key+1] == 'S' || $dOptions[$key+1] == 'H'){ $dExtras[$dOptions[$key]] = $dOptions[$key+1]; } } } } else { $iExtras = array(); # This had an errant space after the single quote in the constant function. Removed Jan. 24th, 2013 $iOptions = explode(', ',constant( 'MODULE_SHIPPING_USPS_INTL_SERVICES_' . $vendors_id)); foreach ($iOptions as $key => $val){ if(strlen($iOptions[$key]) > 1){ if ($iOptions[$key+1] == 'C' || $iOptions[$key+1] == 'S' || $iOptions[$key+1] == 'H') { $iExtras[$iOptions[$key]] = $iOptions[$key+1]; } } } # This was not an MVS constant prior to Jan. 24th, 2013 if (constant('MODULE_SHIPPING_USPS_REGULATIONS_' . $vendors_id) == 'True'){ $iInfo = '<div id="iInfo">' . '<div id="showInfo" class="ui-state-error" style="cursor:pointer; text-align:center;" onclick="$(\'#showInfo\').hide();$(\'#hideInfo, #Info\').show();">' . MODULE_SHIPPING_USPS_TEXT_INTL_SHOW . '</div>' . '<div id="hideInfo" class="ui-state-error" style="cursor:pointer; text-align:center; display:none;" onclick="$(\'#hideInfo, #Info\').hide();$(\'#showInfo\').show();">' . MODULE_SHIPPING_USPS_TEXT_INTL_HIDE .'</div>' . '<div id="Info" class="ui-state-highlight" style="display:none; padding:10px; max-height:200px; overflow:auto;">' . '<b>Prohibitions:</b><br>' . nl2br($uspsQuote['Package']['Prohibitions']) . '<br><br><b>Restrictions:</b><br>' . nl2br($uspsQuote['Package']['Restrictions']) . '<br><br><b>Observations:</b><br>' . nl2br($uspsQuote['Package']['Observations']) . '<br><br><b>CustomsForms:</b><br>' . nl2br($uspsQuote['Package']['CustomsForms']) . '<br><br><b>ExpressMail:</b><br>' . nl2br($uspsQuote['Package']['ExpressMail']) . '<br><br><b>AreasServed:</b><br>' . nl2br($uspsQuote['Package']['AreasServed']) . '<br><br><b>AdditionalRestrictions:</b><br>' . nl2br($uspsQuote['Package']['AdditionalRestrictions']) .'</div>' . '</div>'; } } if (isset($uspsQuote['Package']['Postage']) && tep_not_null($uspsQuote['Package']['Postage'])){ $PackageSize = 1; } else { $PackageSize = ($order->delivery['country']['iso_code_2'] == 'US' ? sizeof($uspsQuote['Package']) : sizeof($uspsQuote['Package']['Service'])); } for ($i=0; $i<$PackageSize; $i++) { $Services = array(); $shownServices = array(); $hiddenServices = array(); $customerServices = array(); $hiddenCost = 0; $shownCost = 0; $shownString = ''; $customerString = ''; $handling = 0; $types = explode(", ", constant('MODULE_SHIPPING_USPS_TYPES_' . $vendors_id)); if (isset($uspsQuote['Package'][$i]['Error']) && tep_not_null($uspsQuote['Package'][$i]['Error'])) continue; $Package = ($PackageSize == 1 ? $uspsQuote['Package']['Postage'] : ($order->delivery['country']['iso_code_2'] == 'US' ? $uspsQuote['Package'][$i]['Postage'] : $uspsQuote['Package']['Service'][$i])); if ($order->delivery['country']['iso_code_2'] == 'US') { if (tep_not_null($Package['SpecialServices']['SpecialService'])){ foreach ($Package['SpecialServices']['SpecialService'] as $key => $val){ if (isset($dExtras[$val['ServiceName']]) && tep_not_null($dExtras[$val['ServiceName']]) && ((constant('MODULE_SHIPPING_USPS_RATE_TYPE_' . $vendors_id) == 'Online' && $val['AvailableOnline'] == 'true') || (constant('MODULE_SHIPPING_USPS_RATE_TYPE_' . $vendors_id) == 'Retail' && $val['Available'] == 'true'))) { $val['ServiceAdmin'] = $dExtras[$val['ServiceName']]; $Services[] = $val; } } } $cost = constant('MODULE_SHIPPING_USPS_RATE_TYPE_' . $vendors_id) == 'Online' && tep_not_null($Package['CommercialRate']) ? $Package['CommercialRate'] : $Package['Rate']; $type = ($Package['MailService']); # Filters for priority mail/priority mail express # Priority Mail if(preg_match('#Priority Mail [0-9]-DayTM#i', $type)){ $type = preg_replace('#Priority Mail [0-9]-DayTM#i', 'Priority MailTM', $type); }elseif(preg_match('#Priority Mail MilitaryTM#i', $type)){ $type = preg_replace('#Priority Mail MilitaryTM#i', 'Priority MailTM', $type); }elseif(preg_match('#Priority Mail DPOTM#i', $type)){ $type = preg_replace('#Priority Mail DPOTM#i', 'Priority MailTM', $type); }elseif(preg_match('#Priority Mail Express [0-9]-DayTM#i', $type)){ $type = preg_replace('#Priority Mail Express [0-9]-DayTM#i', 'Priority Mail ExpressTM', $type); }elseif(preg_match('#Priority Mail Express MilitaryTM#i', $type)){ $type = preg_replace('#Priority Mail Express MilitaryTM#i', 'Priority Mail ExpressTM', $type); }elseif(preg_match('#Priority Mail Express DPOTM#i', $type)){ $type = preg_replace('#Priority Mail Express DPOTM#i', 'Priority Mail ExpressTM', $type); }elseif(isset($uspsQuote['Package'][$i]['FirstClassMailType'])){ if($uspsQuote['Package'][$i]['FirstClassMailType'] == 'FLAT'){ $type = 'First-Class MailRM Large Envelope'; }elseif($uspsQuote['Package'][$i]['FirstClassMailType'] == 'PARCEL'){ $type = 'First-Class MailRM Parcel'; } } } else { foreach ($Package['ExtraServices']['ExtraService'] as $key => $val){ if (isset($iExtras[$val['ServiceName']]) && tep_not_null($iExtras[$val['ServiceName']]) && ((constant('MODULE_SHIPPING_USPS_RATE_TYPE_' . $vendors_id) == 'Online' && $val['AvailableOnline'] == 'True') || (constant('MODULE_SHIPPING_USPS_RATE_TYPE_' . $vendors_id) == 'Retail' && $val['Available'] == 'True'))) { $val['ServiceAdmin'] = $iExtras[$val['ServiceName']]; $Services[] = $val; } } $cost = constant('MODULE_SHIPPING_USPS_RATE_TYPE_' . $vendors_id) == 'Online' && tep_not_null($Package['CommercialPostage']) ? $Package['CommercialPostage'] : $Package['Postage']; $type = ($Package['SvcDescription']); } if ($cost == 0) continue; foreach ($types as $key => $val){ if(!is_numeric($val) && $val == $type) { $minweight = $types[$key+1]; $maxweight = $types[$key+2]; $handling = $types[$key+3]; } } foreach ($Services as $key => $val) { $sDisplay = $Services[$key]['ServiceAdmin']; if ($sDisplay == 'H') $hiddenServices[] = array($Services[$key]['ServiceName'] => (constant('MODULE_SHIPPING_USPS_RATE_TYPE_' . $vendors_id) == 'Online' ? $Services[$key]['PriceOnline'] : $Services[$key]['Price'])); elseif ($sDisplay == 'S') $shownServices[] = array($Services[$key]['ServiceName'] => (constant('MODULE_SHIPPING_USPS_RATE_TYPE_' . $vendors_id) == 'Online' ? $Services[$key]['PriceOnline'] : $Services[$key]['Price'])); elseif ($sDisplay == 'C') $customerServices[] = array($Services[$key]['ServiceName'] => (constant('MODULE_SHIPPING_USPS_RATE_TYPE_' . $vendors_id) == 'Online' ? $Services[$key]['PriceOnline'] : $Services[$key]['Price'])); } foreach($hiddenServices as $key => $val){ foreach($hiddenServices[$key] as $key1 => $val1){ $hiddenCost += $val1; } } if (sizeof($shownServices) > 0) { $shownString = '<div id="shownString" style="float:right; padding-right:10px; display:none;">' . '<div id="shownStringShow" style="cursor:pointer; text-align:center;" onclick="$(\'#shownStringShow\', $(this).parent().parent()).hide();$(\'#shownStringHide, #shownStringInfo\', $(this).parent().parent()).show();">' . MODULE_SHIPPING_USPS_TEXT_SSTRING_SHOW . '</div>' . '<div id="shownStringHide" style="cursor:pointer; text-align:center; display:none;" onclick="$(\'#shownStringHide, #shownStringInfo\', $(this).parent().parent()).hide();$(\'#shownStringShow\', $(this).parent().parent()).show();">' . MODULE_SHIPPING_USPS_TEXT_SSTRING_HIDE .'</div>' . '</div><div style="clear:both;"></div>' . '<div id="shownStringInfo" style="display:none;">' . '<div style="padding-left:20px; float:left;">' . MODULE_SHIPPING_USPS_TEXT_BASE_COST . '</div><div style="padding-right:20px; float:right;">' . ($cost == 0 ? MODULE_SHIPPING_USPS_TEXT_FREE : $currencies->format(($cost + (sizeof($hiddenServices) > 0 ? $handling + $hiddenCost : 0)) * $shipping_num_boxes)) . '</div><div style="clear:both;"></div>' ; if (sizeof($hiddenServices) == 0){ $shownString .= '<div style="padding-left:20px; float:left;">' . MODULE_SHIPPING_USPS_TEXT_HANDLING_COST . '</div>'. '<div style="padding-right:20px; float:right;">' . ($handling== 0 ? MODULE_SHIPPING_USPS_TEXT_FREE : $currencies->format($handling * $shipping_num_boxes)) . '</div>'. '<div style="clear:both;"></div>'. '<div style="clear:both;"></div>' ; } foreach($shownServices as $key => $val){ foreach($shownServices[$key] as $key1 => $val1){ $shownString .= '<div style="padding-left:20px; float:left;">' . str_replace(array('RM', 'TM'), array('®', '™'), $key1) . '</div><div style="padding-right:20px; float:right;">' . ($val1 == 0 ? MODULE_SHIPPING_USPS_TEXT_FREE : $currencies->format($val1 * $shipping_num_boxes)) . '</div><div style="clear:both;"></div>'; $shownCost = $shownCost + $val1; } } $shownString .= '</div><div style="clear:both;"></div>'; } if (sizeof($customerServices) > 0) { $customerString = '<div id="customerString" style="float:right; padding-right:10px; display:none;">' . '<div id="customerStringShow" style="cursor:pointer; text-align:center;" onclick="$(\'#customerStringShow\', $(this).parent().parent()).hide();$(\'#customerStringHide, #customerStringInfo\', $(this).parent().parent()).show();">' . MODULE_SHIPPING_USPS_TEXT_CSTRING_SHOW . '</div>' . '<div id="customerStringHide" style="cursor:pointer; text-align:center; display:none;" onclick="$(\'#customerStringHide, #customerStringInfo\', $(this).parent().parent()).hide();$(\'#customerStringShow\', $(this).parent().parent()).show();">' . MODULE_SHIPPING_USPS_TEXT_CSTRING_HIDE .'</div>' . '</div><div style="clear:both;"></div>' . '<div id="customerStringInfo" style="display:none;">'; foreach($customerServices as $key => $val) { foreach($customerServices[$key] as $key1 => $val1) { $customerString .= '<div style="padding-left:20px; float:left;">' . str_replace(array('RM', 'TM'), array('®', '™'), $key1) . '</div>'. '<div style="padding-right:20px; float:right;"><input type="checkbox" name="' . $key1 . '" value="' . $val1 * $shipping_num_boxes . '" id="' . $type . '"></div>'. '<div style="padding-right:5px; float:right;">' . ($val1 == 0 ? MODULE_SHIPPING_USPS_TEXT_FREE : $currencies->format($val1 * $shipping_num_boxes)) . '</div>'. '<div style="clear:both;"></div>' ; $customerCost = $customerCost + $val1; } } $customerString .= '</div><div style="clear:both;"></div>'; } if ((($method == '' && in_array($type, $types)) || $method == $type) && $shipping_weight < $maxweight && $shipping_weight > $minweight){ $methods[] = array( 'id' => $type, 'title' => str_replace(array('RM', 'TM', '**'), array('®', '™', ''), $type), 'cost' => ($cost + $handling + $hiddenCost + $shownCost) * $shipping_num_boxes, 'shownString' => (string)$shownString, 'customerString' => (string)$customerString ); } } if (sizeof($methods) == 0) return false; # Custom markup and insurance pricing # print_r(get_defined_constants(true)); $calculated_markup = 1 + (((int) constant('MODULE_SHIPPING_USPS_FLAT_RATE_' . $vendors_id)) / 100); foreach($methods as $c=>$key){ # Add custom markup $new_cost = round($key['cost'] * $calculated_markup, 2); # 5% markup $insurance_fee = 0; # Insure FCM packages if(!stristr($methods[$c]['title'], 'First-Class') === false){ if(!stristr($methods[$c]['title'], 'International') === false){ # First Class International if(constant('MODULE_SHIPPING_USPS_FCM_INTL_STAMPS_INSUR_' . $vendors_id) == 'True'){ $insurance_fee = $this->stamps_com_insurance_rate($order->info['subtotal'], true); $methods[$c]['title'] .= ' - Insured ($ ' . number_format($insurance_fee, 2) .')<br> '; } $methods[$c]['title'] .= ' - <u>Not a tracked shipping method</u>'; }else{ # First Class Domestic if(constant('MODULE_SHIPPING_USPS_FCM_DOM_STAMPS_INSUR_' . $vendors_id) == 'True'){ $insurance_fee = $this->stamps_com_insurance_rate($order->info['subtotal'], false); $methods[$c]['title'] .= ' - Insured ($ ' . number_format($insurance_fee, 2) .')<br> '; } $methods[$c]['title'] .= ' - <u>Not a tracked shipping method</u>'; } } $new_cost += $insurance_fee; $methods[$c]['cost'] = round($new_cost, 2); } reset($methods); if (sizeof($methods) > 1) { foreach($methods as $c=>$key) { $sort_cost[] = $key['cost']; $sort_id[] = $key['id']; } array_multisort($sort_cost, (constant('MODULE_SHIPPING_USPS_RATE_SORTER_' . $vendors_id) == 'Ascending' ? SORT_ASC : SORT_DESC), $sort_id, SORT_ASC, $methods); } $this->quotes = array( 'id' => $this->code, 'module' => $this->title . ' ' . $this->pounds . ' lbs, ' . $this->ounces . ' oz', 'methods' => $methods, 'tax' => $this->tax_class($vendors_id) > 0 ? tep_get_tax_rate($this->tax_class($vendors_id), $order->delivery['country']['id'], $order->delivery['zone_id']) : null, 'icon' => tep_not_null($this->icon) || tep_not_null($iInfo) ? (tep_not_null($this->icon) ? tep_image($this->icon, $this->title) : '') . (tep_not_null($iInfo) ? '<br>' . $iInfo : '') : null ); return $this->quotes; } function _getQuote($vendors_id, $vendors_data) { global $order, $shipping_weight; # Settings $testing = false; $send_emails = false; $log_files = false; $log_path = dirname(__FILE__).'/'; if($testing){ $usps_shipping_server = 'stg-production.shippingapis.com'; $usps_shipping_api_dll = 'ShippingApi.dll'; }else{ $usps_shipping_server = 'production.shippingapis.com'; $usps_shipping_api_dll = 'shippingapi.dll'; } # Check for domestic or international delivery if ($order->delivery['country']['iso_code_2'] == 'US'){ # Domestic delivery $package_count = 0; # Filter zip code $ZipDestination = substr(str_replace(' ', '', $order->delivery['postcode']), 0, 5); # Build Domestic XML $request = '<RateV4Request USERID="' . constant('MODULE_SHIPPING_USPS_USERID_' . $vendors_id) . '">' . '<Revision>2</Revision>'; # Cycle through our selected types for domestic foreach(explode(', ', constant('MODULE_SHIPPING_USPS_TYPES_' . $vendors_id)) as $request_type) { if(is_numeric($request_type) || preg_match('#International#' , $request_type)) continue; $FirstClassMailType = ''; $Container = 'VARIABLE'; if (preg_match('#First\-Class#', $request_type)) { # Determine First-Class Mail settings if ($shipping_weight > 13/16) { # If too heavy for FCM skip this package type continue; } else { $service = 'First-Class Mail'; if ($request_type == 'First-Class MailRM Large Envelope'){ $FirstClassMailType = 'FLAT'; } else { $FirstClassMailType = 'PARCEL'; } } } # Determine media mail settings elseif ($request_type == 'Media MailRM') $service = 'MEDIA'; # Determine standard post / parcel post settings elseif ($request_type == 'Standard PostRM') $service = 'PARCEL'; # Determine priority mail settings elseif (preg_match('#Priority MailTM#', $request_type)) { $service = 'PRIORITY COMMERCIAL'; if ($request_type == 'Priority MailTM Flat Rate Envelope') $Container = 'FLAT RATE ENVELOPE'; elseif ($request_type == 'Priority MailTM Legal Flat Rate Envelope') $Container = 'LEGAL FLAT RATE ENVELOPE'; elseif ($request_type == 'Priority MailTM Padded Flat Rate Envelope') $Container = 'PADDED FLAT RATE ENVELOPE'; elseif ($request_type == 'Priority MailTM Small Flat Rate Box') $Container = 'SM FLAT RATE BOX'; elseif ($request_type == 'Priority MailTM Medium Flat Rate Box') $Container = 'MD FLAT RATE BOX'; elseif ($request_type == 'Priority MailTM Large Flat Rate Box') $Container = 'LG FLAT RATE BOX'; elseif ($request_type == 'Priority MailTM Regional Rate Box A') $Container = 'REGIONALRATEBOXA'; elseif ($request_type == 'Priority MailTM Regional Rate Box B') $Container = 'REGIONALRATEBOXB'; elseif ($request_type == 'Priority MailTM Regional Rate Box C') $Container = 'REGIONALRATEBOXC'; } # Determine Express Mail Settings elseif (preg_match('#Priority Mail ExpressTM#', $request_type)){ $service = 'EXPRESS COMMERCIAL'; if ($request_type == 'Priority Mail ExpressTM Flat Rate Envelope') $Container = 'FLAT RATE ENVELOPE'; elseif ($request_type == 'Priority Mail ExpressTM Legal Flat Rate Envelope') $Container = 'LEGAL FLAT RATE ENVELOPE'; elseif ($request_type == 'Priority Mail ExpressTM Flat Rate Boxes') $Container = 'FLAT RATE BOX'; } # If a valid mail setting was not found skip this package else continue; # Create XML for this package using settings determined above $request .= '<Package ID="' . $package_count . '">' . '<Service>' . $service . '</Service>' . ($FirstClassMailType != '' ? '<FirstClassMailType>' . $FirstClassMailType . '</FirstClassMailType>' : '') . '<ZipOrigination>' . $vendors_data['vendors_zipcode'] . '</ZipOrigination>' . '<ZipDestination>' . $ZipDestination . '</ZipDestination>' . '<Pounds>' . $this->pounds . '</Pounds>' . '<Ounces>' . $this->ounces . '</Ounces>' . '<Container>' . $Container . '</Container>' . '<Size>REGULAR</Size>' . '<Machinable>TRUE</Machinable>' . '</Package>'; $package_count++; } $request .= '</RateV4Request>'; if($send_emails) tep_mail(STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, 'USPS XML Request: ' . USPS_CLASS_INCLUDE_DATE_TIME, $request, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS); if($log_files) file_put_contents ($log_path . 'usps_last_request.txt', $request); # Define request type $request = 'API=RateV4&XML=' . urlencode($request); } else { #International delivery # Build XML $request = '<IntlRateV2Request USERID="' . constant('MODULE_SHIPPING_USPS_USERID_' . $vendors_id) . '">' . '<Revision>2</Revision>' . '<Package ID="0">' . '<Pounds>' . $this->pounds . '</Pounds>' . '<Ounces>' . $this->ounces . '</Ounces>' . '<MailType>All</MailType>' . '<GXG>' . '<POBoxFlag>N</POBoxFlag>' . '<GiftFlag>N</GiftFlag>' . '</GXG>' . '<ValueOfContents>' . ($order->info['subtotal'] + $order->info['tax']) . '</ValueOfContents>' . '<Country>' . tep_get_country_name($order->delivery['country']['id']) . '</Country>' . '<Container>RECTANGULAR</Container>' . '<Size>LARGE</Size>' . '<Width>2</Width>' . '<Length>10</Length>' . '<Height>6</Height>' . '<Girth>0</Girth>' . '<OriginZip>' . $vendors_data['vendors_zipcode'] . '</OriginZip>' . '<CommercialFlag>N</CommercialFlag>' . '<ExtraServices>' . '<ExtraService>0</ExtraService>' . '<ExtraService>1</ExtraService>' . '<ExtraService>2</ExtraService>' . '<ExtraService>3</ExtraService>' . '<ExtraService>5</ExtraService>' . '<ExtraService>6</ExtraService>' . '</ExtraServices>' . '</Package>' . '</IntlRateV2Request>' ; if($send_emails) tep_mail(STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, 'USPS XML Request: ' . USPS_CLASS_INCLUDE_DATE_TIME, $request, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS); if($log_files) file_put_contents ($log_path . 'usps_last_request.txt', $request); # Define request type $request = 'API=IntlRateV2&XML=' . urlencode($request); } # Transmit request $body = ''; $http = new httpClient(); # Connect to USPS and retrieve rate data if ($http->Connect($usps_shipping_server, 80)) { $http->addHeader('Host', $usps_shipping_server); $http->addHeader('User-Agent', 'osCommerce'); $http->addHeader('Connection', 'Close'); if ($http->Get('/'.$usps_shipping_api_dll.'?' . $request)) { $body = trim($http->getBody()); if($send_emails) tep_mail(STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, 'USPS XML Response : ' . USPS_CLASS_INCLUDE_DATE_TIME, $body, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS); if($log_files) file_put_contents ($log_path . 'usps_last_response.txt', $body); $body_lines = preg_split ('/$\R?^/m', $body); if($log_files) file_put_contents ($log_path . 'usps_last_response_lines.txt', print_r($body_lines,true)); $body_lines = array_filter( preg_split ('/$\R?^/m', $body), function($check){ return strpos($check,'<') !== false; } ); array_walk( $body_lines, function (&$item, $key){ $item = trim($item); } ); $body = implode('', $body_lines); } $http->Disconnect(); if($log_files) file_put_contents ($log_path . 'usps_last_response_filtered.txt', $body); # Return the XML document we received as an associative array $body_return = json_decode(json_encode(simplexml_load_string($body)),TRUE); if($log_files) file_put_contents ($log_path . 'usps_last_response_return_array_pre_filter.txt', print_r($body_return,true)); # Perform a preg_replace on the body_return array # ™ = TM # ® = RM array_walk_recursive($body_return, function(&$item, $key){ $item = preg_replace( array( '/\<sup\>\&reg;\<\/sup\>/', '/\<sup\>\&trade;\<\/sup\>/', '/\" /', '/\",/', '/\"<br>/', '/<br>/', '/\<sup\>\®\<\/sup\>/', '/\<sup\>\™\<\/sup\>/' ), array( 'RM', 'TM', '",', '" ', '"<br>', 'BREAK', 'RM', 'TM' ), $item ); }); if($log_files) file_put_contents ($log_path . 'usps_last_response_return_array_post_filter.txt', print_r($body_return,true)); return $body_return; } else { return false; } } function install($vendors_id = '1') { tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Enable USPS Shipping', 'MODULE_SHIPPING_USPS_STATUS_" . $vendors_id . "', 'True', 'Do you want to offer USPS shipping?', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')"); tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Enter the USPS User ID', 'MODULE_SHIPPING_USPS_USERID_" . $vendors_id . "', 'NONE', 'Enter the USPS USERID assigned to you.', '6', '0', now(), '" . $vendors_id . "')"); tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added, vendors_id) values ('Tax Class', 'MODULE_SHIPPING_USPS_TAX_CLASS_" . $vendors_id . "', '0', 'Use the following tax class on the shipping fee.', '6', '0', 'tep_get_tax_class_title', 'tep_cfg_pull_down_tax_classes(', now(), '" . $vendors_id . "')"); tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added, vendors_id) values ('Shipping Zone', 'MODULE_SHIPPING_USPS_ZONE_" . $vendors_id . "', '0', 'If a zone is selected, only enable this shipping method for that zone.', '6', '0', 'tep_get_zone_class_title', 'tep_cfg_pull_down_zone_classes(', now(), '" . $vendors_id . "')"); tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Sort Order', 'MODULE_SHIPPING_USPS_SORT_ORDER_" . $vendors_id . "', '0', 'Sort order of display.', '6', '0', now(), '" . $vendors_id . "')"); tep_db_query(" insert into " . TABLE_VENDOR_CONFIGURATION . " ( configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id ) values ( 'Shipping Methods (Domestic and International)', 'MODULE_SHIPPING_USPS_TYPES_" . $vendors_id . "', '0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00, 0, 70, 0.00', '<b><u>Checkbox:</u></b> Select the services to be offered<br><b><u>Minimum Weight (lbs)</u></b>first input field<br><b><u>Maximum Weight (lbs):</u></b>second input field<br><br>USPS returns methods based on cart weights. These settings will allow further control (particularly helpful for flat rate methods) but will not override USPS limits', '6', '0', 'tep_cfg_usps_services( array( \'First-Class MailRM Large Envelope\', \'First-Class MailRM Parcel\', \'Media MailRM\', \'Standard PostRM\', \'Priority MailTM\', \'Priority MailTM Flat Rate Envelope\', \'Priority MailTM Legal Flat Rate Envelope\', \'Priority MailTM Padded Flat Rate Envelope\', \'Priority MailTM Small Flat Rate Box\', \'Priority MailTM Medium Flat Rate Box\', \'Priority MailTM Large Flat Rate Box\', \'Priority MailTM Regional Rate Box A\', \'Priority MailTM Regional Rate Box B\', \'Priority MailTM Regional Rate Box C\', \'Priority Mail ExpressTM\', \'Priority Mail ExpressTM Flat Rate Envelope\', \'Priority Mail ExpressTM Legal Flat Rate Envelope\', \'Priority Mail ExpressTM Flat Rate Boxes\', \'First-Class MailRM International Large Envelope**\', \'First-Class Package International ServiceTM**\', \'Priority Mail InternationalRM\', \'Priority Mail InternationalRM Flat Rate Envelope**\', \'Priority Mail InternationalRM Small Flat Rate Box**\', \'Priority Mail InternationalRM Medium Flat Rate Box\', \'Priority Mail InternationalRM Large Flat Rate Box\', \'Priority Mail Express InternationalTM\', \'Priority Mail Express InternationalTM Flat Rate Envelope\', \'Priority Mail Express InternationalTM Flat Rate Boxes\', \'USPS GXGTM Envelopes**\', \'Global Express GuaranteedRM (GXG)**\' ),', now(), '" . $vendors_id . "') "); tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Extra Services (Domestic)', 'MODULE_SHIPPING_USPS_DMST_SERVICES_" . $vendors_id . "', 'Certified MailRM, N, Insurance, N, Adult Signature Restricted Delivery, N, Registered without Insurance, N, Registered MailTM, N, Collect on Delivery, N, Return Receipt for Merchandise, N, Return Receipt, N, Certificate of Mailing, N, Express Mail Insurance, N, Delivery ConfirmationTM, N, Signature ConfirmationTM, N', 'N: Not Availble<br>S: Forced (Show)<br>H: Forced (Hide)', '6', '0', 'tep_cfg_usps_extraservices(array(\'Certified MailRM\', \'Insurance\', \'Adult Signature Restricted Delivery\', \'Registered without Insurance\', \'Registered MailTM\', \'Collect on Delivery\', \'Return Receipt for Merchandise\', \'Return Receipt\', \'Certificate of Mailing\', \'Express Mail Insurance\', \'Delivery ConfirmationTM\', \'Signature ConfirmationTM\'), ', now(), '" . $vendors_id . "')"); tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Extra Services (International)', 'MODULE_SHIPPING_USPS_INTL_SERVICES_" . $vendors_id . "', 'Registered Mail, N, Insurance, N, Return Receipt, N, Restricted Delivery, N, Pick-Up, N, Certificate of Mailing, N', 'N: Not Availble<br>S: Forced (Show)<br>H: Forced (Hide)', '6', '0', 'tep_cfg_usps_extraservices(array(\'Registered Mail\', \'Insurance\', \'Return Receipt\', \'Restricted Delivery\', \'Pick-Up\', \'Certificate of Mailing\'), ', now(), '" . $vendors_id . "')"); tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Retail pricing or Online pricing?', 'MODULE_SHIPPING_USPS_RATE_TYPE_" . $vendors_id . "', 'Online', 'Rates will be returned ONLY for methods available in this pricing type. Applies to prices <u>and</u> add on services', '6', '0', 'tep_cfg_select_option(array(\'Retail\', \'Online\'), ', now(), '" . $vendors_id . "')"); tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Rates Sort Order:', 'MODULE_SHIPPING_USPS_RATE_SORTER_" . $vendors_id . "', 'Ascending', 'Ascending: Low to High<br>Descending: High to Low', '6', '0', 'tep_cfg_select_option(array(\'Ascending\', \'Descending\'), ', now(), '" . $vendors_id . "')"); tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Show International Regulations:', 'MODULE_SHIPPING_USPS_REGULATIONS_" . $vendors_id . "', 'True', 'Displays international regulations and customs information.', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')"); ################################################################# # Begin 2013 Additions by Jared De Blander ################################################################# # These additions include the following # # 1. Enable/Disable FCM Intl Insurance # 2. Enable/Disable FCM Domestic Insurance. # 3. Adjustable Flat Mark-Up Rate. # # New constants - appended with vendor id # # MODULE_SHIPPING_USPS_FCM_INTL_STAMPS_INSUR_ # MODULE_SHIPPING_USPS_FCM_DOM_STAMPS_INSUR_ # MODULE_SHIPPING_USPS_FLAT_RATE_ # Enable/Disable FCM Intl Insurance tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Enable Stamps.com First Class International Insurance:', 'MODULE_SHIPPING_USPS_FCM_INTL_STAMPS_INSUR_" . $vendors_id . "', 'False', 'Enables the function call to enable Stamps.com insurance on First-Class Mail International Packages.', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')"); # Enable/Disable FCM Domestic Insurance. tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added, vendors_id) values ('Enable Stamps.com First Class Domestic Insurance:', 'MODULE_SHIPPING_USPS_FCM_DOM_STAMPS_INSUR_" . $vendors_id . "', 'False', 'Enables the function call to enable Stamps.com insurance on First-Class Mail Domestic Packages.', '6', '0', 'tep_cfg_select_option(array(\'True\', \'False\'), ', now(), '" . $vendors_id . "')"); # Adjustable Flat Mark-Up Rate. tep_db_query("insert into " . TABLE_VENDOR_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, vendors_id) values ('Flat % Markup Rate', 'MODULE_SHIPPING_USPS_FLAT_RATE_" . $vendors_id . "', '0', 'Enter a flat percent markup to be applied to all quoted rates (5 = 5%).', '6', '0', now(), '" . $vendors_id . "')"); ################################################################# # End 2013 Additions by Jared De Blander ################################################################# } function keys($vendors_id) { return array( 'MODULE_SHIPPING_USPS_STATUS_'. $vendors_id, 'MODULE_SHIPPING_USPS_USERID_'. $vendors_id, 'MODULE_SHIPPING_USPS_TAX_CLASS_'. $vendors_id, 'MODULE_SHIPPING_USPS_ZONE_'. $vendors_id, 'MODULE_SHIPPING_USPS_SORT_ORDER_'. $vendors_id, 'MODULE_SHIPPING_USPS_TYPES_'. $vendors_id, 'MODULE_SHIPPING_USPS_DMST_SERVICES_'. $vendors_id, 'MODULE_SHIPPING_USPS_INTL_SERVICES_'. $vendors_id, 'MODULE_SHIPPING_USPS_RATE_TYPE_'. $vendors_id, 'MODULE_SHIPPING_USPS_RATE_SORTER_'. $vendors_id, 'MODULE_SHIPPING_USPS_REGULATIONS_'. $vendors_id, ################################################################# # Begin 2013 Additions by Jared De Blander ################################################################# 'MODULE_SHIPPING_USPS_FCM_INTL_STAMPS_INSUR_'. $vendors_id, 'MODULE_SHIPPING_USPS_FCM_DOM_STAMPS_INSUR_'. $vendors_id, 'MODULE_SHIPPING_USPS_FLAT_RATE_'. $vendors_id, ); } function remove($vendors_id) { tep_db_query("delete from " . TABLE_VENDOR_CONFIGURATION . " where vendors_id = '". $vendors_id ."' and configuration_key in ('" . implode("', '", $this->keys($vendors_id)) . "')"); } function check($vendors_id = '1') { if (!isset($this->_check)) { $check_query = tep_db_query("select configuration_value from " . TABLE_VENDOR_CONFIGURATION . " where vendors_id = '". $vendors_id ."' and configuration_key = 'MODULE_SHIPPING_USPS_STATUS_" . $vendors_id . "'"); $this->_check = tep_db_num_rows($check_query); } return $this->_check; } }
  2. Here are my emails to USPS re: bugs / bad docs ----- 1st Email ----- 1. The largest and most troublesome issue is that the staging server is sending out responses with errant characters in the XML doc. There are what appear to be hexidecimal values on new lines by themselves inserted at random points in the XML (i.e., half way through the name of a tag). We reported this issue the last time there was a major API update and it does not seem to have been resolved. I have written a filter and provided to the osCommerce community for their and our own development but it would be nice to be able to remove the extraneous code from our modules to be able to test on this server. The following is the PHP code to filter the XML returned from the staging server. $body represents a string containing the XML document. Also, the line break characters that are inserted are inconsistent and vary between which operating system standards requiring an array walk to trim blank chars after the regular expression "R" split. I have attached a file containing the response and an image where you can see the text view/hex view of the file rapidly. $body_lines = array_filter( preg_split ('/$\R?^/m', $body), function($check){ return strpos($check,'<') !== false; } ); array_walk( $body_lines, function (&$item, $key){ $item = trim($item); } ); $body = implode('', $body_lines); 2. In the API doc section 9 uses two different sets of HTML for the Registered mark ®. most cases: &lt;sup&gt;&amp;reg;&lt;/sup&gt; vs used in the outliers: <sup>&reg;</sup&gt; ...who appear to be: Global Express Guaranteed Document Global Express Guaranteed Non-Document Rectangular Global Express Guaranteed Non-Document Non-Rectangular 3. It seems as though some rates are not being returned. For instance for an international rate quote I am not seeing: Priority Mail InternationalRM Flat Rate Envelope** Priority Mail InternationalRM Small Flat Rate Box 4. The case sensitivity of the 3-Day / Military / etc differs between section 4.3 & section 8. The XML document appears to use the section 4.3 casing in all observed cases. 5. Priority Mail Express does not show the day info in the <MailService> tag; however, it does show the Military identifier if ship date is not specified. I have not tested with specifying a ship date. 6. Priority Mail returns "x-Day", Military in the <MailService> tag when ship date is not specified. This behaviour is inconsistent with Priority Mail Express. I have not tested with specifying a ship date. ----- 2nd Email ----- I saw USPS pushed an update to the staging server Tuesday night and published a new API doc and have been updating our code to match; however, I noticed the MailService tag for First Class Mail is wrong. It is returned as simply First-Class Mail ...irrespective of service and without registered mark and not... First-Class Mail&lt;sup&gt;&#174;&lt;/sup&gt; Large Envelope First-Class Mail&lt;sup&gt;&#174;&lt;/sup&gt; Letter First-Class Mail&lt;sup&gt;&#174;&lt;/sup&gt; Parcel First-Class Mail&lt;sup&gt;&#174;&lt;/sup&gt; Postcards ...per Appendix A. Also, the XML document being returned by the server is still corrupt and requires a filter be applied. Is this being looked into/addressed?
  3. Hey All, I did work on the MVS version of this and got it working last week prior to the roll out. Their dev server was horrible. I reported a bunch of the errors in their documentation (most of the 1.1 updates were per my bug reports.). I'll try to post what I have later tonight. I emailed them about the First-Class bug they introduced after releasing version 1.1 and they never got back to me. There is another field in the XML reponse you can look at though to determine the type of First-Class I'm not sure how the non-MVS decodes the XML doc but in the version I have maintained it located at $uspsQuote['Package'][$i]['FirstClassMailType'] where $i is the package portion of the document I'm currently inspecting. It is populated like FLAT, PARCEL, etc.
  4. This filter seems to be working for me. It looks like the "R" regular expression wasn't catching all of their mixed newline instances properly but a basic trim was able to get rid of them. Hope it helps someone else out :) $body_lines = array_filter( preg_split ('/$\R?^/m', $body), function($check){ return strpos($check,'<') !== false; } ); array_walk( $body_lines, function (&$item, $key){ $item = trim($item); } ); $body = implode('', $body_lines);
  5. Appears there is some other garbage in here as well. Also I was mistaken in my prior post - there are not <br> entries. The garbage that does show up all appears to be on its own line, however, they don't use consistent line breaks. Some are /n and some are /r/n. I started working on a little filter to their response but it still has some hiccups: $body = implode('', array_filter( preg_split ('/$\R?^/m', $body), function($check){ return strpos($check,'<') !== false; } ) );
  6. Once again USPS has crap in their XML output on their testng server they setup for this. XML output is prefaced by 27<br> and after the closing XML tag has a <br>0 This garbage will have to be removed from the string prior to being passed to your XML parser (ie simplexml_load_string, etc)
  7. It looks like the API documentation can be found here: https://www.usps.com/business/web-tools-apis/2013-july-webtools-release-notes.rtf Test server should be available on July 15th.
  8. Has anyone seen further details on this? Are they providing a testing server for the updated API?
  9. Hello, Has anyone been able to get Google Checkout working with MVS (Multi-Vendor Shipping) reliably? The 3 second timeout required by Google to provide shipping rates seems to make this quite challenging to work reliably when quoting from many vendors as it can take over 3 seconds to get that number of responses from UPS + FedEx + USPS. We setup some code to cache shipping rates from the rate providers so it will generally always work after the page is F5'd but Google defaults to the standard provided rates in almost all cases on the initial page load. Is anyone aware of a method to allow GoogleCheckout to work properly with MVS? Thanks
  10. You call linkpoint / yourpay support and/or your merchant bank. Probably its a card you dont handle... like someone tried an Amex card and you haven't added Amex to your merchant account.
  11. The error logging and debug messages are verbose enough to both list the expiration and the result of using that expiration. You just need to test a case to see where the data is being pulled from because the standard module pulls it from the data entered at the time of purchase and not from a prior stored db location.
  12. The module can already store the data but I suggest you look at PCI compliance data before considering storage anyway.
  13. You need to look at the details of the transaction and why they are failing. Linkpoint will return data and say declined, refused, etc. It is possible that some data may be missing in some cases that you are entering in with linkpoint connect or that there are some minor differences. At the moment all of our YYYM failures are a result of a card authorization problem, lack of funds, stolen card, etc. If we can drill down some specific scenarios with data, we should be able to work through the issue.
  14. You can do a refund through code either on the orders page or elsewhere. We haven't finalized it due to a lack of time but if someone wanted to pay for some time, we can do it. Otherwise you have to go to your payment processing panel at linkpoint/yourpay where you review your transaction, take your transaction number and perform a refund through their virtual terminal.
  15. It's in the readme and/or code notes and in several places on this forum. The module will be labeled "Secure Credit Card Transaction" and not linkpoint API in the admin section.