|  | <?php | 
						
						
							|  | if(!defined('QUINTACMS') ) die('No Quinta.'); | 
						
						
							|  | 
 | 
						
						
							|  | if (!defined("PAYPALNVPACTION.CLASS.PHP")){ | 
						
						
							|  | define("PAYPALNVPACTION.CLASS.PHP",1); | 
						
						
							|  | 
 | 
						
						
							|  | /** | 
						
						
							|  | * Class PayPalNVPAction - PayPal  NVP API  action | 
						
						
							|  | * | 
						
						
							|  | * This class provides an interface to the PayPal NVP API. | 
						
						
							|  | * | 
						
						
							|  | * @todo - meaningful comments .. we do a bunch of stuff here: | 
						
						
							|  | *  connect, redirect, handle returns, save transaction ..etc. | 
						
						
							|  | *    | 
						
						
							|  | *  | 
						
						
							|  | *@author Erik Winn <sidewalksoftware@gmail.com> | 
						
						
							|  | * | 
						
						
							|  | *@version 0.3 | 
						
						
							|  | * | 
						
						
							|  | *@package Quinta | 
						
						
							|  | * @subpackage Classes | 
						
						
							|  | */ | 
						
						
							|  | 
 | 
						
						
							|  | 	class PayPalNVPAction extends PaymentActionBase{ | 
						
						
							|  | 		/** | 
						
						
							|  | 		* @var array Name - Value pairs with which to construct GET query strings | 
						
						
							|  | 		*/ | 
						
						
							|  | 		protected $aryRequestValues = array( | 
						
						
							|  | 		'USER' => '', | 
						
						
							|  | 		'PWD' => '', | 
						
						
							|  | 		'PAYERID' => '', | 
						
						
							|  | 		'VERSION' => PAYPAL_NVP_VERSION, | 
						
						
							|  | 		'PAYMENTACTION' => 'Sale', //Sale | Autorization | Order | 
						
						
							|  | 		'METHOD' => '', //API method - REQUIRED | 
						
						
							|  | 		'TOKEN' => '',     //transaction token, returned in response - OPTIONAL/required with get/doExpressCheckout .. | 
						
						
							|  | 		'AMT' => '',           // total purchase amount, including tax and shipping - REQUIRED | 
						
						
							|  | 		'CURRENCYCODE' => 'USD', //AUD, CAD, CHF, CZK, DKK, EUR, GBP, HKD, HUF, JPY, NOK, NZD, PLN, SEK, SGD | 
						
						
							|  | 		'RETURNURL' => '', //URL to which customer is returned after paying - REQUIRED | 
						
						
							|  | 		'CANCELURL' => '', //URL to which customer is returned if they cancel - REQUIRED | 
						
						
							|  | 		'NOTIFYURL' => '', //URL for receiving Instant Payment Notification - optional | 
						
						
							|  | 		'IPADDRESS' => '', // Local ServerName  | 
						
						
							|  | 		'MAXAMT' => '', // The expected maximum total amount of the complete order - OPTIONAL | 
						
						
							|  | 		'DESC' => '', // Description of purchase - OPTIONAL | 
						
						
							|  | 		'CUSTOM' => '', // Custom data, whatever you like, returned by GetExpressCheckoutDetails - OPTIONAL | 
						
						
							|  | 		'INVNUM' => '', // Invoice or Order number returned by DoExpressCheckoutPayment - OPTIONAL | 
						
						
							|  | 		'REQCONFIRMSHIPPING' => '', //Require PayPal to confirm customers address (filed at PayPal) - OPTIONAL | 
						
						
							|  | 		'NOSHIPPING' => '', //If true (1), PayPal is to display no shipping address info - OPTIONAL | 
						
						
							|  | 		'ALLOWNOTE' => '', //If true (1) user can add a note returned by GetExpressCheckoutDetails - OPTIONAL | 
						
						
							|  | 		'ADDRESSOVERRIDE' => '', //If true (1), PayPal displays address sent with request - OPTIONAL | 
						
						
							|  | 		'LOCALECODE' => '', //Display PayPal pages using this locale (AU, FR, DE, GB, IT, ES, US) -  OPTIONAL | 
						
						
							|  | 		'PAGESTYLE' => '', //page style from the Profile subtab of the My Account tab - OPTIONAL | 
						
						
							|  | 		'HDRIMG' => '', // (https) URL for the image to appear at the top left of the payment page. - OPTIONAL | 
						
						
							|  | 		'HDRBORDERCOLOR' => '', //Sets the border color around the header of the payment page.- OPTIONAL | 
						
						
							|  | 		'HDRBACKCOLOR' => '', // Sets the background color for the header of the payment page - OPTIONAL | 
						
						
							|  | 		'PAYFLOWCOLOR' => '', // Sets the background color for the payment page - OPTIONAL | 
						
						
							|  | 		'EMAIL' => '', // Email address of the buyer to prefill field at PayPal - OPTIONAL | 
						
						
							|  | 		'LANDINGPAGE' => '', //Type of PayPal page to display ("Billing" for non-PayPal account else "Login") - OPTIONAL | 
						
						
							|  | 		'SHIPTONAME' => '', // (Customer name associated with shipping address - REQUIRED | 
						
						
							|  | 		'SHIPTOSTREET' => '', //First street address - REQUIRED | 
						
						
							|  | 		'SHIPTOSTREET2' => '', //Second street address - OPTIONAL | 
						
						
							|  | 		'SHIPTOCITY' => '', // Name of city. -REQUIRED | 
						
						
							|  | 		'SHIPTOSTATE' => '', // Name of state. -REQUIRED | 
						
						
							|  | 		'SHIPTOZIP' => '', // Postal code. -REQUIRED | 
						
						
							|  | 		'SHIPTOCOUNTRY' => '', // Country. -REQUIRED | 
						
						
							|  | 		'ITEMAMT' => '', //Sum of cost of all items in this order, REQUIRED if you use L_AMTn or shipping etc. ... | 
						
						
							|  | 		'SHIPPINGAMT' => '', //Total shipping costs for this order - optional. | 
						
						
							|  | 		'HANDLINGAMT' => '', //Total handling costs for this order - optional | 
						
						
							|  | 		'TAXAMT' => '', //Sum of tax for all items in this order - optional | 
						
						
							|  | 		); | 
						
						
							|  | 
 | 
						
						
							|  | 		/** | 
						
						
							|  | 		*  | 
						
						
							|  | 		* @var array Response values will be stored here  | 
						
						
							|  | 		*/ | 
						
						
							|  | 		protected $aryResponseValues; | 
						
						
							|  | 		/** | 
						
						
							|  | 		* PayPal transaction ORM object for logging transactions .. | 
						
						
							|  | 		* @var PaypalTransaction - represents the paypal_transaction table .. | 
						
						
							|  | 		*/ | 
						
						
							|  | 		protected $objPaypalTransaction; | 
						
						
							|  | 		 | 
						
						
							|  | 		/** | 
						
						
							|  | 		* The following are base strings for constructing multiple item requests, eg. L_NAME0, L_NAME1, etc... | 
						
						
							|  | 		* All are optional. | 
						
						
							|  | 		*/ | 
						
						
							|  | 		protected $strItemName = 'L_NAME'; | 
						
						
							|  | 		protected $strItemDescBase = 'L_DESC'; | 
						
						
							|  | 		protected $strItemAmountBase = 'L_AMT'; | 
						
						
							|  | 		protected $strItemQuantityBase = 'L_QTY'; | 
						
						
							|  | 		protected $strItemNumberBase = 'L_NUMBER'; | 
						
						
							|  | 		protected $strItemTaxBase = 'L_TAXAMT'; | 
						
						
							|  | 
 | 
						
						
							|  | 		protected $blnShowShippingAddress = false; | 
						
						
							|  | 		 | 
						
						
							|  | 		/** | 
						
						
							|  | 		* PayPalNVPAction Constructor | 
						
						
							|  | 		* | 
						
						
							|  | 		* This sets various defaults specific to the NVP API | 
						
						
							|  | 		* | 
						
						
							|  | 		* @param Order objOrder - the Order to process | 
						
						
							|  | 		*/ | 
						
						
							|  | 		public function __construct(Order $objOrder){ | 
						
						
							|  | 			try { | 
						
						
							|  | 				parent::__construct($objOrder); | 
						
						
							|  | 			} catch (QCallerException $objExc) { | 
						
						
							|  | 				$objExc->IncrementOffset(); | 
						
						
							|  | 				throw $objExc; | 
						
						
							|  | 			} | 
						
						
							|  | 			 | 
						
						
							|  | 			$this->blnTestMode = $objOrder->PaymentMethod->TestMode; | 
						
						
							|  | 			if($this->TestMode){ | 
						
						
							|  | 				$this->strRedirectDomainName = PAYPAL_REDIRECT_TESTURL; | 
						
						
							|  | 				$this->aryRequestValues['USER'] = PAYPAL_NVP_TESTUSERNAME; | 
						
						
							|  | 				$this->aryRequestValues['PWD'] = PAYPAL_NVP_TESTPASSWORD; | 
						
						
							|  | 				if('' != PAYPAL_NVP_TESTSIGNATURE){ | 
						
						
							|  | 					$this->aryRequestValues['SIGNATURE'] = PAYPAL_NVP_TESTSIGNATURE; | 
						
						
							|  | 					$this->strRemoteDomainName = PAYPAL_NVP_TESTURL; | 
						
						
							|  | 				}else{ | 
						
						
							|  | 					$this->UseCurl = true; | 
						
						
							|  | 					$this->UseSslCertificate = true; | 
						
						
							|  | 					$this->strRemoteDomainName = PAYPAL_NVP_CURL_TESTURL; | 
						
						
							|  | 					$this->strSslCertificateUri = PAYPAL_CERT_TESTPATH; | 
						
						
							|  | 				} | 
						
						
							|  | 			}else{ | 
						
						
							|  | 				/// FIXME: put these somewhere safer and load it .. currently in config file! | 
						
						
							|  | 				$this->strRedirectDomainName = PAYPAL_REDIRECT_URL; | 
						
						
							|  | 				$this->aryRequestValues['USER'] = PAYPAL_NVP_USERNAME; | 
						
						
							|  | 				$this->aryRequestValues['PWD'] = PAYPAL_NVP_PASSWORD; | 
						
						
							|  | 				if('' != PAYPAL_NVP_TESTSIGNATURE){ | 
						
						
							|  | 					$this->aryRequestValues['SIGNATURE'] = PAYPAL_NVP_SIGNATURE; | 
						
						
							|  | 					$this->strRemoteDomainName = PAYPAL_NVP_URL; | 
						
						
							|  | 				}else{ | 
						
						
							|  | 					$this->UseCurl = true; | 
						
						
							|  | 					$this->UseSslCertificate = true; | 
						
						
							|  | 					$this->strRemoteDomainName = PAYPAL_NVP_CURL_URL; | 
						
						
							|  | 					$this->strSslCertificateUri = PAYPAL_CERT_PATH; | 
						
						
							|  | 				} | 
						
						
							|  | 			} | 
						
						
							|  | 			 | 
						
						
							|  | 			$this->strRemoteCgiUrl = '/nvp'; | 
						
						
							|  | 			$this->strRequestType = 'POST'; | 
						
						
							|  | 
 | 
						
						
							|  | 			$this->aryRequestValues['IPADDRESS'] = Quinta::$ServerName; | 
						
						
							|  | 			//unused .. | 
						
						
							|  | 			$this->strTemplateUri = __QUINTA_CORE_VIEWS__ . '/PayPalNVPAction.tpl.php'; | 
						
						
							|  | 		} | 
						
						
							|  | 				 | 
						
						
							|  | 	   /** | 
						
						
							|  | 		* The createRequest functions are handled by separate functions which create requests | 
						
						
							|  | 		* by stage as they may occur before and after a customer is redirected to PayPal and may | 
						
						
							|  | 		* therefor be two separate requests.  | 
						
						
							|  | 		*/         | 
						
						
							|  | 		protected function createPOSTRequest(){} | 
						
						
							|  | 		protected function createGETRequest(){} | 
						
						
							|  | 		 | 
						
						
							|  | 		/** | 
						
						
							|  | 		* Performs any preparation steps prior to submitting an actual payment. | 
						
						
							|  | 		* Eg. 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(){} | 
						
						
							|  | 		/** | 
						
						
							|  | 		*@return bool true on success | 
						
						
							|  | 		*/         | 
						
						
							|  | 		public function Process(){} | 
						
						
							|  | 		/** | 
						
						
							|  | 		* Performs any steps necessary after submitting an actual payment. For example | 
						
						
							|  | 		* the PayPal Express Checkout redirects the user here .. | 
						
						
							|  | 		*@return bool true on success   | 
						
						
							|  | 		*/         | 
						
						
							|  | 		public function PostProcess(){} | 
						
						
							|  | 		/** | 
						
						
							|  | 		* Parses the direct API response from PayPal into an array of values. This also inserts an entry | 
						
						
							|  | 		* into the paypal_transaction table and initializes the PaypalTransaction object to which other | 
						
						
							|  | 		* functions may refer for information returned concerning the transaction. | 
						
						
							|  | 		* | 
						
						
							|  | 		*@todo | 
						
						
							|  | 		*   - handle errors gracefully !! | 
						
						
							|  | 		*   - L_ERRORCODE0=81100&L_SHORTMESSAGE0=Missing%20Parameter&L_LONGMESSAGE0 | 
						
						
							|  | 		*       errornumber: 10415 - transaction already completed for token | 
						
						
							|  | 		*   | 
						
						
							|  | 		*   - optionally save address values from PP and use address confirmation ... this ain't gonna be | 
						
						
							|  | 		*     soon since it requires a whole reworking of the scheme to put shipping options after redirect | 
						
						
							|  | 		*     and adding the shipping charge.. ick, i have pp slime on my keyboard .. | 
						
						
							|  | 		*@return boolean true if the response was successfully parsed. | 
						
						
							|  | 		*/         | 
						
						
							|  | 		protected function handleResponse(){ | 
						
						
							|  | 			$this->aryResponseValues = array(); | 
						
						
							|  | 			$strResponseRaw = $this->strResponse; | 
						
						
							|  | 			 | 
						
						
							|  | 			$this->strResponse = urldecode($this->strResponse); | 
						
						
							|  | 			$pos = strpos($this->strResponse, "TOKEN=" ); | 
						
						
							|  | 			if( false === $pos ){ | 
						
						
							|  | 				$this->HasErrors = true; | 
						
						
							|  | 				return false; | 
						
						
							|  | 			} | 
						
						
							|  | 			$this->strResponse = substr( $this->strResponse, $pos); | 
						
						
							|  | 			 | 
						
						
							|  | 			//split up the string and store the values in a map .. | 
						
						
							|  | 			$aryTokens = explode('&', $this->strResponse ); | 
						
						
							|  | 			foreach($aryTokens as $strToken){ | 
						
						
							|  | 				$aryTemp = explode('=', $strToken); | 
						
						
							|  | 				$this->aryResponseValues[$aryTemp[0]] = $aryTemp[1]; | 
						
						
							|  | 			}                 | 
						
						
							|  | 			if( empty($this->aryResponseValues) ){ | 
						
						
							|  | 				$this->HasErrors = true; | 
						
						
							|  | 				$this->strErrors .= 'Response: ' . $strResponseRaw; | 
						
						
							|  | 				return false; | 
						
						
							|  | 			} | 
						
						
							|  | 			 | 
						
						
							|  | 			//initialize a transaction logging object .. | 
						
						
							|  | 			$this->objPaypalTransaction = new PaypalTransaction(); | 
						
						
							|  | 			$this->objPaypalTransaction->OrderId = $this->objOrder->Id; | 
						
						
							|  | 			$this->objPaypalTransaction->PaymentMethodId = $this->objOrder->PaymentMethodId; | 
						
						
							|  | 			$this->objPaypalTransaction->ApiAction = $this->aryRequestValues['METHOD']; | 
						
						
							|  | 			$this->objPaypalTransaction->ApiVersion = $this->aryResponseValues['VERSION']; | 
						
						
							|  | 			$this->objPaypalTransaction->CorrelationId = $this->aryResponseValues['CORRELATIONID']; | 
						
						
							|  | 			$this->objPaypalTransaction->AckReturned = $this->aryResponseValues['ACK']; | 
						
						
							|  | 			//clean up the timestamp .. note: the settor converts this to a QDateTime  | 
						
						
							|  | 			$strDateTime = str_replace('T',' ', $this->aryResponseValues['TIMESTAMP']); | 
						
						
							|  | 			$this->objPaypalTransaction->TimeStamp = $strDateTime; | 
						
						
							|  | 			 | 
						
						
							|  | 			$this->checkAckReturned(); | 
						
						
							|  | 			 | 
						
						
							|  | 			if($this->HasErrors){ | 
						
						
							|  | 				foreach($this->aryResponseValues as $strName => $strValue) | 
						
						
							|  | 					if( false !== strpos( $strName, 'L_ERRORCODE') || false !== strpos( $strName, 'MESSAGE') ) | 
						
						
							|  | 						$this->strErrors .= '<br />' . $strName . ': ' . $strValue; | 
						
						
							|  | 				 | 
						
						
							|  | 				$this->objPaypalTransaction->Messages = $this->strErrors; | 
						
						
							|  | 				$this->objPaypalTransaction->Save(); | 
						
						
							|  | 				return false; | 
						
						
							|  | 			} | 
						
						
							|  | 						 | 
						
						
							|  | 			//server transaction ok, finish with the payment .. | 
						
						
							|  | 			switch(strtoupper($this->objPaypalTransaction->ApiAction)){ | 
						
						
							|  | 				case 'DOEXPRESSCHECKOUTPAYMENT': | 
						
						
							|  | 					$this->objPaypalTransaction->PaymentStatus = $this->aryResponseValues['PAYMENTSTATUS']; | 
						
						
							|  | 					$this->objPaypalTransaction->PpToken = $this->aryResponseValues['TOKEN']; | 
						
						
							|  | 					break; | 
						
						
							|  | 				case 'GETEXPRESSCHECKOUTDETAILS': | 
						
						
							|  | 					$this->objPaypalTransaction->PayerId = $this->aryResponseValues['PAYERID']; | 
						
						
							|  | 					$this->objPaypalTransaction->PayerStatus = $this->aryResponseValues['PAYERSTATUS']; | 
						
						
							|  | 				case 'SETEXPRESSCHECKOUT': | 
						
						
							|  | 					$this->objPaypalTransaction->PpToken = $this->aryResponseValues['TOKEN']; | 
						
						
							|  | 					break; | 
						
						
							|  | 				default: | 
						
						
							|  | 					//unsupported method .. | 
						
						
							|  | 			}             | 
						
						
							|  | 			$this->objPaypalTransaction->Save(); | 
						
						
							|  | 			return true; | 
						
						
							|  | 		} | 
						
						
							|  | 		 | 
						
						
							|  | 		protected function checkAckReturned(){ | 
						
						
							|  | 			$strAck =$this->objPaypalTransaction->AckReturned ; | 
						
						
							|  | 			if( '' == $strAck) | 
						
						
							|  | 				$this->HasErrors = true; | 
						
						
							|  | 			else{ | 
						
						
							|  | 				switch( strtoupper( $strAck )){ | 
						
						
							|  | 					case 'SUCCESS': | 
						
						
							|  | 					case 'SUCCESSWITHWARNING': | 
						
						
							|  | 						$this->HasErrors = false; | 
						
						
							|  | 						break; | 
						
						
							|  | 					case 'FAILURE': | 
						
						
							|  | 					case 'FAILUREWITHWARNING': | 
						
						
							|  | 						$this->HasErrors = true; | 
						
						
							|  | 						break; | 
						
						
							|  | 					default: | 
						
						
							|  | 						//error ..                     | 
						
						
							|  | 						$this->HasErrors = true; | 
						
						
							|  | 				} | 
						
						
							|  | 			}         | 
						
						
							|  | 			return ! $this->HasErrors; | 
						
						
							|  | 		} | 
						
						
							|  | 		 | 
						
						
							|  | 		public function __get($strName){ | 
						
						
							|  | 			switch ($strName){ | 
						
						
							|  | 				case 'PaypalTransaction': | 
						
						
							|  | 					return $this->objPaypalTransaction; | 
						
						
							|  | 				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 | 
						
						
							|  |  | 
						
						
							|  | ?>
 |