|
|
- <?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
-
- ?>
|