<?php

/*/
	File:  signup_checkoutlib.phi
	(c) 2004 Windshields To Go USA - All rights reserved.
	Purpose:  A wrapper for the checkout pages.
	Created:  OmniOp - using saveOrder() based on Nathan's original input form & db design.
	Modification History:
		* 07/01/2004 - Nathan - ported over to the AGH affiliate site
		* 07/14/2004 - Nathan - modifications for the new site affiliate account/store site; code cleanup
		* 09/30/2010 - Nathan - switched to rcfax.com from rapidfax; fax_shop_order() and send_fax_as_email()
		* 03/27/2018 - Nathan - working on implementing CyberSource authorization of cards
		* 03/17/2020 - Nathan - consolidated _lib and _functions libraries
		* 04/03/2020 - Nathan - RESURRECTING this since it's used by /signup.php (renamed from 'checkout_functions')
		* mm/dd/yyyy - name - comment
	Prerequisites:  inc/N.phi, inc/settings.phi, & account/affiliate.phi
	Note:  The BofA gateway apparently has a problem with emails unless addresses are all lower case.
/*/


	$ALWAYS_INVOICE = false;  // warning - this dodges any credit card processing error(s)
	//$m_PagerEmail = "8056807305@vtext.com";  // special pager BCC email address for an extra order notification
	$m_DevEmail = "acarrillo.eng@gmail.com";  // used for special notifications and w2g notification bypassing

	require_once '/home/auto11/public_html/vendor/autoload.php';
	

//-------------------------------------------------------------------------
// CyberSource authorization of cards.
//-------------------------------------------------------------------------
function ProcessOrder_CyberSource_Authorization() {

	global $g_Config, $g_oNDB, $g_oAffiliate, $m_DevEmail;

	// grab the data
	$ip = getenv("REMOTE_ADDR"); // user's ip
	$oOrderInfo = getOrderInfo();
	$oBillingInfo = getBillInfo();
	$NextInvID = getTheInvoiceID();
	$description = getProdDescription();
	$charge_total = '.06';
	if( ProcessOrder_ByPassCheck($oBillingInfo) ) {
		return array( 'code' => 0 );
	}

	// cybersource SOAP processing
	$gateway_w2g = array (
		'merchant_id' => "v6554402",
		'transaction_key' => "8hvK3b1JxJFGltld+MLUgnA0zpXV7KFUxa6m9KhmF9fs7ZJUQnuSvm8LXY0kygZlSrc5i/Iay+Gj2w82vOypAWT0v58YTjNNLfcPxh1bbKcmh1XFse2Sx9FKM+tOc8gJjbz9rNwN88nGaClFN6Yim+mfRNwApRbWIoIx+E8T/DCeeZ26sGhCkvQzgEJ/r8IGDbKR184FlQczrkW2kKXWqVSuAnRoiOK/HgNmLYTvCobmz4ZBOswTuStNvT/JguT6U1doHRBONtrmkUC33vXQevNv7wNXAMB1aaOXAZ17qUZJS93h2Ux355EE6k1xAJ7kgerFP/VFLunR7sZi2MPLkw==",
	);
	$client = new CyberSourceSoapClientEx( $gateway_w2g );
	$request = $client->createRequest( "x" . $NextInvID );

	// parameter
	$ccAuthService = new stdClass();
	$ccAuthService->run = 'true';
	$request->ccAuthService = $ccAuthService;

	// billing
	$billTo = new stdClass();
	$billTo->firstName = $oOrderInfo->b_FirstName;
	$billTo->lastName = $oOrderInfo->b_LastName;
	$billTo->street1 = $oOrderInfo->b_Address;
	$billTo->city = $oOrderInfo->b_City;
	$billTo->state = $oOrderInfo->b_State;
	$billTo->postalCode = $oOrderInfo->b_Zip;
	$billTo->country = 'US';
	if( trim($oOrderInfo->b_Email) == "" ) {
		$billTo->email = "order.".$NextInvID."@w2g.us";
	} else {
		$billTo->email = $oOrderInfo->b_Email;
	}
	$billTo->ipAddress = $ip;
	$request->billTo = $billTo;
	
	// card
	$card = new stdClass();
	$card->accountNumber = $oBillingInfo->c_num;
	$card->expirationMonth = $oBillingInfo->c_expmonth;
	$card->expirationYear = $oBillingInfo->c_expyear;
	$request->card = $card;

	// purchase
	$purchaseTotals = new stdClass();
	$purchaseTotals->currency = 'USD';
	$purchaseTotals->grandTotalAmount = $charge_total; // 0.00 [N] trying a raw zero pre-auth
	$request->purchaseTotals = $purchaseTotals;

	// run it
	//debug: mysql_query("INSERT INTO `log` "); // !don't need now
	$reply = $client->runTransaction($request);
	//DumpInfo( $reply );

	// determine what happened
	$TransactionResult = array();
	$TransactionResult['decision'] = $reply->decision;
	if( $reply->decision == "ACCEPT") {        // success
		$TransactionResult['code'] = 0;
	} elseif( $reply->decision == "REJECT") {  // failure
		$TransactionResult['code'] = 1;
	} else {                                   // other?
		$TransactionResult['code'] = 2;
	}
	
	//* [N:2018] change "result=''" from the custom code to the "$reply->reasonCode" ??
	// log this transaction into the DB
	$SQL = "INSERT INTO invoiceTransactions SET stamp=Now(), ";
	$SQL .= "invoiceID='".$NextInvID."', ";
	$SQL .= "ip='".$ip."', ";
	$SQL .= "total='".$charge_total."', ";
	$SQL .= "source='customer', ";
	$SQL .= "archive='basic', ";
	$SQL .= "result='".intval($reply->reasonCode)."', ";
	$SQL .= "reqID='".$reply->requestID."', ";
	$SQL .= "avs='".$reply->ccAuthReply->avsCode."', ";
	$SQL .= "gatewayType='CyberSourceSOAP', ";
	$SQL .= "factor=".$g_oNDB->escape($reply->ccAuthReply->authFactorCode).", ";
	$SQL .= "shortMsg='".substr($reply->ccAuthReply->authRecord, 0, 25)."', ";
	$SQL .= "authCode=".$g_oNDB->escape($reply->ccAuthReply->authorizationCode).", ";
	$SQL .= "reconciliation=".$g_oNDB->escape($reply->ccAuthReply->reconciliationID).", ";
	$SQL .= "token=".$g_oNDB->escape($reply->requestToken)." ";
	if( !$g_oNDB->execute($SQL) ) {
		//! [N:2018] LOG it if there's an error - not this !!!
		echo "<font color=red>[NDB ERR!] ".$g_oNDB->message()."</font>";
		if( $g_Config->isDev ) {
			echo "<!--\n\n".$SQL."\n\n--><p>";
		}
	}
	
	// all done
	return $TransactionResult; // haven't determined this yet

} // ProcessOrder_CyberSource_Authorization()


//-------------------------------------------------------------------------
// ProcessOrder_ByPassCheck: Special checking to bypass test orders. (won't run an auth)
//-------------------------------------------------------------------------
function ProcessOrder_ByPassCheck( $oBilling ) {
	
	if( 
		$oBilling->c_num == '4111111111111111'
		&& $oBilling->c_cvv = '111'
		&& $oBilling->c_iscvv == 'true'
		&& $oBilling->c_type == 'Discover'
	) {
		return true;
	}
	return false;

} // ProcessOrder_ByPassCheck()


//-------------------------------------------------------------------------
// Establishes settings like the next template to display: success/failure.
//-------------------------------------------------------------------------
function PrepareTransactionMessage_CyberSource( $CyberSourceResult ) {

	global $g_Config, $ALWAYS_INVOICE;
	
	$result_msg = array();
	$msg['status'] = $status[0]; 
	if( 
		(isset($ALWAYS_INVOICE) && $ALWAYS_INVOICE===true)     // override (as of 2018, this is ON)
		|| $CyberSourceResult['code'] == 0                     // CyberSource SOAP system 
		|| $CyberSourceResult['IOC_response_code'] == 0        // legacy system
	) {
		$msg['template'] = "orderSuccess.phi";
		$g_Config->PageInfo['name'] = "checkout";
		$g_Config->PageInfo['title'] = "Order Success - Invoice";
	} else {
		$msg['template'] = "orderFailed.phi";
		$msg['reason'] = $result['IOC_reject_description'];
		$msg['code'] = $result['IOC_response_code'];
		$g_Config->PageInfo['name'] = "checkout";
		$g_Config->PageInfo['title'] = "Checkout - Order Failed";
	}
	return $msg;

} // PrepareTransactionMessage_CyberSource()


///-------------------------------------------------------------------------
function OLD_processOrder() {

	global $g_oAffiliate, $m_DevEmail;

	$oOrderInfo = getOrderInfo();
	$oBillingInfo = getBillInfo();
	
	/// Business rule = if the deductible is less then the total glass cost the authorize the deductible amount only, otherwise.
	if( empty($oOrderInfo->i_Deductible) ) {
		$charge_total = $_SESSION['cart']->getTotal();
	} else {
		if (cleanString($oOrderInfo->i_Deductible) < $_SESSION['cart']->getTotal()) {
			$charge_total = cleanString($oOrderInfo->i_Deductible);
		} else {
			$charge_total = $_SESSION['cart']->getTotal();	
		}
	}	

	/// set up the bamart parameters...
	$invoiceID = getTheInvoiceID();
	$description = getProdDescription();
	$chargeInfo = "";
	$message = "";
	$merchant = "windshield_net";
	if( $g_oAffiliate->ID==2 || $oOrderInfo->s_Email==$m_DevEmail ) {
		$paymentURL = "https://cart.bamart.com/debug/payment.mart";
	} else {
		$paymentURL = "https://cart.bamart.com/payment.mart";
	}
	$ip = getenv("REMOTE_ADDR"); // get the ip number of the user
	$referer = getenv("HTTP_REFERER");
	$host = gethostbyaddr($ip);

	/// begin amassing the order data string...
	//$chargeInfo .= "ioc_Handshake_id=" . prepPostValue(get_random_key(32)) . "&";
	$chargeInfo .= "ioc_cvv_indicator=" . ($oBillingInfo->c_iscvv == "true" ? "0" : "1") . "&";
	$chargeInfo .= "ioc_order_description=" . prepPostValue($description) . "&";
	$chargeInfo .= "ioc_merchant_shopper_id=" . prepPostValue(session_id()) . "&";
	$chargeInfo .= "ioc_merchant_order_id=" . prepPostValue($invoiceID) . "&";
	$chargeInfo .= "ioc_merchant_id=" . prepPostValue($merchant) . "&";
	$chargeInfo .= "ioc_order_total_amount=" . prepPostValue($charge_total) . "&";
	$chargeInfo .= "ioc_order_ship_amount=" . prepPostValue($_SESSION['cart']->getShipping()) . "&";
	$chargeInfo .= "ecom_billto_postal_name_first=" . prepPostValue($oOrderInfo->b_FirstName) . "&";
	$chargeInfo .= "ecom_billto_postal_name_last=" . prepPostValue($oOrderInfo->b_LastName) . "&";	
	$chargeInfo .= "ecom_billto_postal_street_line1=" . prepPostValue($oOrderInfo->b_Address) . "&";
	$chargeInfo .= "ecom_billto_postal_city=" . prepPostValue($oOrderInfo->b_City) . "&";
	$chargeInfo .= "ecom_billto_postal_stateprov=" . prepPostValue($oOrderInfo->b_State) . "&";
	$chargeInfo .= "ecom_billto_postal_postalcode=" . prepPostValue($oOrderInfo->b_Zip) . "&";
	$chargeInfo .= "ecom_billto_telecom_phone_number=" . prepPostValue($oOrderInfo->b_Phone) . "&";
	$chargeInfo .= "ecom_billto_postal_countrycode=" . prepPostValue("US") . "&";
	$chargeInfo .= "ecom_billto_online_email=" . prepPostValue($oOrderInfo->b_Email) . "&";

	// If the shipping info is the same as the billing, then we need to add the same as flag other wise we post all og the shipping info
	if ($oOrderInfo->b_SameAsShipping == 1) {
		$chargeInfo .= "ioc_shipto_same_as_billto=" . ($oOrderInfo->b_SameAsShipping == 1 ? "1" : "0") . "&";	
	} else {
		$chargeInfo .= "ecom_shipto_postal_name_first=" . prepPostValue($oOrderInfo->s_FirstName) . "&";
		$chargeInfo .= "ecom_shipto_postal_name_last=" . prepPostValue($oOrderInfo->s_LirstName) . "&";	
		$chargeInfo .= "ecom_shipto_postal_street_line1=" . prepPostValue($oOrderInfo->s_Address) . "&";
		$chargeInfo .= "ecom_shipto_postal_city=" . prepPostValue($oOrderInfo->s_City) . "&";
		$chargeInfo .= "ecom_shipto_postal_stateprov=" . prepPostValue($oOrderInfo->s_State) . "&";
		$chargeInfo .= "ecom_shipto_postal_postalcode=" . prepPostValue($oOrderInfo->s_Zip) . "&";
		$chargeInfo .= "ecom_shipto_telecom_phone_number=" . prepPostValue($oOrderInfo->s_Phone) . "&";
		$chargeInfo .= "ecom_shipto_postal_countrycode=" . prepPostValue("US") . "&";
		$chargeInfo .= "ecom_shipto_online_email=" . prepPostValue($oOrderInfo->s_Email) . "&";
	}
	$chargeInfo .= "ecom_payment_card_name=" . prepPostValue($oBillingInfo->c_type) . "&";
	$chargeInfo .= "ecom_payment_card_number=" . prepPostValue($oBillingInfo->c_num) . "&";
	$chargeInfo .= "ecom_payment_card_expdate_month=" . prepPostValue($oBillingInfo->c_expmonth) . "&";
	$chargeInfo .= "ecom_payment_card_expdate_year=" . prepPostValue($oBillingInfo->c_expyear) . "&";
	if( empty($oBillingInfo->c_iscvv) || $oBillingInfo->c_iscvv != "true" ) {
		$chargeInfo .= "ecom_payment_card_verification=" . prepPostValue($oBillingInfo->c_cvv);
	}
	//$chargeInfo .= "ioc_auto_settle_flag=Y&";
	
	/// execute the query...
	$chargeInfo = str_replace('"', '\"', $chargeInfo);
	$cmd = "/usr/bin/curl --referer windshieldstogo.com -d \"".$chargeInfo."\" ".$paymentURL;
	if( !GetSetting("w2g_processCards", 0, false) ) {  // special setting that turns off live card processing
		//return array( "IOC_response_code" => 0 );
		$return_string = array("<br><b><font color=#A80000>W2G_CCProcess_Bypassed=TRUE</font></b><br>IOC_response_code=0");
	} else {
		exec( $cmd, $return_string );
	}
	//mysql_query("INSERT INTO `log` SET stamp=Now(), event='".addslashes($cmd."\n\n<br><br><br><br>\n\n".str_var_dump($return_string))."'");

	/// extract and return the response values into an array...
	$return_array = explode("<BR>", $return_string[0]);
	$rResponse = array();
	foreach( $return_array as $key => $value ) {
		$temp_pair = explode("=", $value);
		$rResponse[$temp_pair[0]] = $temp_pair[1];
	}
	$rResponse[] = $return_string;
	return $rResponse; // return an array with reponse information

} // OLD_processOrder()


//-------------------------------------------------------------------------
// prepValue: Prep a value for 
//-------------------------------------------------------------------------
function prepPostValue( $value, $crop=0 ) {

	$value = stripslashes($value);  // why did we used to urldecode() here too?
	if( intval($crop) > 0 ) $value = substr( $value, 0, $crop );
	return urlencode($value);

} // prepValue()


///-------------------------------------------------------------------------
function OLD_prepareTrnxMsg( $response ) {

	global $g_Config, $ALWAYS_INVOICE;
	
	//IOC_response_code
	//IOC_reject_description
	$msg = array();
	$msg['status'] = $status[0]; 
	if( (isset($ALWAYS_INVOICE) && $ALWAYS_INVOICE===true) || $response['IOC_response_code'] == 0 ) {
		$msg['template'] = "orderSuccess.phi";
		$g_Config->PageInfo['name'] = "checkout";
		$g_Config->PageInfo['title'] = "Order Success - Invoice";
	} else {
		$msg['template'] = "orderFailed.phi";
		$msg['reason'] = $response['IOC_reject_description'];
		$msg['code'] = $response['IOC_response_code'];
		$g_Config->PageInfo['name'] = "checkout";
		$g_Config->PageInfo['title'] = "Checkout - Order Failed";
	}
	return $msg;

} // prepareTrnxMsg()


//-------------------------------------------------------------------------
// saveOrder:  Saves the checkout shipping/billing info into the database. Returns the invoice ID.
//-------------------------------------------------------------------------
function saveOrder( $oOrderInfo, $oBillingInfo ) {

	global $g_Config, $g_oNDB, $g_oAffiliate, $m_DevEmail, $CustomerKey;
	//if( $oOrderInfo->isW2G ) echo "W2G !<br>"; if( $oOrderInfo->isReferral) echo "REFERRAL !<br>";

	$var = new object;
	$var->isW2G = $oOrderInfo->isW2G;
	$var->isReferral = $oOrderInfo->isReferral;
	$var->site_email = $g_oAffiliate->settings['site_email'];
	$var->site_phone = $g_oAffiliate->settings['site_phone'];
	$var->affiliateID = $g_oAffiliate->ID;
	$var->affiliateBusiness = $g_oAffiliate->business;
	$var->affiliateWebsite = $g_oAffiliate->website;
	$var->orderType = $_SESSION['cart']->orderType;
	$var->statusIntro = $g_Config->settings['w2g_statusIntro'];
	$var->guarantee = $g_Config->settings['w2g_guarantee'];
	
	/// generate a customerKey for the order status area...
	$var->customerKey = GenerateCustomerKey();
	if( $g_Config->isDev ) $var->customerKey = "9999";
	$CustomerKey = $var->customerKey;

	/// set up the data and create a cart to reference the id in all of the other tables...
	$prodArray = ($_SESSION['cart']->getCartItems());
	for( $c=0; $c<count($prodArray); $c++ ) {
		$pid = $prodArray[$c]['id'];
		if( !empty($_SESSION['cart']->items[$pid]['CODE']) ) {
			$zip = $_SESSION['cart']->items[$pid]['CODE'];
		}
	}
	$randomKey = get_random_key(25);  //!!! legacy feature from CGI throw this out sometime
	$subtotal = cleanString($_SESSION['cart']->subTotal);
	$cart_query = "INSERT INTO cart (stamp, randomKey, subtotal, ZIP, IP, session) VALUES (Now(), '$randomKey', '$subtotal', '$zip', '".mysql_escape_string($_SERVER['REMOTE_ADDR'])."', '".mysql_escape_string(session_id())."')";
	$res_cart = db_query($cart_query, false, false, false);
	
	// make sure cart is created, becasue if it is not, then we can not continue.
	if( !$res_cart ) {
		SendMail( $var->site_email, $m_DevEmail, "Duplicate cart ID - AGH affiliate." );  // notify m¥self
		emptyCart();
		return 0;
	}
	$cartID = mysql_insert_id();	
	
	//Insert into Checkout table order Information
	$cc_last4 = strlen(cleanString($oBillingInfo->c_num)) >= 4 ? substr(cleanString($oBillingInfo->c_num), strlen(cleanString($oBillingInfo->c_num))-4) : "????";
	$query_checkout = "INSERT INTO checkout (
			cartID, s_FirstName, s_LastName, s_Email, s_Company, s_Address, s_City, s_State, s_Zip, s_Phone, s_Fax, s_VIN, s_Unit, s_InstallDate, s_InstallYear, s_CrossStreet, b_SameAsShipping,
			b_FirstName, b_LastName, b_Email, b_Company, b_Address, b_City, b_State, b_Zip, b_Phone, i_Name, i_Phone, i_Policy, i_Deductible, comments, referrer, paymentMethod,
			ShipWindshields, s_Warehouse, c_num, c_type, c_expmonth, c_expyear, c_cvv, subtotal, shipping, total, discountCode, agreeLowPrice, agreeFactors, b_Country, s_Country, s_Method
		) VALUES (
			'$cartID'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_FirstName)) . "'                                                             
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_LastName)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_Email)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_Company)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_Address)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_City)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_State)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_Zip)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_Phone)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_Fax)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_VIN)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_Unit)) . "'
			, '" . gets_InstallDate( cleanString($oOrderInfo->s_InstallDate), cleanString($oOrderInfo->s_InstallMonth), cleanString($oOrderInfo->s_InstallTime) ) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_InstallYear)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_CrossStreet)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_SameAsShipping)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_FirstName)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_LastName)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_Email)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_Company)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_Address)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_City)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_State)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_Zip)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->b_Phone)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->i_Name)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->i_Phone)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->i_Policy)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->i_Deductible)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->comments)) . "'
			, '" . (isset($_SESSION['referral']) ? $_SESSION['referral'] : cleanString($oOrderInfo->referrer)) /* try session; otherwise use their manual selection */ . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->paymentMethod)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->ShipWindshields)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->s_Warehouse)) . "'
			, 'XXXX-XXXX-XXXX-" . $cc_last4 . "'
			, '" . mysql_escape_string(cleanString($oBillingInfo->c_type)) . "'
			, 'XX"./*removed c_expmonth*/"'
			, '" . mysql_escape_string(cleanString($oBillingInfo->c_expyear)) . "'
			, '"./*removed c_cvv*/"'
			, '" . mysql_escape_string(cleanString($_SESSION['cart']->subTotal)) . "'
			, '" . mysql_escape_string(cleanString($oOrderInfo->shipping)) . "'
			, '" . mysql_escape_string(cleanString($_SESSION['cart']->total)) . "'
			, '" . mysql_escape_string(cleanString($oBillingInfo->discountCode)) . "'
			, '" . mysql_escape_string(cleanString($oBillingInfo->agreeLowPrice)) . "'
			, '" . mysql_escape_string(cleanString($oBillingInfo->agreeFactors)) . "'
			, 'USA'
			, 'USA'
			, '" . cleanString($oOrderInfo->ShipWindshields) . "'
	)";

	// Perform the query for the auto_main database
	$installDate = getfInstallDate (cleanString($oOrderInfo->s_InstallDate), cleanString(month2Month ($oOrderInfo->s_InstallMonth)), cleanString($oOrderInfo->s_InstallYear), cleanString($oOrderInfo->s_InstallTime));
	$res_checkout = db_query($query_checkout, false, false, false);
	if( !$res_checkout ) {  // notify m¥self
		SendMail( $var->site_email, $m_DevEmail, "Couldn't insert into checkout." );  // notify m¥self
		emptyCart();
		return 0;
	 }
	
	
	/* insert the invoice in the main database */

	/// set up the referencing infos...
	$checkoutID = mysql_insert_id();  // we need this
	if( $oOrderInfo->isW2G || $oOrderInfo->isReferral ) {
		$affID = 0;
		$refID = $g_oAffiliate->ID;
		$ocode = "W2G";
	} else {
		$affID = $g_oAffiliate->ID;
		$refID = 0;
		$ocode = strtoupper($g_oAffiliate->code);
	}
        $invoiceID = getTheInvoiceID();

	$onum = getNextOrderNum( $ocode, $invoiceID, intval($g_oNDB->getField("SELECT orderNumMin FROM affiliate WHERE ID=".$g_oAffiliate->ID)), ($oOrderInfo->isW2G || $oOrderInfo->isReferral) );
	
	/// insert the invoice record (MAIN db)...
	$SQL = "INSERT INTO invoice (cartID, checkoutID, affiliateID, referrerID, orderCode, orderNum, customerKey, fulfilled, purchaseDate, installDate) VALUES (";
	$SQL .= "'$cartID', '$checkoutID', '$affID', '$refID', '".cleanString($ocode)."', '$onum', ".$var->customerKey.", 'N', Now(), '".$installDate."')";
	if( !db_query( $SQL, false, false, false ) ) {
		SendMail( $var->site_email, $m_DevEmail, "Couldn't insert main invoice record." );  // notify m¥self
		emptyCart();
		return 0;
	 }

	//Return the auto increment id from the invoice table
	$invoiceID = mysql_insert_id();
	
	/// insert the initial order received event...
	//$msg = $g_oNDB->getField("SELECT message FROM messagePrefill WHERE subject ='Order Received'");
	//if( $msg=="" || ereg("^ERROR", $msg) ) $msg = "Your order has been received. Please allow two (2) regular business hours for processing.";
	$msg = $g_oNDB->getField(0,"SELECT message FROM messagePrefill WHERE subject ='Order Received'","Your order has been received. Please allow two (2) regular business hours for processing.");
	$g_oNDB->execute("INSERT INTO invoiceEvents SET invoiceID='".$invoiceID."', stamp=Now(), postedBy='system', subject='Order Received', event=".$g_oNDB->escape($msg).", isVisible='Y'");

	// make sure the invoice id that was used to Process the order matched the invoice_id that was just entered
	// If they don't match then we have a problem so we will die ans show an error message..
	if( $_SESSION['invoice_id'] != $invoiceID || empty($invoiceID) || empty($checkoutID) ) {
		$DEBUG_MSG = "SESSION[invoice_id]: " . $_SESSION['invoice_id']."\n"."invoiceID: " . $invoiceID."\n";
		if( !isset($_SESSION['invoice_id']) || intval($_SESSION['invoice_id']) ) {
			$DEBUG_MSG .= "\n*** NOTE: _SESSION['invoice_id'] NOT SET.. did you call \"getTheInvoiceID()\" ?";
		}
		/** // probably (almost certainly !) shouldn't ever actually be deleting ANYTHING !!
		mysql_query( "DELETE FROM invoice WHERE ID=" . $invoiceID );
		mysql_query( "DELETE FROM checkout WHERE ID=" . $checkoutID );
		mysql_query( "ALTER TABLE invoice AUTO_INCREMENT=0" );
		mysql_query( "ALTER TABLE checkout AUTO_INCREMENT=0" );
		/**/
		SendMail( $var->site_email, $m_DevEmail, "Screwed up the MAIN db.", $DEBUG_MSG );  // notify m¥self
		if( $g_oNDB->error() > 0 ) {
			$DEBUG_MSG .= "\n" . $g_oNDB->message()."\n";
		}
		// automatically retry one more saveOrder() ??
		exit( "There has been a database error (-101) while processing your order.<br><b>\nIf you wish, try again, but please do contact us if we can help.</b><br>\nWe apologize for this inconvenience and thank you for shopping with us.<!--\n\n".$DEBUG_MSG."\n\n-->" );
	}

	//Insert the carts contents in to the cart Items table.
	$prodArray = ($_SESSION['cart']->getCartItems());
	$var->product_string = "";
	for( $c=0; $c<count($prodArray); $c++ ) {
		$pid = $prodArray[$c]['id'];
		$pmake = $prodArray[$c]['make'];
		$pmodel = $prodArray[$c]['model'];
		$pstyle = $prodArray[$c]['style'];
		$pprice = $_SESSION['cart']->items[$pid]['PRICE'];
		$pyear = $prodArray[$c]['year'];
		$pqty = $_SESSION['cart']->items[$pid]['QTY'];
		$popt = ($_SESSION['cart']->items[$pid]['OPT']=="install" ? "inst" : ($_SESSION['cart']->items[$pid]['OPT']=="hardware" ? $_SESSION['cart']->items[$pid]['HARDWARE'] : ""));
		$pcode = $_SESSION['cart']->items[$pid]['CODE'];

		//Lets make sure the product qty is always one
		$query_cart = "INSERT INTO cartItems(cartID, productID, itemPrice, quantity, parameters, partDetails) VALUES('$cartID', '$pid', '$pprice', '$pqty', '$popt', '$pcode')"; 
		$res_cart = db_query($query_cart, false, false, false);
		
		// Create an array so the auto_invoice database does not have to pull part information
		$a_pid[$c] = $prodArray[$c]['id'];
		$a_pmake[$c] = $prodArray[$c]['make'];
		$a_pmodel[$c] = $prodArray[$c]['model'];
		$a_pstyle[$c] = $prodArray[$c]['style'];
		$a_pyear[$c] = $prodArray[$c]['year'];
		$a_pqty[$c] = $_SESSION['cart']->items[$pid]['QTY'];
		$a_pprice[$c] = $_SESSION['cart']->items[$pid]['PRICE'];
		$a_popt[$c] = ($_SESSION['cart']->items[$pid]['OPT'] == "install" ? "inst" : "");
		$a_pcode[$c] = $_SESSION['cart']->items[$pid]['CODE'];
		$var->product_string .= "Year: " . $pyear ."\n";
		$var->product_string .= "Make: " . $pmake ."\n";
		$var->product_string .= "Model: " . $pmodel ."\n";
		$var->product_string .= "Style: " . $pstyle ."\n";
		$var->product_string .= "Style: " . $prodArray[$c]['id'] ."\n";
		$var->product_string .= "Installed: " . ($popt=="inst" ? "Yes" : "No") ."\n";
		if( $popt!="" && $popt!="inst" ) $var->product_string .= "Hardware: " . $_SESSION['cart']->items[$pid]['HARDWARE'] ."\n";
		$var->product_string .= "Qty: " . $pqty ."\n";
		$var->product_string .= "Item Price: " . $pprice ."\n";
		$var->product_string .= "Items Subtotal Price: " . sprintf( "%.2f", $pprice*$pqty) ."\n";
		if( $c < count($prodArray)-1 ) {
			$var->product_string .= "\n";
		}											
	}
	
	/* Now perform the same queries to the auto_invoice database */
	db_disconnect();  // temporary while we update the backup "invoice" db
	//echo "<p>".$g_Config->dbInfoI['server'].":".$g_Config->dbInfoI['database'].":".$g_Config->dbInfoI['user'].":".$g_Config->dbInfoI['password']."<hr>";
	db_connect( $g_Config->dbInfoI['server'], $g_Config->dbInfoI['database'], $g_Config->dbInfoI['user'], $g_Config->dbInfoI['password'] );
	$res_cart_i = db_query($cart_query, false, false, false);
	unset($cartID);
	$cartID = mysql_insert_id();

	/// set up the checkout record insertion command...
	$query_checkout_i = "INSERT INTO checkout(
		cartID, s_FirstName, s_LastName, s_Email, s_Company, s_Address, s_City, s_State, s_Zip, s_Phone, s_Fax, s_VIN, s_Unit, s_InstallDate, s_InstallYear, s_CrossStreet, b_SameAsShipping,
		b_FirstName, b_LastName, b_Email, b_Company, b_Address, b_City, b_State, b_Zip, b_Phone, i_Name, i_Phone, i_Policy, i_Deductible, comments, referrer, paymentMethod,
		ShipWindshields, s_Warehouse, c_num, c_type, c_expmonth, c_expyear, c_cvv, subtotal, shipping, total, discountCode, agreeLowPrice, agreeFactors, b_Country, s_Country, s_Method
		) VALUES (
		  '$cartID',
		'" . cleanString($oOrderInfo->s_FirstName) . "',
		'" . cleanString($oOrderInfo->s_LastName) . "',
		'" . cleanString($oOrderInfo->s_Email) . "',
		'" . cleanString($oOrderInfo->s_Company) . "',
		'" . cleanString($oOrderInfo->s_Address) . "',
		'" . cleanString($oOrderInfo->s_City) . "',
		'" . cleanString($oOrderInfo->s_State) . "',
		'" . cleanString($oOrderInfo->s_Zip) . "',
		'" . cleanString($oOrderInfo->s_Phone) . "',
		'" . cleanString($oOrderInfo->s_Fax) . "',
		'" . cleanString($oOrderInfo->s_VIN) . "',
		'" . cleanString($oOrderInfo->s_Unit) . "',
		'" . gets_InstallDate( cleanString($oOrderInfo->s_InstallDate), cleanString($oOrderInfo->s_InstallMonth), cleanString($oOrderInfo->s_InstallTime) ) . "',
		'" . cleanString($oOrderInfo->s_InstallYear) . "',
		'" . cleanString($oOrderInfo->s_CrossStreet) . "',
		'" . cleanString($oOrderInfo->b_SameAsShipping) . "',
		'" . cleanString($oOrderInfo->b_FirstName) . "',
		'" . cleanString($oOrderInfo->b_LastName) . "',
		'" . cleanString($oOrderInfo->b_Email) . "',
		'" . cleanString($oOrderInfo->b_Company) . "',
		'" . cleanString($oOrderInfo->b_Address) . "',
		'" . cleanString($oOrderInfo->b_City) . "',
		'" . cleanString($oOrderInfo->b_State) . "',
		'" . cleanString($oOrderInfo->b_Zip) . "',
		'" . cleanString($oOrderInfo->b_Phone) . "',
		'" . cleanString($oOrderInfo->i_Name) . "',
		'" . cleanString($oOrderInfo->i_Phone) . "',
		'" . cleanString($oOrderInfo->i_Policy) . "',
		'" . cleanString($oOrderInfo->i_Deductible) . "',
		'" . cleanString($oOrderInfo->comments) . "',
		'" . (isset($_SESSION['referral']) ? cleanString($_SESSION['referral']) : cleanString($oOrderInfo->referrer)) . "',
		'" . cleanString($oOrderInfo->paymentMethod) . "',
		'" . cleanString($oOrderInfo->ShipWindshields) . "',
		'" . cleanString($oOrderInfo->s_Warehouse) . "',
		'" . cleanString($oBillingInfo->c_num) . "',
		'" . cleanString($oBillingInfo->c_type) . "',
		'" . cleanString($oBillingInfo->c_expmonth) . "',
		'" . cleanString($oBillingInfo->c_expyear) . "',
		'" . cleanString($oBillingInfo->c_cvv) . "',
		'" . cleanString($_SESSION['cart']->subTotal) . "',
		'" . cleanString($oOrderInfo->shipping) . "',
		'" . cleanString($_SESSION['cart']->total) . "',
		'" . cleanString($oBillingInfo->discountCode) . "',
		'" . cleanString($oBillingInfo->agreeLowPrice) . "',
		'" . cleanString($oBillingInfo->agreeFactors) . "',
		'USA',
		'USA',
		'" . cleanString($oOrderInfo->ShipWindshields) . "'
		)
	";
	
	/// insert the row and get the auto_increment'ed ID...
	$res_checkout_i = db_query($query_checkout_i, false, false, false);
	unset($checkoutID);
	$checkoutID = mysql_insert_id();	

	/// insert the invoice record (INVOICE db) -- making sure that the auto_main autoincrements are replaced...
	$SQL = "INSERT INTO invoice (refID, cartID, checkoutID, affiliateID, referrerID, orderCode, orderNum, fulfilled, purchaseDate, installDate) VALUES (";
	$SQL .= "'$invoiceID', '$cartID', '$checkoutID', '$affID', '$refID', '".cleanString($ocode)."', '$onum', 'N', Now(), '".$installDate."')";
	db_query( $SQL, false, false, false );

	//Insert the carts contents in to the cart Items table.
	for( $c=0; $c<count($a_pid); $c++ ) {
		$pid = $a_pid[$c];
		$pmake = $a_pmake[$c];
		$pmodel = $a_pmodel[$c];
		$pstyle = $a_pstyle[$c];
		$pprice = $a_pprice[$c];				
		$pyear = $a_pyear[$c];
		$pqty = $a_pqty[$c];
		$popt = $a_popt[$c];
		$pcode = $a_pcode[$c];
		//Lets make sure the product qty is always one
		$query_cart = "INSERT INTO cartItems (cartID, productID, itemPrice, quantity, parameters, partDetails) VALUES ($cartID, '$pid', '$pprice', '$pqty', '$popt', '$pcode')";
		$res_cart_i = db_query($query_cart, false, false, false);
	}
	db_disconnect(); // all done with the backup "invoice" db
	
	
	/* Done saving data -- but re-connect to the MAIN db before proceeding with sending out emails... */
	db_connect( $g_Config->dbInfo['server'], $g_Config->dbInfo['database'], $g_Config->dbInfo['user'], $g_Config->dbInfo['password'] );
	
	/* Amass info that will be used for the emails to w2g (or the shop) and to the customer. */
	
	/// order info...
	$var->invoice_num = $invoiceID;
	$var->inv_num = sprintf("%06d", $invoiceID);
	$var->order_num = ($ocode=="W2G" ? "" : $ocode."-").sprintf("%04d", $onum);
	$var->orderNumber = sprintf("%04d", $onum);

	/// product info... (should be just one, but later we might support more than one item in a cart)
	foreach( $_SESSION["cart"]->items as $productID => $rInfo ) {
		$rData = $g_oNDB->getRow( "SELECT nspart, moldingPart FROM product WHERE ID='".$productID."'" );
		$var->nspart .= cleanString( $rData['nspart'] ) . " ";
		$var->moldingPart .= cleanString( $rData['moldingPart'] ) . " ";
	}

	/// customer info...
	$var->first_name_s = cleanString($oOrderInfo->s_FirstName) ;
	$var->last_name_s = cleanString($oOrderInfo->s_LastName);
	$var->email_s = cleanString($oOrderInfo->s_Email);
	$var->company_s = cleanString($oOrderInfo->s_Company);
	$var->address_s = cleanString($oOrderInfo->s_Address);
	$var->city_s = cleanString($oOrderInfo->s_City);
	$var->state_s = cleanString($oOrderInfo->s_State);
	$var->zip_s = cleanString($oOrderInfo->s_Zip);
	$var->phone_s = cleanString($oOrderInfo->s_Phone);
	$var->fax_s = cleanString($oOrderInfo->s_Fax);
	$var->vin_s = cleanString($oOrderInfo->s_VIN);
	$var->unit_s = cleanString($oOrderInfo->s_Unit);
	$var->first_name_b = cleanString($oOrderInfo->b_FirstName);
	$var->last_name_b = cleanString($oOrderInfo->b_LastName);
	$var->company_b = cleanString($oOrderInfo->b_Company);
	$var->address_b = cleanString($oOrderInfo->b_Address);
	$var->city_b = cleanString($oOrderInfo->b_City);
	$var->state_b = cleanString($oOrderInfo->b_State);
	$var->zip_b = cleanString($oOrderInfo->b_Zip);
	$var->phone_b = cleanString($oOrderInfo->b_Phone);

	/// credit card info...
	$var->cc_type = cleanString($oBillingInfo->c_type);
	//$var->cc_num = cleanString($oBillingInfo->c_num); // never want this in a template!
	$var->cc_last4 = strlen(cleanString($oBillingInfo->c_num)) >= 4 ? substr(cleanString($oBillingInfo->c_num), strlen(cleanString($oBillingInfo->c_num))-4) : "????";
	$var->cc_last2 = strlen(cleanString($oBillingInfo->c_num)) >= 2 ? substr(cleanString($oBillingInfo->c_num), strlen(cleanString($oBillingInfo->c_num))-2) : "??";
	//$var->cc_month = cleanString($oBillingInfo->c_expmonth); // never want this in a template
	$var->cc_year = cleanString($oBillingInfo->c_expyear);
	if( $oBillingInfo->c_type == 'PayPal' ) {
		$var->CheckoutMethodMessage = "\nPayPal Order:\nOnce we confirm your order details, we will send \nyou a link so that you can finish the checkout process.\nThank you.\n\n";
		$var->CheckoutMethodMessage .= "<br><br>";
	} elseif( $oBillingInfo->c_type == 'GoogleCheckout' ) {
		$var->CheckoutMethodMessage = "\nGoogle Checkout:\nOnce we confirm your order details, we will send \nyou a link so that you can finish the checkout process.\nThank you.\n\n";
		$var->CheckoutMethodMessage .= "<br><br>";
	} else {
		$var->CheckoutMethodMessage = "\n";
	}
	
	/// checkout info...
	$var->subtotal = cleanString($_SESSION['cart']->subTotal);
	$shipping = $_SESSION['cart']->getShipping();
	$var->shipping = floatval($shipping) >= 0.01 ? cleanString($_SESSION['cart']->getShipping()) : "N/A";
	$var->total = cleanString($_SESSION['cart']->total);
	$var->tax = cleanString($_SESSION['cart']->tax);	
	$var->warehouse = cleanString($oOrderInfo->s_Warehouse);
	$var->service_type = cleanString($oOrderInfo->ShipWindshields);
	$var->payment_method = cleanString($oOrderInfo->paymentMethod);
	$var->insurance_name = cleanString($oOrderInfo->i_Name);
	$var->insurance_phone = cleanString($oOrderInfo->i_Phone);
	$var->insurance_policy = cleanString($oOrderInfo->i_Policy);
	$var->insurance_deduct = cleanString($oOrderInfo->i_Deductible);
	$var->referral = (isset($_SESSION['referral']) ? $_SESSION['referral'] : cleanString($oOrderInfo->referrer));
	$var->agree_low = cleanString($oBillingInfo->agreeLowPrice);
	$var->agree_fact = cleanString($oBillingInfo->agreeFactors);
	$var->discount_code = cleanString($oBillingInfo->discountCode);
	
	// [nl:2013-09-16] remove later; not hurting anything for now - stay compatible just in case lingering code references it
	$var->cross_street = cleanString($oOrderInfo->s_CrossStreet);
	
	$var->install_date = gets_InstallDate( cleanString($oOrderInfo->s_InstallDate), cleanString($oOrderInfo->s_InstallMonth), cleanString($oOrderInfo->s_InstallTime)) . " " . cleanString($oOrderInfo->s_InstallYear );
	$var->comments = cleanString($oOrderInfo->comments);
	
	/// send out emails (unless it is a dev test)...
	ProcessNotifications( $var );
	
	// Return the original invoice id.
	$_SESSION['invoiceID'] = $invoiceID; 	/* this is not happening for upreferred orders ?? */
	$_SESSION['order_num'] = $var->order_num;
	return array($invoiceID, $var->order_num);

} // saveOrder()


///-------------------------------------------------------------------------
function email_shop_order( $var ) {

	global $g_Config, $g_oAffiliate, $m_PagerEmail;

	/// initz...
	$StoreEmail = GetSetting("w2g_orderEmail"/*name*/, 0/*refID*/, "orders@windshieldstogo.com"/*default*/, 'master'/*type*/);  // store email for for w2g orders

	// Create the email body using template see the lib mail class to view other mailing features	
	$template = $g_Config->BasePath."account/checkout/templates/email_order_".($var->isReferral || $var->isW2G ? "w2g" : "affiliate")."_shop.phi";
	$message = read_template($template, $var);
	$oMail = new Mail;
	$oMail->From( $var->email_s );
	if( $var->isW2G || $var->isReferral ) {
		$oMail->To( $StoreEmail );
		if( isset($m_PagerEmail) && $m_PagerEmail != "" ) $oMail->Bcc( $m_PagerEmail );
	} else {
		$oMail->To( $var->site_email );
	}
	if( $var->isReferral || $var->isW2G ) {
		$oMail->Subject( "Windshields To Go USA - Windshield Order # ".$var->order_num);
	} else {
		$oMail->Subject( $g_oAffiliate->business." - Windshield Order # ".$var->order_num );
	}
	$oMail->isMIME(false);
	$oMail->Body($message);
	$oMail->Send();

} // email_shop_order()


///-------------------------------------------------------------------------
function email_customer_order( $var ) {

	global $g_Config, $g_oAffiliate;

	/// initz...
	$StoreEmail = GetSetting("w2g_orderEmail"/*name*/, 0/*refID*/, "orders@windshieldstogo.com"/*default*/, 'master'/*type*/);  // store email for for w2g orders

	/// create the email body using template see the lib mail class to view other mailing features
	$template = $g_Config->BasePath."account/checkout/templates/email_order_".($var->isReferral || $var->isW2G ? "w2g" : "affiliate")."_customer.phi";
	if( !$var->isW2G && !$g_oAffiliate->settings['use_status'] ) {
		$template = $g_Config->BasePath."account/checkout/templates/email_order_affiliate_customer_no-status.phi";
	}
	$message = read_template($template, $var);
	$oMail = new Mail;
	$oMail->To( $var->email_s );
	if( $var->isReferral || $var->isW2G ) {
		$oMail->From( $StoreEmail );
		$oMail->Subject( "Windshields To Go USA - Order # ".$var->order_num );
	} else {
		$oMail->From( $var->site_email );
		$oMail->Subject( $g_oAffiliate->business." - Windshield Order # ".$var->order_num );
	}
	$oMail->isMIME(true);
	$oMail->Body($message, "");
	$oMail->Send();

} // email_customer_order()


///-------------------------------------------------------------------------
function fax_shop_order( &$var ) {

	global $g_Config, $g_oAffiliate;

	/// get the order number from the vartemplate...
	$subject = "Notification of Order # " . $var->order_num;

	/// set up the plain text message...
	if( $g_oAffiliate->checkoutMode == CHECKOUT_MODE_ABRIDGED ) {
		$template = $g_Config->BasePath."account/checkout/templates/fax_order_affiliate_abridged.phi";
	} else {
		$template = $g_Config->BasePath."account/checkout/templates/fax_order_affiliate.phi";
	}
	$message = read_template( $template, $var );
	$message = strip_tags( preg_replace('!<br.*>!iU', "\n", str_replace("&nbsp;"," ",$message)) );
	$message = str_replace( "\n\n\n", "\n", str_replace( ":\n   \n", ":   ", str_replace( "\t", "", $message ) ) );
	
	/// do it...
	@send_fax_as_email( $g_oAffiliate->fax, $message, $subject, $var->orderNum );
	
	/// also notify agh about faxes that are sent...
	SendMail( "faxnotifications@autoglasshosting.com", "beau@ws2go.net", "AGH Fax Notification: ".$var->order_num, $message );

} // fax_shop_order


//-------------------------------------------------------------------------
// Process the notifications for this order. Currently supported are email & fax.
//-------------------------------------------------------------------------
function ProcessNotifications( &$var ) { 
	
	global $g_Config, $g_oAffiliate, $g_oNDB;

	/// don't allow junk in here...
	ob_start();

	/// email the customer...
	email_customer_order( $var );

	/// email the shop...
	email_shop_order( $var );
	
	/// also notify the shop via fax...
	if( !($var->isReferral || $var->isW2G) && $g_oAffiliate->settings['notify_fax'] && $g_oAffiliate->fax != "" ) {
		fax_shop_order( $var );
	}

	/// check on any text we didn't allow to be echo'ed...
	$text = trim(ob_get_contents());
	if( strlen($warning) > 0 ) {
		if( $g_oAffiliate->isW2G ) {
			$msg = "stifled message:\n\n".$text;
			$SQL = "INSERT INTO `log` SET stamp=Now(), type='Checkout-Notify', code=".$g_oNDB->escape("aff:".$g_oAffiliate->aff).", event=".$g_oNDB->escape($msg);
			@$g_oNDB->execute( $SQL );
		}
	}
	ob_end_clean(); // at very end just in case there was an SQL error

} // ProcessNotifications()


//-------------------------------------------------------------------------
// validate the signup form, and return the error messages in a string.  if the string is empty, then there are no errors
//-------------------------------------------------------------------------
function validateStep1And2( &$frm, &$errors, $mode="standard" ) {

	$errors = new Object;
	$msg = "";

	if( empty($frm['s_FirstName']) ) {
		$errors->s_FirstName = true;
		$msg .= "<li>You did not specify your first name.</li>";
	}
	if( empty($frm['s_LastName']) ) {
		$errors->s_LastName = true;
		$msg .= "<li>You did not specify your last name.</li>";
	}
	/*if( empty($frm['s_Email']) ) {
		$errors->s_Email = true;
		$msg .= "<li>You did not specify your email address.</li>";
	}/**/
	if( strtolower($frm['s_Email']) != strtolower($frm['s_Email_verify']) ) {
		$errors->s_Email_verify = true;
		$msg .= "<li>Your email address was not properly verified.</li>";
	}
	if( empty($frm['s_Address']) ) {
		$errors->s_Address = true;
		$msg .= "<li>You did not specify your address.</li>";
	}
	if( empty($frm['s_City']) ) {
		$errors->s_City = true;
		$msg .= "<li>You did not specify your city.</li>";
	}
	if( empty($frm['s_State']) ) {
		$errors->s_State = true;
		$msg .= "<li>You did not specify your state.</li>";
	}
	if( empty($frm['s_Zip']) ) {
		$errors->s_Zip = true;
		$msg .= "<li>You did not specify zip code.</li>";
	}
	if( empty($frm['s_Phone']) ) {
		$errors->s_Phone = true;
		$msg .= "<li>You did not specify your phone number.</li>";
	}
	
	// If the items in the cart are to be installed
	if( glassInsMode() == "I" || glassInsMode() == "B" ) {
		/* [NL:2013-09-27] disabled/removed
		if( empty($frm['s_CrossStreet']) ) {
			$errors->s_CrossStreet = true;
			$msg .= "<li>You did not specify your nearest Cross Street.</li>";
		}*/
		if( empty($frm['s_InstallMonth']) ) {
			$errors->s_InstallMonth = true;
			$msg .= "<li>You did not specify the Install Month.</li>";
		}
		if( empty($frm['s_InstallDate']) ) {
			$errors->s_InstallDate = true;
			$msg .= "<li>You did not specify the Install Date.</li>";
		}
		if( empty($frm['s_InstallYear']) ) {
			$errors->s_InstallYear = true;
			$msg .= "<li>You did not specify the Install Year.</li>";
		}
		if( empty($frm['s_InstallTime']) ) {
			$errors->s_InstallTime = true;
			$msg .= "<li>You did not specify the Install Time.</li>";
		}
	}
	/// billing info...
	if( $mode != "GC" ) {  // not desired when in Google Checkout mode
		if( empty($frm['b_FirstName']) ) {
			$errors->b_FirstName = true;
			$msg .= "<li>You did not specify your billing first name.</li>";
		}
		if( empty($frm['b_LastName']) ) {
			$errors->b_LastName = true;
			$msg .= "<li>You did not specify your billing last name.</li>";
		}
		/* if( empty($frm['b_Email']) ) {
			$errors->b_Email = true;
			$msg .= "<li>You did not specify your billing email address.</li>";
		} */
		if( empty($frm['b_Address']) ) {
			$errors->b_Address = true;
			$msg .= "<li>You did not specify your billing address.</li>";
		}
		if( empty($frm['b_City']) ) {
			$errors->b_City = true;
			$msg .= "<li>You did not specify your billing city.</li>";
		}
		if( empty($frm['b_State']) ) {
			$errors->b_State = true;
			$msg .= "<li>You did not specify your billing state.</li>";
		}
		if( empty($frm['b_Zip']) ) {
			$errors->b_Zip = true;
			$msg .= "<li>You did not specify your billing zip code.</li>";
		}
		if( empty($frm['b_Phone']) ) {
			$errors->b_Phone = true;
			$msg .= "<li>You did not specify your billing phone number.</li>";
		}
	}
	return $msg;

} // validateStep1And2()


//-------------------------------------------------------------------------
// validate the signup form, and return the error messages in a string.  if the string is empty, then there are no errors
//-------------------------------------------------------------------------
function validateStep3( &$frm, &$errors, $mode="standard" ) {

	global $g_oAffiliate, $bCollectCCInfo;
	
	$errors = new Object;
	$msg = "";

	/// be sure they agree...
	if( $g_oAffiliate->displayUnderstand && empty($frm['agreeLowPrice']) && empty($frm['agreeFactors']) ) {
		$errors->agreeLowPrice = true;
		$errors->agreeFactors = true;
		$msg .= "<li>You must agree to at least one of the two terms and conditions listed in red on Step 4 of the checkout, beneath the total cost quote for your order.</li>";
	}
	
	/// new "by phone" charge type override (only in the new GC version)...
	if( isset($_REQUEST['c_type_override']) ) {
		if( trim($_REQUEST['c_type_override'])=="PHONE" ) {
			$frm['c_type'] = "PHONE";
		}
	}
	
	/// check the payment type...
	if( $mode == "GC" ) return "";  // all okay; passthru
	if( empty($frm['c_type']) ) {
		$errors->c_type = true;
		$msg .= "<li>You did not specify the payment type.</li>";
	}
	if( $frm['c_type'] == 'CASH' || $frm['c_type'] == 'CHECK' || $frm['c_type'] == 'NET30' || $frm['c_type'] == 'PayPal') {
		return $msg;  // all done
	}

	/// W2G collects & charges credit cards...
	if( $bCollectCCInfo ) {

		if( empty($frm['c_expyear']) ) {
			$errors->c_expyear = true;
			$msg .= "<li>You did not specify the card expiration year.</li>";
		}
		if( empty($frm['c_expmonth']) ) {
			$errors->c_expmonth = true;
			$msg .= "<li>You did not specify the card expiration month.</li>";
		}
		if( empty($frm['c_cvv']) && !isset($frm['c_iscvv']) ) {
			$errors->c_cvv = true;
			$msg .= "<li>You did not specify the card CVV/CCID number. If your card does not have this number, please call us at " . $g_oAffiliate->settings['site_phone'] . "</li>";
		}
		if( $g_oAffiliate->displayEnterY && strtoupper(trim($frm['c_agree'])) != "Y" ) {
			$errors->c_agree = true;
			$msg .= "<li>You did not agree for your order total to be charged to your credit card. Please type a \"Y\" in the field below.</li>";
		}
		if( empty($frm['c_num']) ) {
			$errors->c_num = true;
			$msg .= "<li>You did not specify the credit card number.</li>";
		} else {
			$themessage = CCValidationSolution($frm['c_num']);
			if( $themessage != "" ) {
				$errors->c_num = true;
				$msg .= "<li>$themessage</li>";
			}
		}

	/// affiliates don't do credit cards...
	} elseif( $g_oAffiliate->displayEnterY ) {
		if(strtoupper(trim($frm['c_agree'])) != "Y") {
			$errors->c_agree = true;
			$msg .= "<li>You did not agree for to the payment amount for your order. Please type a \"Y\" in the field below.</li>";
		}
	}

	return $msg;

} // validateStep3()


//-------------------------------------------------------------------------
// Determine what the next ID will be.
/*/ !!! [N20040819] This is crap. And I don't say that lightly. I really need to get this fixed to be coded to work correctly under asynchronous conditions. !!! /*/
/* [NL:2008-09-15] Still total CRAP! Occasional errors redirected to home caused by this? (Locked file bailouts?) */
//-------------------------------------------------------------------------
function getTheInvoiceID() {

	global $g_oNDB;
	
	$ID = intval($g_oNDB->getField(0, "SELECT Max(ID) FROM invoice", 0));
	$lock_file = "/tmp/windshieldstogo_LastInvoiceID.temp";
	if( !file_exists($lock_file) ) {
		system("echo \"".$ID."\" > ".$lock_file);
		$invoiceID = ($ID + 1);
	} else {
		$c = 0;
		while( file_exists($lock_file) ) {
			$var = $c++;
		}
		system("echo \"".$ID."\" > ".$lock_file);
		$invoiceID = ($ID + 1);
	}
	system( "rm ".$lock_file." -f" );
	///exit("<h1>new invoiceID: " . $invoiceID . "</h1>");
	
	/// all done...
	$_SESSION['invoice_id'] = $invoiceID;
	return $invoiceID;

} // getTheInvoiceID ()


///-------------------------------------------------------------------------
function getProdDescription() {

	//Get the carts contents
	$prodArray = ($_SESSION['cart']->getCartItems());
	$description = "";
	for( $c=0; $c<count($prodArray); $c++ ) {
		$pid = $prodArray[$c]['id'];
		$pmake = $prodArray[$c]['make'];
		$pmodel = $prodArray[$c]['model'];
		$pstyle = $prodArray[$c]['style'];
		$pprice = $prodArray[$c]['PRICE'];
		$pyear = $prodArray[$c]['year'];
		$pqty = $_SESSION['cart']->items[$pid]['QTY'];
		$popt = $_SESSION['cart']->items[$pid]['OPT'];
		$pcode = $_SESSION['cart']->items[$pid]['CODE'];
		//Lets make sure the product qty is always one
		$description .= ($c + 1) . ") " . $pid . "- " . $pmake . ", " . $pmodel . ", " . $pstyle . ", " . $pprice . ", " . $pyear . "; QTY-" . $pqty . "; OPT-" . $popt . "; CODE-" . $pcode; 
	}
	return $description;

} // getProdDescription()


///-------------------------------------------------------------------------
function emptyCart() {

	/*don't do this so that we can remember the aff/acct
	session_unset();
	session_destroy();/**/
	$acct = $_SESSION['AFF_KEY'];  // we want to remember this
	$_SESSION = array();
	//session_unregister('cart');
	unset($_SESSION['cart']);
	clearOrderInfo();
	clearBillingInfo();
	//session_register('AFF_KEY');
	//$AFF_KEY = $acct;
	$_SESSION['AFF_KEY'] = $acct;
	session_write_close();

} // emptyCart()


///-------------------------------------------------------------------------
function isInvoiceID() {

	if( isset($_SESSION['invoiceID']) ) emptyCart();

} // isInvoiceID()


//-------------------------------------------------------------------------
// this function saves the order information into the _SESSION variable $_SESSION['order'].  it is used in the purchase confirmation stage
//-------------------------------------------------------------------------
function createOrderObject( $frm ) {
	
	global $g_Config, $g_oAffiliate;
	
	$oOrderInfo = new Object();
	$oOrderInfo->isW2G = $frm['isW2G'];
	$oOrderInfo->isReferral = $frm['isReferral'];
	$oOrderInfo->s_Method = $frm['s_Method'];
	$oOrderInfo->s_FirstName = $frm['s_FirstName'];
	$oOrderInfo->s_LastName = $frm['s_LastName'];
	$oOrderInfo->s_Email = strtolower($frm['s_Email']);
	$oOrderInfo->s_Company = $frm['s_Company'];
	$oOrderInfo->s_Address = $frm['s_Address'];
	$oOrderInfo->s_City = $frm['s_City'];
	$oOrderInfo->s_State = $frm['s_State'];
	$oOrderInfo->s_Zip = $frm['s_Zip'];
	$oOrderInfo->s_Phone = $frm['s_Phone'];
	$oOrderInfo->s_Fax = $frm['s_Fax'];
	$oOrderInfo->s_VIN = $frm['s_VIN'];
	$oOrderInfo->s_Unit = $frm['s_Unit'];
	$oOrderInfo->s_InstallDate = $frm['s_InstallDate'];
	$oOrderInfo->s_InstallYear = $frm['s_InstallYear'];
	$oOrderInfo->s_InstallMonth = $frm['s_InstallMonth'];
	$oOrderInfo->s_InstallTime = $frm['s_InstallTime'];		
	$oOrderInfo->s_CrossStreet = '';  // [NL:2013-09-27] $frm['s_CrossStreet'];	
	$oOrderInfo->b_SameAsShipping = (empty($frm['b_SameAsShipping']) ? 0 : 1);
	$oOrderInfo->b_FirstName = $frm['b_FirstName'];
	$oOrderInfo->b_LastName = $frm['b_LastName'];
	$oOrderInfo->b_Email = strtolower($frm['b_Email']);
	$oOrderInfo->b_Company = $frm['b_Company'];
	$oOrderInfo->b_Address = $frm['b_Address'];
	$oOrderInfo->b_City = $frm['b_City'];
	$oOrderInfo->b_State = $frm['b_State'];

	//Check for tax infomation
	$_SESSION['cart']->state  = $frm['b_State'];
	if( $g_oAffiliate->isW2G || $isReferral ) {
		/*unimplemented: if( $bInState ) $rate = 0.0725;
		$bInState = @strpos(strtolower($_SESSION['order']->b_State), strtolower($CFG->taxState));*/
		$_SESSION['cart']->setTax( 0.00 );
	} else {
		$_SESSION['cart']->setTax( $g_oAffiliate->settings['tax'] );
	}
	$oOrderInfo->tax = $_SESSION['cart']->tax;
	
	/// get the product's info; this will need updating if/whenever we need to handle more than one item in the cart...
	foreach( $_SESSION["cart"]->items as $prodID => $rInfo ) {
		$productID = $prodID;
		$zip = $rInfo["CODE"];
	}
	$oPricing = new ProductInfo( $productID, $zip, null, false );
	// $oOrderInfo->b_Country = $frm['b_Country'];
	$oOrderInfo->b_Zip = $frm['b_Zip'];
	$oOrderInfo->b_Phone = $frm['b_Phone'];
	$oOrderInfo->i_Name = $frm['i_Name'];
	$oOrderInfo->i_Phone = $frm['i_Phone'];
	$oOrderInfo->i_Policy = $frm['i_Policy'];
	$oOrderInfo->i_Deductible = $frm['i_Deductible'];
	$oOrderInfo->comments = $frm['comments'];
	$oOrderInfo->referrer = $frm['referrer'];
	$oOrderInfo->paymentMethod = $frm['paymentMethod'];	
	/// forced-shipping calc for FC/FH, or the special YH decrease for DR/QT and VN...
	if( $frm['ShipWindshields'] == "YC" ) {
		$oOrderInfo->shipping = 275;
	} elseif( $frm['ShipWindshields'] == "YH" ) {
		if( $oPricing->opening == 'DR' || $oPricing->opening == 'QT' ) {
			$oOrderInfo->shipping = 45;
		} elseif( $oPricing->opening == 'VN' ) {
			$oOrderInfo->shipping = 35;
		} else {
			$oOrderInfo->shipping = 185;
		}
	} elseif( $frm['ShipWindshields'] == "FC" ) {
		$oOrderInfo->shipping = 275 + $oPricing->extraShippingPrice;
	} elseif( $frm['ShipWindshields'] == "FH" ) {
		$oOrderInfo->shipping = 160 + $oPricing->extraShippingPrice;
	} else {
		$oOrderInfo->shipping = 0;		
	}
	$oOrderInfo->ShipWindshields = $frm['ShipWindshields'];
	$oOrderInfo->s_Warehouse = $frm['s_Warehouse'];
	
	/// set up the session...
	$_SESSION['order'] = $oOrderInfo;

} // createOrderObject()


//-------------------------------------------------------------------------
// this function saves the order information into the _SESSION variable $_SESSION['orderinfo'].  it is used in the purchase confirmation stage
//-------------------------------------------------------------------------
function createBillingObject( &$frm ) {

	$oBillingInfo = new Object();
	$oBillingInfo->c_type = $frm['c_type'];
	$oBillingInfo->c_num = $frm['c_num'];
	$oBillingInfo->c_expmonth = $frm['c_expmonth'];
	$oBillingInfo->c_expyear = $frm['c_expyear'];
	$oBillingInfo->c_cvv = $frm['c_cvv'];	
	$oBillingInfo->c_iscvv = $frm['c_iscvv'];		
	$oBillingInfo->subtotal = $frm['subtotal'];
	$oBillingInfo->shipping = $frm['shipping'];
	$oBillingInfo->tax = $frm['tax'];
	$oBillingInfo->total = $frm['total'];
	$oBillingInfo->discountCode = $frm['discountCode'];
	$oBillingInfo->serviceType = $frm['serviceType'];
	$oBillingInfo->paymentMethod = $frm['paymentMethod'];
	$oBillingInfo->agreeLowPrice = $frm['agreeLowPrice'];
	$oBillingInfo->agreeFactors = $frm['agreeFactors'];
	$oBillingInfo->cartID = '';

	/// set up the session...
	$_SESSION['billing'] = $oBillingInfo;

} // createBillingObject()


//-------------------------------------------------------------------------
// this function is the counterpart to save_orderinfo.  it is used to retrieve the order information in the complete order page
//-------------------------------------------------------------------------
function getOrderInfo() {

	if( empty($_SESSION['order']) ) {
		return false;
	} else {
		return $_SESSION['order'];
	}

} // getOrderInfo()


//-------------------------------------------------------------------------
// this function is the counterpart to save_orderinfo.  it is used to retrieve the order information in the complete order page
//-------------------------------------------------------------------------
function getBillInfo() {

	if( empty($_SESSION['billing']) ) return false;
	return $_SESSION['billing'];

} // getBillInfo()


///-------------------------------------------------------------------------
function isClearBilling() {

	if( !getBillInfo() ){
		return true;
	} else {
		clearBillingInfo();
	}
	if( isset($_SESSION['invoiceID']) ) {
		clearBillingInfo();
	} 

} // isClearBilling()


///-------------------------------------------------------------------------
function isClearOrder() {

	if( !getOrderInfo() ) {
		return true;
	} else {
		clearOrderInfo();
	}
	if( isset($_SESSION['invoiceID']) ) {
		clearOrderInfo();
	} 

} // isClearOrder()


//-------------------------------------------------------------------------
// this function is called to clear the orderinfo _SESSION variable, it should be used after an order was successfully completed
//-------------------------------------------------------------------------
function clearOrderInfo() {

	unset($_SESSION['order']);
	$_SESSION['order'] = "";

} // clearOrderInfo()


//-------------------------------------------------------------------------
// this function is called to clear the orderinfo _SESSION variable, it should be used after an order was successfully completed
//-------------------------------------------------------------------------
function clearBillingInfo() {

	unset($_SESSION['billing']);
	$_SESSION['billing'] = "";

} // clearBillingInfo()


///-------------------------------------------------------------------------
function cleanString( $s ) {

	$s = addslashes($s);
	$s = trim($s);
	return $s;

} // cleanString()


///-------------------------------------------------------------------------
function getNextOrderNum( $ocode, $invoiceID, $minNum, $is_w2g ) {

	global $g_oNDB;
	
	/// get the next order number for this affiliate's code...
	$orderCode = $g_oNDB->escape($ocode);  // so the aff code will include 'tick' delimiters!
	if( $is_w2g ) {
		$minimum = intval(GetSetting("w2g_orderNumMin", 0, 0, 'master'));
	} else {
		$minimum = intval($minNum)>0 ? $minNum : 1;
	}
	$g_oNDB->execute( "INSERT INTO orderNums(stamp,refID,code,num) SELECT Now(), '$invoiceID', ".$orderCode.", IF(Max(num)+1 > $minimum, Max(num)+1, $minimum) FROM orderNums WHERE code=".$orderCode );
	$orderNum = $g_oNDB->getField( 'num', "SELECT num FROM orderNums WHERE ID='".$g_oNDB->getLastInsertID()."'", -1 );

	/// returned an error...
	if( $orderNum<0 ) {
		$g_oNDB->execute( "INSERT INTO `log` SET stamp=Now(), type='checkout-NextNum', event=".$g_oNDB->escape("Could not generate an order number for invoice: ".$invoiceID) );
		$orderNum = 0;
	}
	return $orderNum;
	
} // getNextOrderNum()


//-------------------------------------------------------------------------
// get_random_key:  Returns a random key that can be used as a cart key.
//-------------------------------------------------------------------------
function get_random_key( $pwdLen=25, $bForCart=true) {

	/// initz...
	$dSource = "";
	$result = "";
	$pwdSource = "";  // no tick chars!
	$pwdSource .= "ABCDEFGHJKLMNPQRSTUVWXYZ";
	$pwdSource .= "23456789";
	if( !$bForCart ) $pwdSource .= "abcdefghijklmnopqrstuvwxyz01O-_()+=[]!#";
	srand( (double)microtime() * 1234567 );

	/// first pass...
	while( $pwdLen ) {
		$randomKey .= substr( $pwdSource, rand(0, strlen($pwdSource)), 1 );
		$pwdLen--;
	}
	if( !$bForCart ) return $randomKey;

	/// ensure uniqueness... [N20041127] this doesn't like it will work right, though
	$r = db_query("SELECT randomKey FROM cart WHERE randomKey='".$randomKey."'");
	$n = mysql_num_rows($r);
	while( $n>0 ){
		while( $pwdLen ) {
			$randomKey .= substr( $pwdSource, rand(0, strlen($pwdSource)), 1 );
			$pwdLen--;
		}
	}
	return $randomKey;

} // get_random_key()


//-------------------------------------------------------------------------
// str_var_dump:  Returns the string from a var_dump() on a variable.
//-------------------------------------------------------------------------
function str_var_dump($a) {

	ob_start();
	var_dump($a);
	$ret = ob_get_contents();
	ob_end_clean();
	return $ret;

} // str_var_dump()


//-------------------------------------------------------------------------
// Partner_GetDiscounts:  Return the partner's discount numbers formatted into 
//-------------------------------------------------------------------------
function Partner_GetDiscounts( $partnerID ) {

	global $g_oNDB;
	
	/// get the data...
	$rPartner = $g_oNDB->getRow("SELECT partnerListPercentOff, partnerListDollarsOff, partnerNetPercentExtra, partnerNetDollarsExtra, partnerMoldingPercentOff, partnerLaborDollarsOff, promptForPrice, priceAutoAccept FROM affiliate WHERE ID='".$partnerID."'");
	
	/// set up the fields...
	$rPartnerDiscounts = array();
	$rPartnerDiscounts['promptForPrice'] = $rPartner['promptForPrice'];
	$rPartnerDiscounts['priceAutoAccept'] = $rPartner['promptForPrice'];
	$rPartnerDiscounts['listPercentOff'] = $rPartner['partnerListPercentOff'];
	$rPartnerDiscounts['listDollarsOff'] = $rPartner['partnerListDollarsOff'];
	$rPartnerDiscounts['netPercentExtra'] = $rPartner['partnerNetPercentExtra'];
	$rPartnerDiscounts['netDollarsExtra'] = $rPartner['partnerNetDollarsExtra'];
	$rPartnerDiscounts['moldingPercentOff'] = $rPartner['partnerMoldingPercentOff'];
	$rPartnerDiscounts['laborDollarsOff'] = $rPartner['partnerLaborDollarsOff'];
	$rPartnerDiscounts['listPercentOffExtraPosted'] = 0.00;
	
	/// all done...
	return $rPartnerDiscounts;

} // Partner_GetDiscounts()


//-------------------------------------------------------------------------
// ExtractRandomCharacter:  Returns a random character out of the string and removes it from the string.
//-------------------------------------------------------------------------
function ExtractRandomCharacter( &$str, $bCallSrand=false, $bDebug=false ) {

	if( $bDebug ) echo "str: ".$str."<br>\n";
	if( $bCallSrand ) srand( (double)microtime() * 1234567 );
	$length = strlen($str);
	if( $length < 1 ) return "";
	$index = rand(0, $length-1);
	$char = substr( $str, $index, 1 );
	if( $length==1 ) {
		$str = "";
	} elseif( $index==$length ) {
		$str = substr( $str, 0, $length-1 );
	} elseif( $index==0 ) {
		$str = substr( $str, 1 );
	} else {
		$str = substr( $str, 0, $index ) . substr( $str, $index+1 );
	}
	if( $bDebug ) echo $char." : (".$index.") : ".$str."<br>\n";
	return $char;

} // ExtractRandomCharacter()


//-------------------------------------------------------------------------
// GenerateCustomerKey
//-------------------------------------------------------------------------
function GenerateCustomerKey() {

	$nums = "23456789";  // a variable since the extractor whittles it down by reference
	$customerKey = ExtractRandomCharacter($nums, true);
	$nums .= "01";  // don't lead with a zero so that we always have 4 digits; avoid them thinking that a starting 1 is a lower-case L
	return intval($customerKey.ExtractRandomCharacter($nums).ExtractRandomCharacter($nums).ExtractRandomCharacter($nums));

} // GenerateCustomerKey()


//-------------------------------------------------------------------------
// Partner_GenerateCodes:  Generate the random codes that go with each option.
//-------------------------------------------------------------------------
function Partner_GenerateCodes( $bDebug=false ) {

	/// initz...
	srand( (double)microtime() * 1234567 );
	$digits = "123456789";  // $digits1 = $digits; $digits2 = $digits; $digits3 = $digits;

	/// create the randomized codes...
	$codes = array();
	$codes['F'] = ExtractRandomCharacter($digits, false, $bDebug) . ExtractRandomCharacter($digits, false, $bDebug) . ExtractRandomCharacter($digits, false, $bDebug);
	$codes['L'] = ExtractRandomCharacter($digits, false, $bDebug) . ExtractRandomCharacter($digits, false, $bDebug) . ExtractRandomCharacter($digits, false, $bDebug);
	$codes['D'] = ExtractRandomCharacter($digits, false, $bDebug) . ExtractRandomCharacter($digits, false, $bDebug) . ExtractRandomCharacter($digits, false, $bDebug);
	if( $bDebug ) printArray($codes);

	/// all done...
	return $codes;

} // Partner_GenerateCodes()


//-------------------------------------------------------------------------
// Partnering_Find:  Return the ID of a qualifying affiliate partner or 0 if none is found.
//-------------------------------------------------------------------------
function Partnering_Find( $zip ) {

	global $g_oNDB;
	
	/// get a list of the zips...
	$SQL = "SELECT affiliateZipPartnerings.zipPrefix FROM affiliateZipPartnerings LEFT JOIN affiliate ON affiliateZipPartnerings.affiliateID=affiliate.ID WHERE affiliate.isPartner='Y' AND affiliate.isActive='Y' AND affiliate.isEnabled='Y' AND affiliateZipPartnerings.zipPrefix LIKE '".substr($zip, 0, 3)."%' AND affiliateZipPartnerings.isActive='Y'";  // echo $SQL."<br>";
	if( !($rPartnerings = $g_oNDB->getColumn($SQL)) ) return -1;  // not found or there was an error
	
	/// find the best match...
	$matching = "";
	if( isInArray($zip, $rPartnerings) ) {
		$matching = $zip;
	} else{
		for( $i=5; $i>0; $i-- ) {
			if( isInArray(substr($zip,0,$i), $rPartnerings) ) {
				$matching = substr($zip, 0, $i);
				break;
			}
		}
	}
	if( $matching=="" ) return 0;  // nothing matched
	
	/// return the ID of the partnering that was matched...
	return $g_oNDB->getField("SELECT affiliateID FROM affiliateZipPartnerings WHERE zipPrefix=".$g_oNDB->escape($matching));

} // Partnering_Find()


//-------------------------------------------------------------------------
// Partner_AssignOffer:  Assign the specified affiliate to the invoice.    
//-------------------------------------------------------------------------
function Partner_AssignOffer( $partnerID, $invoiceID, $bDebug=false ) {

	global $g_oNDB, $g_Config, $g_oAffiliate;
	
	/// get the data...
	//$rPartner = $g_oNDB->getRow("SELECT * FROM affiliate WHERE ID='".$partnerID."'");
	$oPartner = LoadAffiliateInfo( $partnerID );
	if( $oPartner->ID < 1 ) {
		$g_oNDB->execute( "INSERT INTO `log` SET stamp=Now(), type='Partner-Assign', flag='stage-1', code=".$g_oNDB->escape('aff:'.$g_oAffiliate->aff).", event=".$g_oNDB->escape($oPartner->err) );
		echo( $oPartner->err );
	}
	$rCheckout = $g_oNDB->getRow("SELECT invoice.orderNum, invoice.orderCode, checkout.*, cartItems.productID, cartItems.quantity, cartItems.partDetails, cartItems.parameters, cartItems.ID as cartItemID, product.* FROM invoice LEFT JOIN checkout ON invoice.checkoutID=checkout.ID LEFT JOIN cartItems ON checkout.cartID=cartItems.cartID LEFT JOIN product ON cartItems.productID=product.ID WHERE invoice.ID='".$invoiceID."'");

	/// determine the price...
	$rDiscounts = Partner_GetDiscounts( $partnerID );
	if( $bDebug ) $oPricing = new ProductInfo( $rCheckout['productID'], $rCheckout['partDetails'], null, true );  // original price for comparison
	$oPricing= new ProductInfo( $rCheckout['productID'], "", $rDiscounts, $bDebug );
	
	/// set up the email offer fields...
	$var = new object;
	$var->orderNum = $rCheckout['orderCode']."-".sprintf("%05d",$rCheckout['orderNum']);
	$var->business = $oPartner->business;
	$var->year = $rCheckout['year'];
	$var->make = $rCheckout['make'];
	$var->model = $rCheckout['model'];
	$var->style = $rCheckout['style'];
	$var->part = $rCheckout['part'];
	$var->productID = $rCheckout['productID'];
	$var->itemPrice = $oPricing->itemPrice;
	$var->laborPrice = sprintf("%.2f", $oPricing->installedPrice - $oPricing->itemPrice);
	$var->installedPrice = $oPricing->installedPrice;
	$var->s_InstallDate = $rCheckout['s_InstallDate'];
	$var->s_InstallYear = $rCheckout['s_InstallYear'];
	$var->comments = $rCheckout['comments'];
	
	/// set up the options...
	//DumpInfo($oPartner);
	$randomCodes = Partner_GenerateCodes( /*true /*debugging*/ );
	$url = "https://www.autoglasshosting.com/account/management/invoices/pendings.php?U=".$oPartner->acct."&A=PA&ID=".$invoiceID."&answer=";
	if( !($oPartner->offerFullOption || $oPartner->offerLaborOnly) ){
		echo "<!--\n\nWe can't offer this partner either full install or labor-only !\n\n-->";
		return false;
	}
	$var->optionsText = "";
	if( $oPartner->offerFullOption ) {
		$var->optionsText .= "<p><b style='color: #009800;'>Accept:</b>&nbsp; To fully handle this offer for us at the $".$var->installedPrice." price, please click this link:<br><nobr><a target=_blank style='color: #009800;' href=\"".$url."F&code=".$randomCodes['F']."\">Click here accept this offer and fully handle the order.</a></nobr>";
	}
	if( $oPartner->offerLaborOnly ) {
		$var->optionsText .= "<p><b style='color: #0040E0;'>Labor-Only:</b>&nbsp; Or, to install this item for $".$var->laborPrice.", but have us ship the the glass to your, please click this link:<br><nobr><a target=_blank style='color: #0040E0;' href=\"".$url."L&code=".$randomCodes['L']."\">Click here to accept the labor quote and have us ship you the glass.</a></nobr>";
	}
	$var->optionsText .= "<p><b style='color: #C80000;'>Decline:</b>&nbsp; Lastly, to decline this offer, please click this link:<br><a target=_blank style='color: #C80000;' href=\"".$url."D&code=".$randomCodes['D']."\">click here to decline</a>";
	
	/// set up the email content by using its template...
	$template = $g_Config->rAff['basepath']."checkout/templates/email_partner_offer_html.phi";
	$message = read_template($template, $var);

	/// update the invoice record...
	if( $bDebug ) echo "Assigning $invoiceID to $partnerID<br>";
	$SQL = "UPDATE invoice SET partnerID='".$partnerID."', partnerAnswer='pending', randomCodeFull='".$randomCodes['F']."', ";
	$SQL .= "randomCodeLabor='".$randomCodes['L']."', randomCodeDecline='".$randomCodes['D']."' WHERE ID='".$invoiceID."'";  // echo $SQL."<br>";
	if( !$g_oNDB->execute($SQL) ) {
		$err = $g_oNDB->message();
		$g_oNDB->execute( "INSERT INTO `log` SET stamp=Now(), type='Partner-Assign', flag='stage-2', event=".$g_oNDB->escape($err) );
		echo "<!--\n\n".$err."\n\n-->";
		return false;
	}

	/// update the cartItems record with the offer prices...
	$SQL = "UPDATE cartItems SET partnerGlassPayment='".$var->itemPrice."', partnerLaborPayment='".$var->laborPrice."' WHERE ID='".$rCheckout['cartItemID']."'";
	if( !$g_oNDB->execute($SQL) ) {
		$err = $g_oNDB->message();
		$g_oNDB->execute( "INSERT INTO `log` SET stamp=Now(), type='Partner-Assign', flag='stage-3', event=".$g_oNDB->escape($err) );
	}
	
	/// send the the email to the faxing service...
	echo "<!--\n\nPartner(".$partnerID.") email sent: ".$oPartner->email."-->";
	SendMail( "orders@windshieldstogo.com", $oPartner->email, "Partner Offer for Order # ".$var->orderNum, $message, true );
	if( $oPartner->settings['notify_fax'] && $oPartner->fax != "" ) {
		$fax_email = eregi_replace( "[^[:digit:]]", "", strtolower($oPartner->fax) );
		if( strlen($fax_email)==11 ) $fax_email = eregi_replace( "^1", "", $fax_email );
		$fax_email = $fax_email."@rapidfax.com";
		if( strlen($fax_email) < 23 ) {
			$g_oNDB->execute( "INSERT INTO `log` SET stamp=Now(), type='Partner-Assign', flag='stage-4', event='Probable fax error! Number too short: ".addslashes($fax_email)."::FAX=".addslashes($g_oAffiliate->fax)."'" );
		}
		$message = str_replace( "\n\n\n", "\n", str_replace( ":\n   \n", ":   ", str_replace( "\t", "", strip_tags( preg_replace('!<br.*>!iU', "\n", str_replace("&nbsp;"," ",$message))) ) ) );
		@SendMail( "sendfax@autoglasshosting.com", $fax_email, "Partner Offer for Order # ".$var->orderNum, $message );  // plain
	}
	
	/// all done...
	return true;

} // Partner_AssignOffer()


//-------------------------------------------------------------------------
// setOrderInfo_AGH:  Establish the info about the item(s) being ordered.  
//-------------------------------------------------------------------------
function ANET_setOrderInfo( &$oGateway ) {

	/// use the product id for the unique customer identifier...
	global $g_oNDB;
	$prodArray = $_SESSION['cart']->getCartItems();
	$productID = $prodArray[0]['id'];
	/*
	$rProduct = $g_oNDB->getRow("SELECT * FROM product WHERE ID='".$productID."'");
	//? should really use getProdDescription() even though it's ugly ?
	$description = $rProduct['year'].' '.$rProduct['make'].' '.$rProduct['model'];
	*/
	$description = getProdDescription();

	/// map in the field values...
	$oGateway->rOrderInfo['product'] = $productID;
	if( glassInsMode() == "I" || glassInsMode() == "B" ) {
		$oGateway->rOrderInfo['description'] = "Auto glass installation: ".$description;
	} elseif( $prodArray[0]['OPT']=='hardware' || $prodArray[0]['OPT']=='molding' ) {
		$oGateway->rOrderInfo['description'] = "Auto glass purchase: ".$description." (with ".$prodArray[0]['OPT'].")";
	} else {
		$oGateway->rOrderInfo['description'] = "Auto glass purchase: ".$description;
	}
	//$oGateway->rOrderInfo['total'] = $_SESSION['cart']->getTotal();
	$oGateway->rOrderInfo['total'] = '1.00';
	//$oGateway->rOrderInfo['total'] = '1.00';

} // ANET_setOrderInfo()



//-------------------------------------------------------------------------
// ANET_setCustomerInfo:  Establish the customer's billing/payment information.
//-------------------------------------------------------------------------
function ANET_setCustomerInfo( &$oGateway ) {

	/// use the product id for the unique customer identifier...
	/* [NL:2010-02-14] now using the invoiceID (even if stupidly-guessed) not productID
	$prodArray = $_SESSION['cart']->getCartItems();
	$productID = $prodArray[0]['id'];
	$oGateway->rCustomerInfo['ID'] = $productID;
	/**/
	$invoiceID = getTheInvoiceID(); // note: must call this function (to set up the session) !
	$oGateway->rCustomerInfo['ID'] = $invoiceID;
	
	/// map in the customer info...
	$oBillingInfo = getBillInfo();
	$oGateway->rCustomerInfo['email'] = $oBillingInfo->s_Email;
	$oGateway->rCustomerInfo['first'] = $oBillingInfo->s_FirstName;
	$oGateway->rCustomerInfo['last'] = $oBillingInfo->s_LastName;
	$oGateway->rCustomerInfo['address'] = $oBillingInfo->s_Address;
	$oGateway->rCustomerInfo['city'] = $oBillingInfo->s_City;
	$oGateway->rCustomerInfo['state'] = $oBillingInfo->s_State;
	$oGateway->rCustomerInfo['zip'] = $oBillingInfo->s_Zip;
	$oGateway->rCustomerInfo['phone'] = $oBillingInfo->s_Phone;

	/// map in the charge info...
	$oGateway->rCustomerInfo['c_num'] = $oBillingInfo->c_num;
	$oGateway->rCustomerInfo['c_exp'] = $oBillingInfo->c_expmonth.substr($oBillingInfo->c_expyear,2);
	$oGateway->rCustomerInfo['c_cvn'] = $oBillingInfo->c_cvv;
	
	/* only used to intentionally cause errors
	$oGateway->rCustomerInfo['c_num'] = '9876543210';
	$oGateway->rCustomerInfo['c_exp'] = 'MARZO-14';
	$oGateway->rCustomerInfo['c_cvn'] = 'A-Z';
	/* */

} // ANET_setCustomerInfo()


//-------------------------------------------------------------------------
// Log the results of this transaction to the Nags System                  
// Jeremy Locke 2007-09-03                                                 
//-------------------------------------------------------------------------
function LogAdd( $checkout_step, &$oInfo ) {

	global $g_oAffiliate, $hDB;

	// Set
	if( $checkout_step == '3' ) {
		$step = "receipt";
		$notes = "Order #".$oInfo->order_num;
	} else {
		$step = "checkout";
		$notes = $checkout_step;
	}

	// Execute
	$sql = "INSERT INTO `nsLog` SET"
		." `stamp` = '".mysql_escape_string(date("Y-m-d H:i:s"))."',"
		." `ip` = '".mysql_escape_string($_SERVER["REMOTE_ADDR"])."',"
		." `aff` = '".mysql_escape_string($g_oAffiliate->aff)."',"
		." `step` = '".mysql_escape_string($step)."',"
		." `notes` = '".mysql_escape_string($notes)."'";
	@mysql_query($sql, $hDB);

} // LogAdd()


//-------------------------------------------------------------------------
// [2018] Abridged checkout - shortcut order with alternate input mapping. 
//-------------------------------------------------------------------------
function AbridgedBypass_ConfigureFields( &$data ) {
	
	/// map over the flat fields...
	$fields = array(
		'first'   => 's_FirstName',
		'last'    => 's_LastName',
		'phone'   => 's_Phone',
		'email'   => 's_Email',
		'address' => 's_Address',
		'city'    => 's_City',
		'state'   => 's_State',
		'zip'     => 's_Zip',
		's_VIN'   => 's_VIN',
		's_Unit'  => 's_Unit',
		'ctype'   => 'c_type',
		'iyear'   => 's_InstallYear',
		'imonth'  => 's_InstallMonth',
		'iday'    => 's_InstallDate',
		'itime'   => 's_InstallTime',
	);
	foreach( $fields as $field => $name ) {
		if( !isset($_GET[$field]) ) continue;
		$data[$name] = stripslashes($_GET[$field]);
	}
	
	/// map the data into the expected session objects...
	createOrderObject( $data );

} // AbridgedBypass_ConfigureFields()


//-------------------------------------------------------------------------
// Shortcut for the order object, also map requested date fields.          
//-------------------------------------------------------------------------
function AbridgedBypass_ConfigureFields_OLD( &$data ) {
	
	/// map over the flat fields...
	$fields = array(
		'first'   => 's_FirstName',
		'last'    => 's_LastName',
		'phone'   => 's_Phone',
		'email'   => 's_Email',
		'address' => 's_Address',
		'city'    => 's_City',
		'state'   => 's_State',
		'zip'     => 's_Zip',
		's_VIN'   => 's_VIN',
		's_Unit'  => 's_Unit',
	);
	foreach( $fields as $field => $name ) {
		if( !isset($_GET[$field]) ) continue;
		$data[$name] = stripslashes($_GET[$field]);
	}
	
	/// interpret the install date and time...
	if( isset($_GET['date']) && trim($_GET['date']) != '' ) {
		if( strpos( $_GET['date'], '/' ) !== false ) $delim = '/';
		if( strpos( $_GET['date'], '-' ) !== false ) $delim = '-';
		$parts = explode($delim, $_GET['date']);
		if( is_array($parts) && count($parts) == 3 ) {
			if( intval($parts[0]) > 0 && intval($parts[1]) > 0 && intval($parts[2]) > 0 ) {
				$install_date = @mktime( 0, 0, 0, $parts[0], $parts[1], $parts[2] );
				$data['s_InstallDate'] = date( 'd', $install_date );
				$data['s_InstallYear'] = date( 'Y', $install_date );
				$data['s_InstallMonth'] = date( 'F', $install_date );
			}
		}
	}
	if( isset($_GET['time']) && trim($_GET['time']) != '' ) {
		if( $_GET['time']=='morning' ) $data['s_InstallTime'] = '8 to 1';
		if( $_GET['time']=='afternoon' ) $data['s_InstallTime'] = '12 to 5';
	}

	/// map the data into the expected session objects...
	createOrderObject( $data );

} // AbridgedBypass_ConfigureFields_OLD() */


