/**
	 * Attempts to authorize and capture a credit card for a single payment of a payment basket
	 *
	 * @param array $card                           contains type, number, firstname, lastname, expmonth, expyear, and optionally: address, zip, country
	 * @param cbpaidPaymentBasket $paymentBasket
	 * @param int $now                              unix timestamp of now
	 * @param cbpaidsubscriptionsNotification $ipn  returns the stored notification
	 * @param boolean $authnetSubscription          true if it is a subscription and amount is in mc_amount1 and not in mc_gross
	 * @return array|bool                           subscriptionId if subscription request succeeded, otherwise ARRAY( 'level' => 'spurious' or 'fatal', 'errorText', 'errorCode' => string ) of error to display
	 */
	protected function processSinglePayment( $card, $paymentBasket, $now, &$ipn, $authnetSubscription )
	{
		if ( $this->hasPaypalApi() ) {
			$countries			=	new cbpaidCountries();

			if ( $authnetSubscription > 1 ) {
				if ( $paymentBasket->period1 ) {
					$amount		=	sprintf( '%.2f', $paymentBasket->mc_amount1 );
				} else {
					$amount		=	sprintf( '%.2f', $paymentBasket->mc_amount3 );
				}
			} else {
				$amount			=	sprintf( '%.2f', $paymentBasket->mc_gross );
			}

			$ipAddresses		=	cbpaidRequest::getIParray();

			$requestParams		=	array(	'METHOD' => 'DoDirectPayment',
											'PAYMENTACTION' => 'Sale',
											'IPADDRESS' => substr( array_pop( $ipAddresses ), 0, 15 ),
											'CREDITCARDTYPE' => cbIsoUtf_substr( $card['type'], 0, 10 ),
											'ACCT' => substr( preg_replace ( '/[^0-9]+/', '', strval( $card['number'] ) ), 0, 22 ),
											'EXPDATE' => substr( sprintf( '%02d', intval( $card['expmonth'] ) ), 0, 2 ) . substr( strval( intval( $card['expyear'] ) ), 0, 4 ),
											'CVV2' => substr( preg_replace ( '/[^0-9]+/', '', strval( $card['cvv'] ) ), 0, 4 ),
											'EMAIL' => cbIsoUtf_substr( $paymentBasket->payer_email, 0, 127 ),
											'FIRSTNAME' => cbIsoUtf_substr( $card['firstname'], 0, 25 ),
											'LASTNAME' => cbIsoUtf_substr( $card['lastname'], 0, 25 ),
											'STREET' => cbIsoUtf_substr( $paymentBasket->address_street, 0, 100 ),
											'CITY' => cbIsoUtf_substr( $paymentBasket->address_city, 0, 40 ),
											'STATE' => cbIsoUtf_substr( substr( $paymentBasket->address_state, -2 ), 0, 2 ),
											'COUNTRYCODE' => $countries->countryToTwoLetters( $paymentBasket->address_country ),
											'ZIP' => cbIsoUtf_substr( $paymentBasket->address_zip, 0, 20 ),
											'AMT' => $amount,
											'CURRENCYCODE' => $paymentBasket->mc_currency,
											'DESC' => $paymentBasket->item_name,
											'CUSTOM' => $paymentBasket->id,
											'INVNUM' => $paymentBasket->invoice,
											'NOTIFYURL' => $this->getNotifyUrl( $paymentBasket )
										);

			$this->_signRequestParams( $requestParams );

			$results			=	array();
			$response			=	null;
			$status				=	null;
			$error				=	$this->_httpsRequest( str_replace( 'www', 'api-3t', $this->gatewayUrl( 'psp' ) . '/nvp' ), $requestParams, 105, $response, $status, 'post', 'normal' );

			if ( $response ) {
				parse_str( $response, $results );
			}

			if ( $error || ( $status != 200 ) || ( ! $response ) ) {
				$return			=	array(	'level' => 'spurious',
											'errorText' => CBPTXT::T( "Submitted subscription payment didn't return an error but didn't complete." ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ),
											'errorCode' => '8888'
										);

				$logType		=	'B';
			} else {
				if ( cbGetParam( $results, 'ACK' ) == 'Success' ) {
					$return		=	cbGetParam( $results, 'TRANSACTIONID' );

					$logType	=	'P';
				} else {
					$return		=	array(	'level' => 'fatal',
											'errorText' => cbGetParam( $results, 'L_SHORTMESSAGE0' ) . '. ' . CBPTXT::T( 'Please contact site administrator to check error log.' ),
											'errorCode' => cbGetParam( $results, 'L_ERRORCODE0' )
										);

					$logType	=	'V';
				}
			}

			$ipn				=	$this->_logNotification( $logType, $now, $paymentBasket, $card, $requestParams, $response, $results, $return );
		} else {
			$return				=	array(	'level' => 'fatal',
											'errorText' => CBPTXT::T( 'Needed Paypal API username, password and signature not set.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ),
											'errorCode' => '8888'
										);
		}

		return $return;
	}
	/**
	 * Prepares AIM request
	 *
	 * @param  string				$aimRequestType	 : AUTH_CAPTURE, AUTH_ONLY, CAPTURE_ONLY, CREDIT, VOID, PRIOR_AUTH_CAPTURE
	 * @param  array				$card			 : $card['type'], $card['number'], $card['firstname'], $card['lastname'], $card['expmonth'], $card['expyear'], and optionally: $card['address'], $card['zip'], $card['country']
	 * @param  cbpaidPaymentBasket	$paymentBasket	 : WARNING: Using mc_amount3 as price as it's a subscription, instead of mc_gross.
	 * @param  boolean				$authnetSubscription   true if it is a subscription and amount is in mc_amount1 and not in mc_gross
	 * @return mixed				string of XML request 
	 */
	private function _encodeAIMPostRequest( $aimRequestType, $card, &$paymentBasket, $authnetSubscription )
	{
		$authorize_login_id			= $this->ISOtoUtf8( $this->getAccountParam( 'authorize_login_id' ) );
		$authorize_transaction_key	= $this->ISOtoUtf8( $this->getAccountParam( 'authorize_transaction_key' ) );
		$invoiceNum					= $this->ISOtoUtf8( $paymentBasket->invoice ? $paymentBasket->invoice : $paymentBasket->id );
		$refId						= $this->ISOtoUtf8( $paymentBasket->id );
		// $subscriptionName			= $this->_cbp_utf8_substr( $this->ISOtoUtf8( $paymentBasket->item_name ), 0, 20 );
		$subscriptionDescription	= $this->_cbp_utf8_substr( $this->ISOtoUtf8( $paymentBasket->item_name ), 0, 255 );
		
		//TBD Check if really needed ! $subscriptionTiming			= $this->_computeSubscriptionTiming( $paymentBasket, 'noUpfrontFirstCharge', $card );
		if ( $authnetSubscription > 1 ) {
			if ( $paymentBasket->period1 ) {
				$amount				= sprintf( '%.2f', $paymentBasket->mc_amount1 );
			} else {
				$amount				= sprintf( '%.2f', $paymentBasket->mc_amount3 );
			}
		} else {
			$amount					= sprintf( '%.2f', $paymentBasket->mc_gross );
		}
		$cardNumber					= substr( preg_replace ( '/[^0-9]+/', '', strval( $card['number'] ) ), 0, 22 );
		$cardExpirationDateMM_YYYY	= substr( sprintf( '%02d', intval( $card['expmonth'] ) ), 0, 2 ) . '-' . substr( strval( intval( $card['expyear'] ) ), 0, 4 );
		$cardCVV					= substr( preg_replace ( '/[^0-9]+/', '', strval( $card['cvv'] ) ), 0, 4 );
		if ( ! $cardCVV ) {
			$cardCVV				= '';
		}
		$firstName					= $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['firstname'] ), 0, 50 );
		$lastName					= $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['lastname'] ), 0, 50 );

		$email						= $this->_cbp_utf8_substr( $this->ISOtoUtf8( $paymentBasket->payer_email ), 0, 255 );

		$country					= ( isset( $card['country'] ) ? $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['country'] ), 0, 60 ) : null );
		$zip						= ( isset( $card['zip'] ) ? $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['zip'] ), 0, 20 ) : null );
		$address					= ( isset( $card['address'] ) ? $this->_cbp_utf8_substr( $this->ISOtoUtf8( $card['address'] ), 0, 60 ) : null );

		$ipAddressesArray			= cbpaidRequest::getIParray();
		$ipAddress					= substr( array_pop( $ipAddressesArray ), 0, 15 );			// last one is the address we get from our own (trusted) webserver
		switch ( $aimRequestType ) {
			case 'AUTH_CAPTURE':
				$authnet_values	= array
					(
						'x_version'				=> '3.1',
					 	'x_relay_response'		=> 'FALSE',
						'x_delim_data'			=> 'TRUE',
						'x_delim_char'			=> '*',
						'x_encap_char'			=> '|',
						'x_duplicate_window'	=> '28800',		// no reason to pay twice same basket => 8 hours, maximum allowed by authorize.net
						'x_recurring_billing'	=> 'NO',
					
						'x_login'				=> $authorize_login_id,
						'x_tran_key'			=> $authorize_transaction_key,
					
						'x_amount'				=> $amount,
						'x_currency_code'		=> $paymentBasket->mc_currency,
					
						'x_method'				=> 'CC',
					 	'x_card_num'			=> $cardNumber,
						'x_exp_date'			=> $cardExpirationDateMM_YYYY,
						'x_card_code'			=> $cardCVV,
					
						'x_type'				=> $aimRequestType,
					
						'x_first_name'			=> $firstName,
						'x_last_name'			=> $lastName,
						'x_email'				=> $email,
						'x_cust_id'				=> $paymentBasket->user_id,
						'x_customer_ip'			=> $ipAddress,
					/*
						'x_address'				=> '342 N. Main Street #150',
						'x_city'				=> 'Ft. Worth',
						'x_state'				=> 'TX',
						'x_zip'					=> '12345',
					*/
						'x_invoice_num'			=> $invoiceNum,
						'x_description'			=> $subscriptionDescription,
					
						'cb_custom'				=> $refId,
					);
				if ( $country !== null ) {
					$authnet_values['x_country'] =	$country;
				}
				if ( $zip !== null ) {
					$authnet_values['x_zip']	 =	$zip;
				}
				if ( $address !== null ) {
					$authnet_values['x_address'] =	$address;
				}
				break;
		
			default:
				$authnet_values					 =	array();
				break;
		}
		return $authnet_values;
	}
/**
 * Gets an array of IP addresses taking in account the proxys on the way.
 * An array is needed because FORWARDED_FOR can be facked as well.
 *
 * @obsolete since CBSubs 2.1 (but was still used in CBSubs 3.0 by PayPalPro gateway and cbpaidRequest)
 *
 * @return array of IP addresses, first one being host, and last one last proxy (except fackings)
 */
function cbpaidGetIParray()
{
    return cbpaidRequest::getIParray();
}