|  | <?php | 
						
						
							|  | if(!defined('QUINTACMS') ) die('No Quinta.'); | 
						
						
							|  | 
 | 
						
						
							|  | if (!defined("PAYPALEXPRESSCHECKOUTACTION.CLASS.PHP")){ | 
						
						
							|  | define("PAYPALEXPRESSCHECKOUTACTION.CLASS.PHP",1); | 
						
						
							|  | 
 | 
						
						
							|  | /** | 
						
						
							|  | * Class PayPalExpressCheckoutAction - PayPal  Express Checkout payment action | 
						
						
							|  | * | 
						
						
							|  | * This class provides an implementation of the PayPal Express Checkout System including the | 
						
						
							|  | * two points of entry required. It can be activated either as a selected payment method on the | 
						
						
							|  | * checkout review page or by a click on the "Check out with PayPal" button. | 
						
						
							|  | * | 
						
						
							|  | * Express Checkout consists of 2 phases: | 
						
						
							|  | *   1. A request is sent to the server to set up the transaction details and a "token" is returned. | 
						
						
							|  | *       This token is stored in _SESSION['PayPalToken'] to be used in the second phase | 
						
						
							|  | *        Order status is left at pending and the customer is redirected to PayPal with the token as | 
						
						
							|  | *       part of the redirect URL | 
						
						
							|  | *   2. PayPal returns the customer to the PayPalExpressReturn page - this page must exist in | 
						
						
							|  | *       database and have in it the PayPalExpressReturnModule. This module will use this action | 
						
						
							|  | *       again, calling getExpressCheckoutDetails to complete the transaction. The function will | 
						
						
							|  | *       DoExpressCheckoutPayment | 
						
						
							|  | * | 
						
						
							|  | * Note that this payment action requires the existance of the return and cancel pages to which | 
						
						
							|  | * the customer is returned from PayPal - and that these pages must contain a content block with | 
						
						
							|  | * the PayPalExpressReturnModule to complete the process. Completion will consist of everything | 
						
						
							|  | * normally done in PaymentModule in btnPurchase_Click. | 
						
						
							|  | * | 
						
						
							|  | *@todo | 
						
						
							|  | *   - finish stage two, sending confirmation email on approval and completing the order | 
						
						
							|  | *  | 
						
						
							|  | *@author Erik Winn <sidewalksoftware@gmail.com> | 
						
						
							|  | * | 
						
						
							|  | *@version 0.3 | 
						
						
							|  | * | 
						
						
							|  | *@package Quinta | 
						
						
							|  | * @subpackage Classes | 
						
						
							|  | */ | 
						
						
							|  | 
 | 
						
						
							|  | 	class PayPalExpressCheckoutAction extends PayPalNVPAction{ | 
						
						
							|  | 		/** | 
						
						
							|  | 		* PayPalExpressCheckoutAction Constructor | 
						
						
							|  | 		* | 
						
						
							|  | 		* This sets various defaults specific to the Express Checkout process | 
						
						
							|  | 		* | 
						
						
							|  | 		*@todo | 
						
						
							|  | 		*   - safeguard the return URLs - send session id? or, encode account id and order id .. | 
						
						
							|  | 		* | 
						
						
							|  | 		* @param Order objOrder - the Order to process | 
						
						
							|  | 		*/ | 
						
						
							|  | 		public function __construct(Order $objOrder){ | 
						
						
							|  | 			try { | 
						
						
							|  | 				parent::__construct($objOrder); | 
						
						
							|  | 			} catch (QCallerException $objExc) { | 
						
						
							|  | 				$objExc->IncrementOffset(); | 
						
						
							|  | 				throw $objExc; | 
						
						
							|  | 			} | 
						
						
							|  | 			//the order id may be redundant - we already send INVNUM, this may go away .. | 
						
						
							|  | 			$this->aryRequestValues['RETURNURL'] = 'http://' . Quinta::$ServerName .  __QUINTA_SUBDIRECTORY__  | 
						
						
							|  | 											. '/index.php/PayPalExpressReturn?orderid=' . $this->objOrder->Id; | 
						
						
							|  | 			$this->aryRequestValues['CANCELURL'] = 'http://' . Quinta::$ServerName .  __QUINTA_SUBDIRECTORY__  | 
						
						
							|  | 											. '/index.php/PayPalExpressCancel?orderid=' . $this->objOrder->Id; | 
						
						
							|  | 			 | 
						
						
							|  | 			$this->strRedirectCgiUrl = '/cgi-bin/webscr?'; | 
						
						
							|  | 						  | 
						
						
							|  | 			$this->strTemplateUri = __QUINTA_CORE_VIEWS__ . '/PayPalExpressCheckoutAction.tpl.php'; | 
						
						
							|  | 		} | 
						
						
							|  | 	   /** | 
						
						
							|  | 		* The createRequest functions are handled by get/setExpressCheckoutDetails | 
						
						
							|  | 		* as they occur before and after a customer is redirected to PayPal and are | 
						
						
							|  | 		* therefor two separate requests.  | 
						
						
							|  | 		*/         | 
						
						
							|  | 		protected function createPOSTRequest(){} | 
						
						
							|  | 		protected function createGETRequest(){} | 
						
						
							|  | 		 | 
						
						
							|  | 		/** | 
						
						
							|  | 		* Performs any preparation steps prior to submitting an actual payment. | 
						
						
							|  | 		* We submit a call to the SetExpressCheckoutDetails API here and set up | 
						
						
							|  | 		* the values for the transaction. If successful, aryResponseValues['TOKEN'] | 
						
						
							|  | 		* will contain the identifier for the transaction. | 
						
						
							|  | 		*@return bool true on success   | 
						
						
							|  | 		*/         | 
						
						
							|  | 		public function PreProcess(){             | 
						
						
							|  | 			$this->aryRequestValues['METHOD'] = 'SetExpressCheckout'; | 
						
						
							|  | 			$this->setExpressCheckoutDetails(); | 
						
						
							|  | 			return ! $this->HasErrors; | 
						
						
							|  | 		} | 
						
						
							|  | 		/** | 
						
						
							|  | 		*  | 
						
						
							|  | 		*@return bool true on success | 
						
						
							|  | 		*/         | 
						
						
							|  | 		public function Process(){ | 
						
						
							|  | 			return ! $this->HasErrors; | 
						
						
							|  | 		} | 
						
						
							|  | 		/** | 
						
						
							|  | 		* This function simply checks the return from setExpressCheckoutDetails and then | 
						
						
							|  | 		* redirects the user to PayPal's site to complete the payment | 
						
						
							|  | 		* Updating order_status_history is also performed here. | 
						
						
							|  | 		*@return bool true on success   | 
						
						
							|  | 		*/         | 
						
						
							|  | 		public function PostProcess(){ | 
						
						
							|  | 			$strRedirectTarget = $this->strRedirectDomainName . $this->strRedirectCgiUrl | 
						
						
							|  | 							. 'cmd=_express-checkout&token=' . $this->objPaypalTransaction->PpToken; | 
						
						
							|  | 			Quinta::Redirect($strRedirectTarget ); | 
						
						
							|  | 		} | 
						
						
							|  | 		 | 
						
						
							|  | 		/** | 
						
						
							|  | 		* Creates POST string for the first half of the PayPal transaction, setting the checkout details | 
						
						
							|  | 		* and storing the result in strPOSTRequest. Then a query is sent to NVP API server and the PayPal | 
						
						
							|  | 		* "token" is recieved which will be used by getExpressCheckoutDetails to validate the payment. | 
						
						
							|  | 		*/         | 
						
						
							|  | 		protected function setExpressCheckoutDetails(){ | 
						
						
							|  | 			$this->aryRequestValues['METHOD'] = 'SetExpressCheckout'; | 
						
						
							|  | 
 | 
						
						
							|  | 			//optionally send a shipping address to display .. | 
						
						
							|  | 			if( $this->ShowShippingAddress ) | 
						
						
							|  | 				$this->initShippingDetails(); | 
						
						
							|  | 			else | 
						
						
							|  | 				$this->aryRequestValues['NOSHIPPING'] = 1; | 
						
						
							|  | 
 | 
						
						
							|  | 			$this->aryRequestValues['AMT'] = $this->fltTotalPrice; | 
						
						
							|  | 			$this->aryRequestValues['ITEMAMT'] = $this->objOrder->ProductTotalCharged; | 
						
						
							|  | 			$this->aryRequestValues['INVNUM'] = $this->objOrder->Id; | 
						
						
							|  | 			$this->aryRequestValues['SHIPPINGAMT'] = $this->objOrder->ShippingCharged; | 
						
						
							|  | 			$this->aryRequestValues['HANDLINGAMT'] = $this->objOrder->HandlingCharged; | 
						
						
							|  | 			$this->aryRequestValues['TAXAMT'] = $this->objOrder->Tax; | 
						
						
							|  | 			//truncated to fit PP specs in case of long store names .. | 
						
						
							|  | 			$this->aryRequestValues['DESC'] = urlencode(substr(STORE_NAME . ' Order ' . $this->objOrder->Id, 0, 127)); | 
						
						
							|  | 			 | 
						
						
							|  | 			foreach( $this->aryRequestValues as $strName => $strValue ) | 
						
						
							|  | 				if('' != $strValue ) | 
						
						
							|  | 					$this->strPOSTRequest .= '&' . $strName . '=' . $strValue; | 
						
						
							|  | 			$this->submitRequest(); | 
						
						
							|  | 			 | 
						
						
							|  | 			$this->handleResponse(); | 
						
						
							|  | /*            if( ! $this->submitRequest()) | 
						
						
							|  | 				throw new Exception('PayPal EC submit failed: ' . $this->strPOSTRequest); | 
						
						
							|  | 			 | 
						
						
							|  | 			if( ! $this->handleResponse()) | 
						
						
							|  | 				throw new Exception('PayPal EC submit failed: ' . $this->strErrors | 
						
						
							|  | 												 . ' <br /><br /> Response: ' . $this->strResponse);*/ | 
						
						
							|  | 		} | 
						
						
							|  | 		/** | 
						
						
							|  | 		* This function is called by PayPalExpressReturnModule when the buyer returns from PayPal. | 
						
						
							|  | 		*Here we determine the payer id and status in preparation for completing the order .. | 
						
						
							|  | 		*/ | 
						
						
							|  | 		public function getExpressCheckoutDetails(){ | 
						
						
							|  | 			$this->strPOSTRequest = ''; | 
						
						
							|  | 			$this->aryRequestValues['METHOD'] = 'GetExpressCheckoutDetails'; | 
						
						
							|  | 			$this->aryRequestValues['TOKEN'] = $_GET['token']; | 
						
						
							|  | 			// also adds default settings .. | 
						
						
							|  | 			foreach( $this->aryRequestValues as $strName => $strValue ) | 
						
						
							|  | 				if('' != $strValue ) | 
						
						
							|  | 					$this->strPOSTRequest .= '&' . $strName . '=' . $strValue; | 
						
						
							|  | 
 | 
						
						
							|  | 			$this->submitRequest(); | 
						
						
							|  | 			$this->handleResponse(); | 
						
						
							|  | 			if( $this->HasErrors ) | 
						
						
							|  | 				return false; | 
						
						
							|  | 			if( '' == $this->objPaypalTransaction->PayerId ){ | 
						
						
							|  | 				$this->HasErrors = true; | 
						
						
							|  | 				$this->objPaypalTransaction->Messages .= '| Quinta PP Express: No PayerId returned! |'; | 
						
						
							|  | 				$this->objPaypalTransaction->Save(); | 
						
						
							|  | 				return false; | 
						
						
							|  | 			} | 
						
						
							|  | 			return true; | 
						
						
							|  | 		} | 
						
						
							|  | 		/** | 
						
						
							|  | 		* This function overrides the PaymentActionBase completion - it is called by PayPalExpressReturnModule | 
						
						
							|  | 		* when the buyer returns from PayPal. | 
						
						
							|  | 		*@todo handle PENDINGREASON, REASONCODE, PAYMENTTYPE != INSTANT .. | 
						
						
							|  | 		*/ | 
						
						
							|  | 		public function doExpressCheckoutPayment(){ | 
						
						
							|  | 			$this->strPOSTRequest = ''; | 
						
						
							|  | 			$this->aryRequestValues['METHOD'] = 'DoExpressCheckoutPayment'; | 
						
						
							|  | 			$this->aryRequestValues['TOKEN'] = $this->objPaypalTransaction->PpToken; | 
						
						
							|  | 			$this->aryRequestValues['PAYERID'] = $this->objPaypalTransaction->PayerId; | 
						
						
							|  | 			$this->aryRequestValues['INVNUM'] = $this->objOrder->Id; | 
						
						
							|  | 			$this->aryRequestValues['AMT'] = $this->fltTotalPrice; | 
						
						
							|  | 			$this->aryRequestValues['ITEMAMT'] = $this->objOrder->ProductTotalCharged; | 
						
						
							|  | 			$this->aryRequestValues['SHIPPINGAMT'] = $this->objOrder->ShippingCharged; | 
						
						
							|  | 			$this->aryRequestValues['HANDLINGAMT'] = $this->objOrder->HandlingCharged; | 
						
						
							|  | 			$this->aryRequestValues['TAXAMT'] = $this->objOrder->Tax; | 
						
						
							|  | 			// also adds default settings .. | 
						
						
							|  | 			foreach( $this->aryRequestValues as $strName => $strValue ) | 
						
						
							|  | 				if('' != $strValue ) | 
						
						
							|  | 					$this->strPOSTRequest .= '&' . $strName . '=' . $strValue;			 | 
						
						
							|  | 			$this->submitRequest(); | 
						
						
							|  | 			$this->handleResponse(); | 
						
						
							|  | 			if( $this->HasErrors ) | 
						
						
							|  | 				return false; | 
						
						
							|  | 				 | 
						
						
							|  | 			// check for the payment status | 
						
						
							|  | 			$strStatus = $this->objPaypalTransaction->PaymentStatus; | 
						
						
							|  | 			if('' == $strStatus ){ | 
						
						
							|  | 				$this->HasErrors = true; | 
						
						
							|  | 				$this->objPaypalTransaction->Messages .= '| Quinta PP Express: No Payment status returned! |'; | 
						
						
							|  | 				$this->objPaypalTransaction->Save(); | 
						
						
							|  | 				return false; | 
						
						
							|  | 			} | 
						
						
							|  | 
 | 
						
						
							|  | ///@todo handle PENDINGREASON, REASONCODE, PAYMENTTYPE != INSTANT .. | 
						
						
							|  | 			switch( strtoupper($strStatus) ){ | 
						
						
							|  | 				case 'COMPLETED': | 
						
						
							|  | 					$this->blnApproved = true; | 
						
						
							|  | 					break; | 
						
						
							|  | 				case 'PENDING': | 
						
						
							|  | 				default: | 
						
						
							|  | 					$this->blnApproved = false;                     | 
						
						
							|  | 			} | 
						
						
							|  | 		} | 
						
						
							|  | 		 | 
						
						
							|  | 		protected function initShippingDetails(){ | 
						
						
							|  | 			$this->aryRequestValues['NOSHIPPING'] = 0; | 
						
						
							|  | 			$this->aryRequestValues['ADDRESSOVERRIDE'] = 1; | 
						
						
							|  | 			 | 
						
						
							|  | 			//We must concatenate all the name fields into NAME .. | 
						
						
							|  | 			if( '' != $this->objOrder->ShippingNamePrefix ) | 
						
						
							|  | 				$this->aryRequestValues['SHIPTONAME'] = urlencode($this->objOrder->ShippingNamePrefix . ' '); | 
						
						
							|  | 			$this->aryRequestValues['SHIPTONAME'] .= urlencode($this->objOrder->ShippingFirstName); | 
						
						
							|  | 
 | 
						
						
							|  | 			if( '' != $this->objOrder->ShippingMiddleName ) | 
						
						
							|  | 				$this->aryRequestValues['SHIPTONAME'] .= urlencode(' ' . $this->objOrder->ShippingMiddleName); | 
						
						
							|  | 			$this->aryRequestValues['SHIPTONAME'] .= urlencode(' ' . $this->objOrder->ShippingLastName); | 
						
						
							|  | 
 | 
						
						
							|  | 			if( '' != $this->objOrder->ShippingNameSuffix ) | 
						
						
							|  | 				$this->aryRequestValues['SHIPTONAME'] .= urlencode(' ' . $this->objOrder->ShippingNameSuffix); | 
						
						
							|  | 
 | 
						
						
							|  | 			$this->aryRequestValues['SHIPTOSTREET'] = urlencode($this->objOrder->ShippingStreet1); | 
						
						
							|  | 
 | 
						
						
							|  | 			if( '' != $this->objOrder->ShippingStreet2 ) | 
						
						
							|  | 				$this->aryRequestValues['SHIPTOSTREET2'] = urlencode($this->objOrder->ShippingStreet2); | 
						
						
							|  | 			//PayPal offers no field for County or Suburb so we must put them in street2 .. | 
						
						
							|  | 			if( '' != $this->objOrder->ShippingSuburb ) | 
						
						
							|  | 				$this->aryRequestValues['SHIPTOSTREET2'] .= urlencode(', ' . $this->objOrder->ShippingSuburb); | 
						
						
							|  | 			if( '' != $this->objOrder->ShippingCounty ) | 
						
						
							|  | 				$this->aryRequestValues['SHIPTOSTREET2'] .= urlencode(', ' . $this->objOrder->ShippingCounty); | 
						
						
							|  | 
 | 
						
						
							|  | 			$this->aryRequestValues['SHIPTOCITY'] = urlencode($this->objOrder->ShippingCity); | 
						
						
							|  | 			$this->aryRequestValues['SHIPTOSTATE'] = urlencode($this->objOrder->ShippingState); | 
						
						
							|  | 			$this->aryRequestValues['SHIPTOCOUNTRY'] = urlencode($this->objOrder->ShippingCountry); | 
						
						
							|  | 			$this->aryRequestValues['SHIPTOZIP'] = urlencode($this->objOrder->ShippingPostalCode); | 
						
						
							|  | 		} | 
						
						
							|  | 		/** | 
						
						
							|  | 		* Creates GET query string for the second half of the PayPal transaction. This function is called | 
						
						
							|  | 		* by the PayPalExpressReturnModule only, which is effectively the return URL given to PayPal | 
						
						
							|  | 		* in the first half of the transaction to which the user is redirected after paying. The "token" | 
						
						
							|  | 		*/ | 
						
						
							|  | 		 | 
						
						
							|  | 		public function __get($strName){ | 
						
						
							|  | 			switch ($strName){ | 
						
						
							|  | 				case 'ShowShippingAddress': | 
						
						
							|  | 					return $this->blnShowShippingAddress; | 
						
						
							|  | 				default: | 
						
						
							|  | 					 try { | 
						
						
							|  | 						return parent::__get($strName); | 
						
						
							|  | 					 } catch (QCallerException $objExc) { | 
						
						
							|  | 						$objExc->IncrementOffset(); | 
						
						
							|  | 						throw $objExc; | 
						
						
							|  | 					 } | 
						
						
							|  | 			} | 
						
						
							|  | 		} | 
						
						
							|  | 		 | 
						
						
							|  | 		public function __set($strName, $mixValue){ | 
						
						
							|  | 			switch ($strName){ | 
						
						
							|  | 				case 'ShowShippingAddress': | 
						
						
							|  | 					try { | 
						
						
							|  | 						$this->blnShowShippingAddress = QType::Cast($mixValue, QType::Boolean ); | 
						
						
							|  | 					} catch (QInvalidCastException $objExc) { | 
						
						
							|  | 						$objExc->IncrementOffset(); | 
						
						
							|  | 						throw $objExc; | 
						
						
							|  | 					} | 
						
						
							|  | 					//careful, its backwards .. | 
						
						
							|  | 					$this->aryRequestValues['NOSHIPPING'] = (true === $mixValue) ? 0 : 1; | 
						
						
							|  | 				default: | 
						
						
							|  | 					try { | 
						
						
							|  | 						return (parent::__set($strName, $mixValue)); | 
						
						
							|  | 					} catch (QCallerException $objExc) { | 
						
						
							|  | 						$objExc->IncrementOffset(); | 
						
						
							|  | 						throw $objExc; | 
						
						
							|  | 					} | 
						
						
							|  | 			} | 
						
						
							|  | 		} | 
						
						
							|  | 	 | 
						
						
							|  | 	}//end class | 
						
						
							|  | }//end define | 
						
						
							|  |  | 
						
						
							|  | ?>
 |