/**
	 * Triggers autorecurring payments scheduled in cbpaidScheduler
	 *
	 * @return null|string  NULL: no tasks done. String: result or error to forward to admins.
	 */
	public function triggerScheduledAutoRecurringPayment( ) {
		$schedule					=	cbpaidScheduler::getInstance( $this );
		$newAttempt					=	false;
		if ( $schedule->attemptScheduledTask() ) {
			if ( $this->gateway_account ) {
				$payAccount			=	cbpaidControllerPaychoices::getInstance()->getPayAccount( $this->gateway_account ) ;
				if ( $payAccount ) {
					$payClass		=	$payAccount->getPayMean();
					if ( $payClass ) {
						$return		=	null;
						$transientErrorDoReschedule	=	false;
						$success	=	$payClass->processAutoRecurringPayment( $this, $return, $transientErrorDoReschedule );
						if ( $success === true ) {
							$another =	$schedule->attemptScheduledTaskSuccessful();
						} elseif ( $success === false) {
							$another =	$schedule->attemptScheduledTaskFailed( $transientErrorDoReschedule );
						} elseif ( $success === null ) {
							$another =	false;
						} else {
							$another = false;
							trigger_error( sprintf( "Unexpected return value from processAutoRecurringPayment on %s", $payClass->getPayName() ), E_USER_WARNING );
						}
						if ( $another ) {
							$return	.=	' ' . sprintf( CBPTXT::T("The next payment reattempt is scheduled on %s"), $this->scheduler_next_maturity );
						}
					} else {
						$newAttempt	=	$schedule->attemptScheduledTaskFailed( true );
						$return		=	sprintf( CBPTXT::T("Auto-Recurring payment of Basket %s could not be done, because the payment gateway account %s (id %s) class does not have method processAutoRecurringPayment."), $this->id, $this->payment_method, $this->gateway_account );
					}
				} else {
					$newAttempt		=	$schedule->attemptScheduledTaskFailed( true );
					$return			=	sprintf( CBPTXT::T("Auto-Recurring payment of Basket %s could not be done, because the payment gateway account %s (id %s) is not existent and active."), $this->id, $this->payment_method, $this->gateway_account );
				}
			} else {
				$newAttempt			=	$schedule->attemptScheduledTaskFailed( true );
				$return				=	sprintf( CBPTXT::T("Auto-Recurring payment of Basket %s could not be done, because the basket has no payment gateway account set (method: %s)."), $this->id, $this->payment_method );
			}
		} else {
			$return					=	CBPTXT::P("Error in last scheduled taks for this Basket [BASKET_ID] or error in payment scheduling table: Unexpected state (e.g. already executing) or cannot store. This state will be cleared automatically after 3 cron jobs so that a retry can automatically occur.", array( '[BASKET_ID]' => $this->id ) );
		}
		if ( $newAttempt ) {
			$return					.=	' ' . CBPTXT::T("A new auto-recurring payment attempt has been scheduled.");
		}
		return $return;
	}
 /**
  * The user got redirected back from the payment service provider with a success message: let's see how successfull it was
  *
  * @param  cbpaidPaymentBasket  $paymentBasket       New empty object. returning: includes the id of the payment basket of this callback (strictly verified, otherwise untouched)
  * @param  array                $requestdata         Data returned by gateway
  * @param  string               $type                Type of return ('I' for INS, 'R' for PDT, 'P' for DirectLink (first payment) 'A' for Autorecurring payment (DirectLink), '3' for Refund, '4' for Partial Refund )
  * @param  array                $additionalLogData   Additional data arrays to log with IPN
  * @param  cbpaidPayment        $payment             (optional): Needed only for refunds ($type '3' or '4')
  * @return boolean|null                              TRUE if payment is Completed or Pending, FALSE if registration cancelled or ErrorMSG generated, or NULL if payment Denied or Refunded successfully
  */
 private function _returnParamsHandler($paymentBasket, $requestdata, $type, $additionalLogData = null, $payment = null)
 {
     global $_CB_framework, $_GET, $_POST;
     $ret = null;
     $paymentBasketId = (int) cbGetParam($requestdata, 'orderID', 0);
     if ($paymentBasketId) {
         $isDirectLink = in_array($type, array('P', 'A', '3', '4'));
         if ($isDirectLink && $paymentBasket->id && $paymentBasketId == $paymentBasket->id) {
             $exists = true;
         } else {
             $exists = $paymentBasket->load((int) $paymentBasketId);
         }
         if ($exists && ($isDirectLink || cbGetParam($requestdata, $this->_getPagingParamName('id'), 0) == $paymentBasket->shared_secret && !($type == 'R' && $paymentBasket->payment_status == 'Completed'))) {
             /*
             * Parameter		Value
             					orderID			Your order reference
             					amount			Order amount (not multiplied by 100)
             					currency		Currency of the order
             					PM				Payment method
             					ACCEPTANCE		Acceptance code returned by acquirer
             					STATUS			Transaction status
             					CARDNO			Masked card number
             					PAYID			Payment reference in our system
             					NCERROR			Error code
             					NCERRORPLUS		With DirectLink: Error text
             					BRAND			Card brand (our system derives it from the card number) or similar information for other payment methods.
             					ED			*	Expiry date			//TBD * not yet used
             					TRXDATE		*	Transaction date	//TBD * not yet used
             					CN				Cardholder/customer name
             					SHASIGN			SHA signature composed by our system, if SHA-1-OUT configured by you.
             */
             // MM/DD/YY -> YYYY-MM-DD:
             //	$trxdate		=	vsprintf( '20%3$02d-%1$02d-%2$02d', sscanf( cbGetParam( $requestdata, 'TRXDATE' ), '%02d/%02d/%02d' ) );
             // Log the return record:
             $log_type = $type;
             $reason = null;
             $paymentStatus = $this->_paymentStatus($requestdata, $reason, $paymentBasket->payment_status, $paymentBasket);
             $paymentType = $this->_getPaymentType($requestdata);
             $paymentTime = $_CB_framework->now();
             if ($paymentStatus == 'Error') {
                 $errorTypes = array('I' => 'D', 'R' => 'E', 'P' => 'V', 'A' => 'W', '3' => 'V', '4' => 'V');
                 if (isset($errorTypes[$type])) {
                     $log_type = $errorTypes[$type];
                 }
             }
             $ipn = $this->_prepareIpn($log_type, $paymentStatus, $paymentType, $reason, $paymentTime, 'utf-8');
             if ($paymentStatus == 'Refunded') {
                 // in case of refund we need to log the payment as it has same TnxId as first payment: so we need payment_date for discrimination:
                 $ipn->payment_date = gmdate('H:i:s M d, Y T', $paymentTime);
                 // paypal-style
             }
             $ipn->test_ipn = $this->getAccountParam('normal_gateway') == '0' ? 1 : 0;
             $message_type_to_log = array('R' => 'RETURN_TO_SITE', 'I' => 'NOTIFICATION', 'P' => 'DIRECTLINK PAYMENT', 'A' => 'AUTORECURRING DIRECTLINK PAYMENT', '3' => 'DIRECTLINK REFUND', '4' => 'DIRECTLINK PARTIAL REFUND');
             $ipn->raw_data = '$message_type="' . (isset($message_type_to_log[$type]) ? $message_type_to_log[$type] : 'UNKNOWN') . '";' . "\n";
             if ($additionalLogData) {
                 foreach ($additionalLogData as $k => $v) {
                     $ipn->raw_data .= '$' . $k . '="' . var_export($v, true) . '";' . "\n";
                 }
             }
             $ipn->raw_data .= '$requestdata=' . var_export($requestdata, true) . ";\n" . '$_GET=' . var_export($_GET, true) . ";\n" . '$_POST=' . var_export($_POST, true) . ";\n";
             if ($paymentStatus == 'Error') {
                 $paymentBasket->reason_code = $reason;
                 $this->_storeIpnResult($ipn, 'DIRECTLINKERROR:' . $reason);
                 $this->_setLogErrorMSG(4, $ipn, $this->getPayName() . ': ' . $reason, CBPTXT::T("Sorry, the payment server replied with an error.") . ' ' . CBPTXT::T("Please contact site administrator to check payment status and error log."));
                 $ret = false;
             } else {
                 // Transfer fields from basket to IPN:
                 $ipn->bindBasket($paymentBasket);
                 $ipn->address_name = $paymentBasket->first_name . ($paymentBasket->first_name && $paymentBasket->last_name ? ' ' : '') . $paymentBasket->last_name;
                 $basketToIpn = array('address_street', 'address_city', 'address_state', 'address_zip', 'address_country', 'address_country_code', 'payer_business_name', 'payer_email', 'contact_phone', 'vat_number');
                 foreach ($basketToIpn as $k) {
                     $ipn->{$k} = $paymentBasket->{$k};
                 }
                 // Transfer from the INS of gateway to our IPN:
                 $insToIpn = array('mc_currency' => 'currency', 'sale_id' => 'orderID', 'txn_id' => 'PAYID', 'auth_id' => 'ACCEPTANCE', 'residence_country' => 'IPCTY');
                 foreach ($insToIpn as $k => $v) {
                     $ipn->{$k} = cbGetParam($requestdata, $v, null, _CB_NOTRIM);
                 }
                 $ipn->mc_gross = sprintf('%.2f', cbGetParam($requestdata, 'amount'));
                 if ($paymentStatus == 'Refunded' && $ipn->mc_gross > 0) {
                     $ipn->mc_gross = '-' . $ipn->mc_gross;
                     if (in_array($type, array('3', '4'))) {
                         $ipn->computeRefundedTax($payment);
                         $ipn->parent_txn_id = $payment->txn_id;
                     }
                 }
                 // try to guess first and last name:
                 if (isset($requestdata['CN'])) {
                     $cn = iconv('UTF-8', 'UTF-8//IGNORE', cbGetParam($requestdata, 'CN', null, _CB_NOTRIM));
                     $card_names = explode(' ', $cn, 2);
                     if (count($card_names) < 2) {
                         $card_names = array('', $cn);
                     }
                     $ipn->first_name = $card_names[0];
                     $ipn->last_name = $card_names[1];
                 }
                 $ipn->user_id = (int) $paymentBasket->user_id;
                 $recurring = $type == 'A' ? true : false;
                 // Alias Manager option:
                 $alias_manager = $this->getAccountParam('alias_manager');
                 if ($alias_manager) {
                     $aliasInfo['alias'] = cbGetParam($requestdata, 'ALIAS');
                     if ($aliasInfo['alias']) {
                         if (in_array($ipn->payment_status, array('Completed', 'Processed', 'Pending'))) {
                             // Let's see if basket has been allowed to be autorecurring:
                             $enable_processor = 3;
                             // we allow autorecurring but let user choose, as choice is at payment gateway and enforcement is only settable at processor in this processor...
                             $pay1subscribe2 = $this->_getPaySubscribePossibilities($enable_processor, $paymentBasket);
                             if ($pay1subscribe2 & 0x2) {
                                 // autorecurring allowed or mandatory:
                                 $recurring = true;
                             }
                             if (isset($requestdata['CARDNO'])) {
                                 // Memorize alias only if payment has not been denied and that is first alias payment (means with is a masked CARDNO returned):
                                 $aliasInfo['paymenttype'] = $paymentType;
                                 $aliasInfo['paymentmethod'] = stripslashes(cbGetParam($requestdata, 'PM', ''));
                                 $aliasInfo['paymentbrand'] = stripslashes(cbGetParam($requestdata, 'BRAND', ''));
                                 $aliasInfo['cardnumber'] = stripslashes(cbGetParam($requestdata, 'CARDNO'));
                                 // Format CC expiration date if there is one: 1215 --> 2015-12
                                 $ed = stripslashes(cbGetParam($requestdata, 'ED'));
                                 if (preg_match('/\\d{4}/', $ed)) {
                                     $ed = preg_replace('/(\\d{2})(\\d{2})/', '20\\2-\\1', $ed);
                                 }
                                 $aliasInfo['cardexpirationdate'] = $ed;
                                 $aliasInfo['cardname'] = stripslashes(cbGetParam($requestdata, 'CN', null, _CB_NOTRIM));
                                 $aliasInfo['lastpaymentbasket'] = (int) $paymentBasket->id;
                                 $this->setFinancialCoordinatesAlias($paymentBasket->user_id, $aliasInfo);
                             }
                         }
                     }
                 }
                 if ($recurring) {
                     if ($type != 'A') {
                         $ipn->txn_type = 'subscr_signup';
                         $ipn->subscr_id = (int) $paymentBasket->id;
                         $ipn->subscr_date = $ipn->payment_date;
                     } else {
                         if ($paymentStatus == 'Denied') {
                             if ($paymentBasket->reattempts_tried + 1 <= cbpaidScheduler::getInstance($this)->retries) {
                                 $ipn->txn_type = 'subscr_failed';
                             } else {
                                 $ipn->txn_type = 'subscr_cancel';
                             }
                         } elseif (in_array($paymentStatus, array('Completed', 'Processed', 'Pending'))) {
                             $ipn->txn_type = 'subscr_payment';
                         }
                     }
                 } else {
                     $ipn->txn_type = 'web_accept';
                 }
                 // DirectLink Payments and Auto-recurring payments do not have a SHA-OUT signature, other ones do have one that must be checked:
                 if ($isDirectLink || $this->_pspVerifySignature($requestdata)) {
                     if ($paymentBasketId == cbGetParam($requestdata, 'orderID') && (sprintf('%.2f', $paymentBasket->mc_gross) == $ipn->mc_gross || $ipn->payment_status == 'Refunded') && $paymentBasket->mc_currency == $ipn->mc_currency) {
                         if (in_array($ipn->payment_status, array('Completed', 'Processed', 'Pending', 'Refunded', 'Denied'))) {
                             // We need to detect partial refunds by comparing the refunded amount to the initially sent amount:
                             if ($type == '4' || $ipn->payment_status == 'Refunded' && sprintf('%.2f', $paymentBasket->mc_gross) != sprintf('%.2f', -$ipn->mc_gross)) {
                                 $ipn->payment_status = 'Partially-Refunded';
                             }
                             $this->_storeIpnResult($ipn, 'SUCCESS');
                             $this->_bindIpnToBasket($ipn, $paymentBasket);
                             $autorecurring_type = $recurring ? 2 : 0;
                             // 0: not auto-recurring, 1: auto-recurring without payment processor notifications, 2: auto-renewing with processor notifications updating $expiry_date
                             $autorenew_type = $recurring ? 2 : 0;
                             // 0: not auto-renewing (manual renewals), 1: asked for by user, 2: mandatory by configuration
                             if ($recurring) {
                                 $paymentBasket->reattempt = 1;
                                 // we want to reattempt auto-recurring payment in case of failure
                             }
                             $txnIdMultiplePaymentDates = in_array($paymentStatus, array('Refunded', 'Partially-Refunded'));
                             // in case of refund we need to log the payment as it has same TnxId as first payment.
                             $this->updatePaymentStatus($paymentBasket, $ipn->txn_type, $ipn->payment_status, $ipn, 1, $autorecurring_type, $autorenew_type, $txnIdMultiplePaymentDates);
                             if (in_array($ipn->payment_status, array('Completed', 'Processed', 'Pending'))) {
                                 $ret = true;
                                 if ($recurring && $type != 'A') {
                                     // Now we need to schedule the automatic payments to be done by CBSubs using DirectLink, and which will trigger the auto-renewals (if it's not already the autorecurring payment (A)):
                                     $paymentBasket->scheduleAutoRecurringPayments();
                                 }
                             }
                         } else {
                             $this->_storeIpnResult($ipn, 'FAILED');
                             $paymentBasket->payment_status = $ipn->payment_status;
                             $this->_setErrorMSG('<div class="message">' . $this->getTxtNextStep($paymentBasket) . '</div>');
                             $paymentBasket->payment_status = 'RedisplayOriginalBasket';
                             $ret = false;
                         }
                     } elseif ($isDirectLink && in_array($type, array('3', '4'))) {
                         // DirectLink refunds with pending or refused statuses:
                         $resultText = null;
                         $ret = $this->_refundInitiationStatus($requestdata, $resultText);
                         $this->_setErrorMSG($resultText);
                         $ipn->raw_data .= '$REFUNDRESULT="' . addslashes($resultText) . "\";\n";
                         // We are going through a 'Refund-pending' state here, so we can't update the payment yet, and CBSubs does not yet handle this state, as "Pending" is only for payment.
                         // Log this as PDT, but wait for IPN for real refund for updating the basket:
                         $ipn->payment_status = $ret ? $type == '3' ? 'Refunded' : 'Partially-Refunded' : 'Denied';
                         $this->_storeIpnResult($ipn, 'SUCCESS');
                     } else {
                         $this->_storeIpnResult($ipn, 'MISMATCH');
                         $this->_setLogErrorMSG(3, $ipn, $this->getPayName() . ': orderID, amount or currency missmatch.', CBPTXT::T("Sorry, the payment does not match the basket.") . ' ' . CBPTXT::T("Please contact site administrator to check error log."));
                         $ret = false;
                     }
                 } else {
                     $this->_storeIpnResult($ipn, 'SIGNERROR');
                     $this->_setLogErrorMSG(3, $ipn, $this->getPayName() . ': SHA-OUT hash does not match with gateway. Please check SHA-OUT setting.', CBPTXT::T("The SHA-OUT signature is incorrect.") . ' ' . CBPTXT::T("Please contact site administrator to check error log."));
                     $ret = false;
                 }
             }
         }
     } else {
         $errorInfo = ' Parameters:' . var_export($requestdata, true) . "\n";
         if ($additionalLogData) {
             foreach ($additionalLogData as $k => $v) {
                 $errorInfo .= '$' . $k . '="' . var_export($v, true) . '";' . "\n";
             }
         }
         if (isset($requestdata['NCERRORPLUS'])) {
             switch (substr($requestdata['NCERRORPLUS'], 0, 17)) {
                 case 'unknown order/1/i':
                     $errorClearText = sprintf('The IP address of your server received by the payment server %s is not set in the list of authorized IP addresses entered in the IP address field of the "Technical Informations" menu "Data and origin verification" tab in the payment portal.', substr($requestdata['NCERRORPLUS'], 0, 18));
                     break;
                 case 'unknown order/1/s':
                     $errorClearText = 'The SHASign differs from the SHASign calculated using the value entered in the SHA-1-IN Signature field (password/pass phrase) in the "Data and origin verification" tab in the payment portal.';
                     break;
                 case 'unknown order/0/s':
                     $errorClearText = 'The SHASign field in CBSubs gateway setting is empty but an additional string (password/pass phrase) has been entered in the SHA-1-IN Signature field in the "Data and origin verification" tab in the payment portal.';
                     break;
                 default:
                     $errorClearText = $requestdata['NCERRORPLUS'];
                     break;
             }
         } else {
             $errorClearText = 'Probably due to setting not active in: Technical Information: Transaction Feedback tab: "I want to receive transaction feedback parameters on the redirection URLs"';
         }
         $this->_setLogErrorMSG(3, null, $this->getPayName() . ': The OrderId is missing in the return URL: ' . $errorClearText . $errorInfo, CBPTXT::T("Please contact site administrator to check error log."));
     }
     return $ret;
 }
	/**
	 * The user got redirected back from the payment service provider with a success message: let's see how successfull it was
	 *
	 * @param  cbpaidPaymentBasket  $paymentBasket       New empty object. returning: includes the id of the payment basket of this callback (strictly verified, otherwise untouched)
	 * @param  array                $requestdata         Data returned by gateway
	 * @param  string               $type                Type of return ('R' for PDT, 'I' for INS, 'A' for Autorecurring payment (Vault) )
     * @param  array                $additionalLogData   Additional strings to log with IPN
	 * @return string                                    HTML to display if frontend, text to return to gateway if notification, FALSE if registration cancelled and ErrorMSG generated, or NULL if nothing to display
	 */
	private function _returnParamsHandler( $paymentBasket, $requestdata, $type, $additionalLogData = null )
	{
		global $_CB_framework, $_GET, $_POST;

		$ret													=	null;
		$paymentBasketId										=	(int) cbGetParam( $requestdata, 'USER1', null );

		if ( $paymentBasketId ) {
			$exists												=	$paymentBasket->load( (int) $paymentBasketId );

			if ( $exists && ( ( cbGetParam( $requestdata, $this->_getPagingParamName( 'id' ), 0 ) == $paymentBasket->shared_secret ) && ( ! ( ( ( $type == 'R' ) || ( $type == 'I' ) ) && ( $paymentBasket->payment_status == 'Completed' ) ) ) ) ) {
				// Log the return record:
				$log_type										=	$type;
				$reason											=	null;
				$paymentStatus									=	$this->_mapPaymentStatus( $requestdata, $reason );
				$paymentType									=	$this->_getPaymentType( $requestdata );
				$paymentTime									=	$_CB_framework->now();

				if ( $paymentStatus == 'Error' ) {
					$errorTypes									=	array( 'I' => 'D', 'R' => 'E', '3' => 'V', '4' => 'V' );

					if ( isset( $errorTypes[$type] ) ) {
						$log_type								=	$errorTypes[$type];
					}
				}

				$ipn											=&	$this->_prepareIpn( $log_type, $paymentStatus, $paymentType, $reason, $paymentTime, 'utf-8' );

				if ( $paymentStatus == 'Refunded' ) {
					// in case of refund we need to log the payment as it has same TnxId as first payment: so we need payment_date for discrimination:
					$ipn->payment_date							=	date( 'H:i:s M d, Y T', $paymentTime ); // paypal-style
				}

				$ipn->test_ipn									=	( $this->getAccountParam( 'normal_gateway' ) == '0' ? 1 : 0 );
				$ipn->raw_data									=	'$message_type="' . ( $type == 'R' ? 'RETURN_TO_SITE' : ( $type == 'I' ? 'NOTIFICATION' : ( $type == '3' ? 'REFUND' : ( $type == '4' ? 'PARTIAL_REFUND' : 'UNKNOWN' ) ) ) ) . '";' . "\n";

				if ( $additionalLogData ) {
					foreach ( $additionalLogData as $k => $v ) {
						$ipn->raw_data							.=	'$' . $k . '="' . var_export( $v, true ) . '";' . "\n";
					}
				}

				$ipn->raw_data									.=	/* cbGetParam() not needed: we want raw info */ '$requestdata=' . var_export( $requestdata, true ) . ";\n"
																.	/* cbGetParam() not needed: we want raw info */ '$_GET=' . var_export( $_GET, true ) . ";\n"
																.	/* cbGetParam() not needed: we want raw info */ '$_POST=' . var_export( $_POST, true ) . ";\n";

				if ( $paymentStatus == 'Error' ) {
					$paymentBasket->reason_code					=	$reason;

					$this->_storeIpnResult( $ipn, 'ERROR:' . $reason );
					$this->_setLogErrorMSG( 4, $ipn, $this->getPayName() . ': ' . $reason, CBPTXT::T( 'Sorry, the payment server replied with an error.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check payment status and error log.' ) );

					$ret										=	false;
				} else {
					$ipn->bindBasket( $paymentBasket );

					$ipn->sale_id								=	$paymentBasketId;

					$insToIpn									=	array(	'sale_id' => 'PPREF',
																			'txn_id' => 'PNREF',
																			'first_name' => 'FIRSTNAME',
																			'last_name' => 'LASTNAME',
																			'address_street' => 'ADDRESS',
																			'address_zip' => 'ZIP',
																			'address_city' => 'CITY',
																			'address_country' => 'COUNTRY',
																			'address_state' => 'STATE',
																			'payer_email' => 'EMAIL',
																			'contact_phone' => 'PHONE',
																			'payer_id' => 'PAYERID',
																			'auth_id' => 'CORRELATIONID',
																			'tax' => 'TAXAMT',
																			'mc_fee' => 'FEEAMT',
																			'mc_gross' => 'AMT'
																		);

					foreach ( $insToIpn as $k => $v ) {
						$ipn->$k								=	cbGetParam( $requestdata, $v );
					}

					$ipn->mc_currency							=	$paymentBasket->mc_currency;
					$ipn->user_id								=	(int) $paymentBasket->user_id;

					$recurring									=	( ( cbGetParam( $requestdata, 'USER4' ) == 'R' ) && ( cbGetParam( $requestdata, 'TENDER' ) != 'P' ) ? true : false );

					if ( $recurring ) {
						if ( ( $paymentStatus == 'Completed' ) && ( ! $paymentBasket->subscr_id ) ) {
							if ( $this->hasPaypalPayflow() ) {
								list( /*$p3 */, $t3, $start )	=	$this->_paypalPeriodsLimits( explode( ' ', $paymentBasket->period3 ), $paymentTime );

								if ( $paymentBasket->period1 ) {
									list( /*$p2*/, /*$t2*/, $start )	=	$this->_paypalPeriodsLimits( explode( ' ', $paymentBasket->period1 ), $paymentTime );
								}

								$request						=	array(	'PARTNER' => 'PayPal',
																			'VENDOR' => $this->getAccountParam( 'paypal_payflow_vendor' ),
																			'USER' => $this->getAccountParam( 'paypal_payflow_user' ),
																			'PWD' => $this->getAccountParam( 'paypal_payflow_password' ),
																			'TENDER' => cbGetParam( $requestdata, 'TENDER' ),
																			'TRXTYPE' => 'R',
																			'ACTION' => 'A',
																			'PROFILENAME' => $paymentBasket->item_name,
																			'AMT' => sprintf( '%.2f', $paymentBasket->mc_amount3 ),
																			'START' => date( 'mdY', $start ),
																			'TERM' => ( $paymentBasket->recur_times ? $paymentBasket->recur_times : 0 ),
																			'PAYPERIOD' => $t3,
																			'ORIGID' => $ipn->txn_id
																		);

								$formUrl						=	array();

								foreach ( $request as $k => $v ) {
									$formUrl[$k]				=	$k . '=' . $v;
								}

								$formUrl						=	implode( '&', $formUrl );

								$results						=	array();
								$response						=	null;
								$status							=	null;
								$error							=	$this->_httpsRequest( $this->gatewayUrl( 'psp' ), $formUrl, 105, $response, $status, 'post', 'normal' );

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

								if ( $error || ( $status != 200 ) || ( ! $response ) ) {
									$ipn->txn_type				=	'web_accept';

									$this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' HTTPS POST request to payment gateway server failed.', CBPTXT::T( "Submitted refund request didn't return an error but didn't complete." ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) );
								} else {
									if ( cbGetParam( $results, 'RESULT' ) == 0 ) {
										$ipn->txn_type			=	'subscr_signup';
										$ipn->subscr_id			=	cbGetParam( $results, 'PROFILEID' );
										$ipn->subscr_date		=	$ipn->payment_date;
									} else {
										$ipn->txn_type			=	'web_accept';

										$this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' Paypal Payflow error returned. ERROR: ' . cbGetParam( $results, 'RESPMSG' ), CBPTXT::T( 'Please contact site administrator to check error log.' ) );
									}
								}
							} else {
								$ipn->txn_type					=	'web_accept';

								$this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' Needed Paypal Payflow vendor, user and password not set.', CBPTXT::T( 'Needed Paypal Payflow vendor, user and password not set.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) );
							}
						} elseif ( $paymentStatus == 'Denied' ) {
							if ( ( $paymentBasket->reattempts_tried + 1 ) <= cbpaidScheduler::getInstance( $this )->retries ) {
								$ipn->txn_type					=	'subscr_failed';
							} else {
								$ipn->txn_type					=	'subscr_cancel';
							}
						} elseif ( in_array( $paymentStatus, array( 'Completed', 'Processed', 'Pending' ) ) ) {
							$ipn->txn_type						=	'subscr_payment';
						}
					} else {
						$ipn->txn_type							=	'web_accept';
					}

					// validate payment from PDT or IPN
					$valid										=	$this->_validateIPN( $ipn, $paymentBasket );

					if ( $valid === true ) {
						if ( in_array( $ipn->payment_status, array( 'Completed', 'Processed', 'Pending', 'Refunded', 'Denied' ) ) ) {
							$this->_storeIpnResult( $ipn, 'SUCCESS' );

							$autorecurring_type					=	( in_array( $ipn->txn_type, array( 'subscr_payment', 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed' ) ) ? 2 : 0 );
							$autorenew_type						=	( $autorecurring_type ? ( ( ( $this->getAccountParam( 'enabled', 0 ) == 3 ) && ( $paymentBasket->isAnyAutoRecurring() == 2 ) ) ? 1 : 2 ) : 0 );

							if ( $autorecurring_type && ( $ipn->txn_type == 'subscr_signup' ) && ( ( $paymentBasket->period1 ) && ( $paymentBasket->mc_amount1 == 0 ) ) && ( $ipn->payment_status == '' ) ) {
								$ipn->payment_status			=	'Completed';
							}

							if ( ( $ipn->payment_status == 'Refunded' ) && ( $paymentBasket->mc_gross != ( - $ipn->mc_gross ) ) ) {
								$ipn->payment_status			=	'Partially-Refunded';
							}

							$this->_bindIpnToBasket( $ipn, $paymentBasket );

							// add the gateway to the basket:
							$paymentBasket->payment_method		=	$this->getPayName();
							$paymentBasket->gateway_account		=	$this->getAccountParam( 'id' );

							$this->updatePaymentStatus( $paymentBasket, $ipn->txn_type, $ipn->payment_status, $ipn, 1, $autorecurring_type, $autorenew_type, false );

							if ( in_array( $ipn->payment_status, array( 'Completed', 'Processed', 'Pending' ) ) ) {
								$ret							=	true;
							}
						} else {
							$this->_storeIpnResult( $ipn, 'FAILED' );

							$paymentBasket->payment_status		=	$ipn->payment_status;

							$this->_setErrorMSG( '<div class="message">' . $this->getTxtNextStep( $paymentBasket ) . '</div>' );

							$paymentBasket->payment_status		=	'RedisplayOriginalBasket';
							$ret								=	false;
						}
					} else {
						$this->_storeIpnResult( $ipn, 'MISMATCH' );

						$this->_setLogErrorMSG( 3, $ipn, $this->getPayName() . ' Paypal IPN fraud attempt. ERROR: ' . $valid, CBPTXT::T( 'Invalid transaction.' ) . ' ' . CBPTXT::T( 'Please contact site administrator to check error log.' ) );

						$ret									=	false;
					}
				}
			}
		} else {
			$this->_setLogErrorMSG( 3, null, $this->getPayName() . ': USER1 is missing in the return URL: ' . var_export( $_GET, true ), CBPTXT::T( 'Please contact site administrator to check error log.' ) );
		}

		if ( ( $type == 'R' ) && in_array( $this->getAccountParam( 'template_layout', 'MINLAYOUT' ), array( 'MINLAYOUT', 'MOBILE' ) ) ) {
			$js													=	"if ( top != self ) {"
																.		"document.body.style.display = 'none';"
																.		"parent.location = '" . addslashes( ( $ret ? $this->getSuccessUrl( $paymentBasket ) : $this->getCancelUrl( $paymentBasket ) ) ) . "';"
																.	"}";

			echo '<script type="text/javascript">' . $js . '</script>';
		}

		return  $ret;
	}
 /**
  * The user got redirected back from the payment service provider with a success message: let's see how successfull it was
  *
  * @param  cbpaidPaymentBasket  $paymentBasket       New empty object. returning: includes the id of the payment basket of this callback (strictly verified, otherwise untouched)
  * @param  array                $requestdata         Data returned by gateway
  * @param  string               $type                Type of return ('R' for PDT, 'I' for INS, 'A' for Autorecurring payment (Vault) )
  * @param  array                $additionalLogData   Additional strings to log with IPN
  * @return string                                    HTML to display if frontend, text to return to gateway if notification, FALSE if registration cancelled and ErrorMSG generated, or NULL if nothing to display
  */
 private function _returnParamsHandler($paymentBasket, $requestdata, $type, $additionalLogData = null)
 {
     global $_CB_framework, $_GET, $_POST;
     $oid = $requestdata["ordernumber"];
     $qp = new QuickpayApi();
     $qp->setOptions($this->api_key);
     if ($this->getAccountParam('enabled') == 2) {
         $qp->mode = 'subscriptions?order_id=';
     } else {
         $qp->mode = 'payments?order_id=';
     }
     // Commit the status request, checking valid transaction id
     $str = $qp->status($oid);
     $str["operations"][0] = array_reverse($str["operations"][0]);
     $qp_status = $str[0]["operations"][0]["qp_status_code"];
     $qp_type = strtolower($str[0]["type"]);
     $qp_status_msg = $str[0]["operations"][0]["qp_status_msg"];
     $qp_vars = $str[0]["variables"];
     $qp_id = $str[0]["id"];
     $qp_order_id = $str[0]["order_id"];
     $qp_aq_status_code = $str[0]["aq_status_code"];
     $qp_aq_status_msg = $str[0]["aq_status_msg"];
     $qp_cardtype = $str[0]["metadata"]["brand"];
     $qp_cardnumber = "xxxx-xxxxxx-" . $str[0]["metadata"]["last4"];
     $qp_amount = $str[0]["operations"][0]["amount"];
     $qp_currency = $str[0]["currency"];
     $qp_pending = $str[0]["pending"] == "true" ? " - pending " : "";
     $qp_expire = $str[0]["metadata"]["exp_month"] . "-" . $str[0]["metadata"]["exp_year"];
     $ret = null;
     $paymentBasketId = $requestdata["cbpbasket"];
     if ($paymentBasketId) {
         $exists = $paymentBasket->load((int) $paymentBasketId);
         if ($exists && ($requestdata["cbpid"] == $paymentBasket->shared_secret && !(($type == 'R' || $type == 'I') && $paymentBasket->payment_status == 'Completed'))) {
             // PDT doesn't return transacton information; lets request for it:
             /*				if ( $type == 'R' ) {
             					$requestdata = $str;
             
             					$formvars									=	array();
             
             					$formvars['protocol']						=	'3';
             
             					$formvars['msgtype']						=	'status';
             
             					$formvars['merchant']						=	$this->getAccountParam( 'pspid' );
             
             					$formvars['ordernumber']					=	$this->_prepareOrderNumber( $paymentBasket->id, true );
             
             					$formvars['splitpayment']					=	'0';
             
             
             
             					$this->_signRequestParams( $formvars );
             
             
             
             					$response									=	null;
             
             					$status										=	null;
             
             					$error										=	$this->_httpsRequest( $this->_pspApiUrl(), $formvars, 30, $response, $status, 'post', 'normal', '*/
             /*', true, 443, '', '', true, null );
             */
             /*
             					if ( ( ! $error ) && ( $status == 200 ) && $response ) {
             						$xml_response							=	$this->xmlTagValuesToArray( new SimpleXMLElement( $response, LIBXML_NONET | ( defined('LIBXML_COMPACT') ? LIBXML_COMPACT : 0 ) ) );
             						if ( $xml_response ) {
             							$requestdata						=	$xml_response;
             						}
             					}
             			}
             */
             // Log the return record:
             $log_type = $type;
             $reason = null;
             $paymentStatus = $this->_paymentStatus($qp_status, $this->reason);
             $paymentType = $qp_cardtype;
             $paymentTime = $_CB_framework->now();
             if ($paymentStatus == 'Error') {
                 $errorTypes = array('I' => 'D', 'R' => 'E');
                 if (isset($errorTypes[$type])) {
                     $log_type = $errorTypes[$type];
                 }
             }
             $ipn = $this->_prepareIpn($log_type, $paymentStatus, $paymentType, $this->reason, $paymentTime, 'utf-8');
             if ($qp_type == 'refund') {
                 // in case of refund we need to log the payment as it has same TnxId as first payment: so we need payment_date for discrimination:
                 $ipn->payment_date = gmdate('H:i:s M d, Y T', $paymentTime);
                 // paypal-style
             }
             $ipn->test_ipn = 0;
             $ipn->raw_data = '$message_type="' . ($type == 'R' ? 'RETURN_TO_SITE' : ($type == 'I' ? 'NOTIFICATION' : 'UNKNOWN')) . '";' . "\n";
             if ($additionalLogData) {
                 foreach ($additionalLogData as $k => $v) {
                     $ipn->raw_data .= '$' . $k . '="' . var_export($v, true) . '";' . "\n";
                 }
             }
             $ipn->raw_data .= '$requestdata=' . var_export($requestdata, true) . ";\n" . '$_GET=' . var_export($_GET, true) . ";\n" . '$_POST=' . var_export($_POST, true) . ";\n";
             if ($paymentStatus == 'Error') {
                 $paymentBasket->reason_code = $this->reason;
                 $this->_storeIpnResult($ipn, 'ERROR:' . $this->reason);
                 $this->_setLogErrorMSG(4, $ipn, $this->getPayName() . ': ' . $this->reason, CBPTXT::T('Sorry, the payment server replied with an error.') . ' ' . CBPTXT::T('Please contact site administrator to check payment status and error log.'));
                 $ret = false;
             } else {
                 $ipn->bindBasket($paymentBasket);
                 $ipn->sale_id = $paymentBasketId;
                 $insToIpn = array('txn_id' => $qp_id, 'mc_currency' => $qp_currency, 'receiver_email' => $qp_vars["merchant_email"], 'first_name' => $qp_vars['first_name'], 'last_name' => $qp_vars['last_name'], 'address_street' => $qp_vars['address_one'], 'address_zip' => $qp_vars['postal_code'], 'address_city' => $qp_vars['city'], 'address_country' => $qp_vars['country'], 'address_state' => $qp_vars['state_or_province'], 'contact_phone' => $qp_vars['phone'], 'payer_email' => $qp_vars['email']);
                 foreach ($insToIpn as $k => $v) {
                     $ipn->{$k} = $v;
                 }
                 $ipn->mc_gross = sprintf('%.2f', $qp_amount / 100);
                 $ipn->user_id = (int) $paymentBasket->user_id;
                 // check what type of purchase this is:
                 $recurring = in_array($qp_type, array('subscription', 'recurring')) ? true : false;
                 //subscription handling
                 // handle recurring subscriptions properly or default to single payment:
                 if ($recurring) {
                     $qp->mode = "subscriptions/";
                     $addlink = $qp_id . "/recurring/";
                     $process_parameters["amount"] = $qp_amount;
                     $process_parameters["order_id"] = $paymentBasket->item_number;
                     $process_parameters["auto_capture"] = TRUE;
                     $storder = $qp->createorder($qp_order_id, $qp_currency_code, $process_parameters, $addlink);
                     if ($paymentStatus == 'Completed' && !$paymentBasket->subscr_id) {
                         $ipn->txn_type = 'subscr_signup';
                         $ipn->subscr_id = $qp_id;
                         $ipn->subscr_date = $ipn->payment_date;
                     } elseif ($paymentStatus == 'Denied') {
                         if ($paymentBasket->reattempts_tried + 1 <= cbpaidScheduler::getInstance($this)->retries) {
                             $ipn->txn_type = 'subscr_failed';
                         } else {
                             $ipn->txn_type = 'subscr_cancel';
                         }
                     } elseif (in_array($paymentStatus, array('Completed', 'Processed', 'Pending'))) {
                         $ipn->txn_type = 'subscr_payment';
                     }
                 } else {
                     $ipn->txn_type = 'web_accept';
                 }
                 // validate payment from PDT or IPN
                 $apiReplyRaw = null;
                 $apiReplyArray = null;
                 if ($qp_status == 20000) {
                     $ipn->raw_data .= '$apiReplyRaw=\'' . str_replace(array('\\', '\''), array('\\\\', '\\\''), $apiReplyRaw) . "';\n" . '$apiReplyFormattedArray=' . var_export($apiReplyArray, true) . ";\n";
                     if ($paymentBasketId == $requestdata["cbpbasket"] && (sprintf('%.2f', $paymentBasket->mc_gross) == $ipn->mc_gross || $ipn->payment_status == 'Refunded') && $paymentBasket->mc_currency == $ipn->mc_currency) {
                         if (in_array($ipn->payment_status, array('Completed', 'Processed', 'Pending', 'Refunded', 'Denied'))) {
                             $this->_storeIpnResult($ipn, 'SUCCESS');
                             $this->_bindIpnToBasket($ipn, $paymentBasket);
                             // add the gateway to the basket:
                             $paymentBasket->payment_method = $this->getPayName();
                             $paymentBasket->gateway_account = $this->getAccountParam('id');
                             // 0: not auto-recurring, 1: auto-recurring without payment processor notifications, 2: auto-renewing with processor notifications updating $expiry_date:
                             $autorecurring_type = in_array($ipn->txn_type, array('subscr_payment', 'subscr_signup', 'subscr_modify', 'subscr_eot', 'subscr_cancel', 'subscr_failed')) ? 2 : 0;
                             // 0: not auto-renewing (manual renewals), 1: asked for by user, 2: mandatory by configuration:
                             $autorenew_type = $autorecurring_type ? $this->getAccountParam('enabled', 0) == 3 && $paymentBasket->isAnyAutoRecurring() == 2 ? 1 : 2 : 0;
                             if ($recurring) {
                                 $paymentBasket->reattempt = 1;
                                 // we want to reattempt auto-recurring payment in case of failure
                             }
                             $this->updatePaymentStatus($paymentBasket, $ipn->txn_type, $ipn->payment_status, $ipn, 1, $autorecurring_type, $autorenew_type, false);
                             if (in_array($ipn->payment_status, array('Completed', 'Processed', 'Pending'))) {
                                 $ret = true;
                             }
                         } else {
                             $this->_storeIpnResult($ipn, 'FAILED');
                             $paymentBasket->payment_status = $ipn->payment_status;
                             $this->_setErrorMSG('<div class="message">' . $this->getTxtNextStep($paymentBasket) . '</div>');
                             $paymentBasket->payment_status = 'RedisplayOriginalBasket';
                             $ret = false;
                         }
                     } else {
                         $this->_storeIpnResult($ipn, 'MISMATCH');
                         $this->_setLogErrorMSG(3, $ipn, $this->getPayName() . ': amount or currency missmatch', CBPTXT::T('Sorry, the payment does not match the basket.') . ' ' . CBPTXT::T('Please contact site administrator to check error log.'));
                         $ret = false;
                     }
                 } else {
                     $this->_storeIpnResult($ipn, 'SIGNERROR');
                     $this->_setLogErrorMSG(3, $ipn, $this->getPayName() . ': Transaction does not match with gateway. Please check API Key setting', CBPTXT::T('The API Key is incorrect.') . ' ' . CBPTXT::T('Please contact site administrator to check error log.'));
                     $ret = false;
                 }
             }
         }
     } else {
         $this->_setLogErrorMSG(3, null, $this->getPayName() . ': ordernumber is missing in the return URL: ' . var_export($_GET, true), CBPTXT::T('Please contact site administrator to check error log.'));
     }
     return $ret;
 }