A QCodo powered CMS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

612 lines
19 KiB

<?php
if(!defined('QUINTACMS') ) die('No Quinta.');
if (!defined("PAYMENTACTION.CLASS.PHP")){
define("PAYMENTACTION.CLASS.PHP",1);
/**
* Class PaymentActionBase - base class for classes that perform payment actions with a web service
*
* This class provides the basic actions and properties for all of the PaymentAction classes. This includes
* making the connection to the payment service provider, sending the request in either GET or POST (usually
* in XML ..) and accepting the response from the server. The response is stored in strResponse regardless
* of format.
*
* Subclasses are responsible for formatting the request. Users of the subclasses are responsible for initializing
* the required properties.
*
* Subclasses must implement these methods:
* - PreProcess: perform any actions to set up the transaction
* - Process: perform the actual transaction, ie. connect to server and make a request in most cases.
* - PostProcess: perform any validation checks and update the order_status_history table in most cases.
* PayPal Express Checkout redirects the user to the PayPal site at this point and order_status
* is set to "Pending" (??), there is a special return page for this kind of case that will complete the
* order_status update (??).
*
* See the PaymentModule class documentation and documentation for the PaymentAction subclasses
* for more details.
*
*@todo
* - port payment actions to use WebServiceRequest .. and this class should go away ..
*
*@author Erik Winn <sidewalksoftware@gmail.com>
*
*@version 0.3
*
*@package Quinta
* @subpackage Classes
*/
abstract class PaymentActionBase {
/**
*@var Order local reference to the Order being processed
*/
protected $objOrder;
/**
*@var string Order id (aka Invoice number)
*/
protected $strOrderId;
/**
* In some cases (eg. Authorize.net) this is returned by the provider, in others
* we may provide it for tracking the transaction.
*@var string Transaction identifier
*/
protected $strTransactionId;
/**
* In some cases (eg. Authorize.net) this is used in place of RemotePassword
* - it is provided merely for literate purposes.
*@var string Transaction Key - aka Password ..
*/
protected $strTransactionKey;
/**
*@var string Username, login or account id for the service
*/
protected $strRemoteAccountId;
/**
*@var string password for the service
*/
protected $strRemotePassword;
/**
* Note: this is currently unused, the Quinta built in PaymentModule displays the payment
* action selection as QRadioButtons ..
*@var string the template file to use for this method
*/
protected $strTemplateUri;
/**
*@var string the FQD for the payment service provider API server
*/
protected $strRemoteDomainName;
/**
* Note: This must contain the separater character that follows the script name, eg. '?' or '&'
* if the request is of type GET
*@var string the URL portion after the domain name leading to API script
*/
protected $strRemoteCgiUrl;
/**
* This is the URL to which a customer is redirected to make a payment (eg. www.paypal.com ..)
*@var string the FQD for the payment service provider redirect target
*/
protected $strRedirectDomainName;
/**
* Note: This must contain the separater character that follows the script name, eg. '?' or '&'
* if the request is of type GET
*@var string the URL portion after the domain name leading to redirect script
*/
protected $strRedirectCgiUrl;
/**
*@var string storage for response from service
*/
protected $strResponse;
/**
*@var string storage for POST request to service
*/
protected $strPOSTRequest;
/**
*@var string query string appended to CGI URL for a GET request
*/
protected $strGETRequest;
/**
*@var string The type of request to be made (GET | POST )
*/
protected $strRequestType;
/**
*@var boolean True if we should use CURL to connect to the provider
*/
protected $blnUseCurl = false;
/**
*@var boolean True if we should use SSL to connect to the provider
*/
protected $blnUseSsl = true;
/**
*@var boolean True if we should use SSL certificate to authenticate ourselves ..
*/
protected $blnUseSslCertificate = false;
/**
*@var string full path to our SSL certificate
*/
protected $strSslCertificateUri;
/**
*@var integer Port number to use for the connection (80, 443)
*/
protected $intPort = 443;
/**
*@var integer Connection time out in seconds
*/
protected $intTimeOut = 60;
/**
*@var boolean True if there were errors or if the transaction/connection failed for any reason
*/
protected $blnHasErrors;
/**
*@var string Errors
*/
protected $strErrors;
/**
* This contains a string which either confirms the approval of a payment or gives a reason
* for its failure.
*@var string status explanation text from the transaction
*/
protected $strStatusText;
/**
*@var boolean True if the transaction was approved
*/
protected $blnApproved = false;
/**
* NOTE: You must explicitly set this to disable testing mode ..
*@var boolean True for testing (and by default)
*/
protected $blnTestMode = true;
/**
*@var float total charges for items
*/
protected $fltSubTotal;
/**
*@var float Charges for shipping
*/
protected $fltShippingCharge;
/**
*@var float Charges for handling
*/
protected $fltHandlingCharge;
/**
*@var float Taxes
*/
protected $fltTax;
/**
*@var string CC for customer
*/
protected $strCCNumber;
/**
*@var string CC expiration data for customer
*/
protected $strCCExpirationYear;
protected $strCCExpirationMonth;
/**
*@var string CCV number for customer
*/
protected $strCCVNumber;
/**
*@var float Total of all charges, items, shipping, tax and handling
*/
protected $fltTotalPrice;
/**
* PaymentActionBase Constructor
*
* @param Order objOrder - the order for which we are attempting to pay..
*/
public function __construct(Order $objOrder){
$this->objOrder =& $objOrder;
$this->fltHandlingCharge = $objOrder->HandlingCharged;
$this->fltShippingCharge = $objOrder->ShippingCharged;
$this->fltTax = $objOrder->Tax;
$this->fltTotalPrice = $objOrder->ProductTotalCharged
+ $objOrder->HandlingCharged
+ $objOrder->ShippingCharged
+ $objOrder->Tax;
}
/**
* Performs any preparation steps prior to submitting an actual payment. For example
* the PayPal NVP (Express Checkout) calls the SetExpressCheckoutDetails API here
*@return bool true on success
*/
abstract public function PreProcess();
/**
* Performs any steps necessary after submitting an actual payment. For example
* the PayPal NVP (Express Checkout) calls the GetExpressCheckoutDetails API here.
* Updating order_status_history is also performed here.
*@return bool true on success
*/
abstract public function PostProcess();
/**
* Performs the actual payment submission - in some cases this is a request/response
* routine and in some (eg. PayPal), this simply redirects the user to the provider site.
*@return bool true on success
*/
abstract public function Process();
/**
* Parses the response from the payment service provider
*/
abstract protected function handleResponse();
/**
* Creates GET query string for the transaction appropriate to the provider API, storing
* the result in strGETRequest.
*/
abstract protected function createGETRequest();
/**
* Creates GET query string for the transaction appropriate to the provider API, storing
* the result in strGETRequest.
*/
abstract protected function createPOSTRequest();
/**
* This function directs the call to the appropriate creation function and returns
* a string containing either a query string for a GET or a content string for a POST.
*
* A RequestType may be provided to override a default as an alternative to setting
* it explicitly. Note that this will set the RequestType for the object.
*
*@param string strRequestType - you may provide the RequestType
*/
protected function createRequest($strRequestType=null){
if(null != $strRequestType)
$this->strRequestType = $strRequestType;
switch($this->strRequestType){
case 'GET':
$this->createGETRequest();
return $this->strGETRequest;
break;
case 'POST':
$this->createPOSTRequest();
return $this->strPOSTRequest;
break;
case 'SOAP':
default:
throw new QCallerException('PaymentAction - Unsupported RequestType: ' . $this->strRequestType);
}
}
/**
* Connects to payment service and submits the request. Note that
* this function merely constructs a request URL from internal variables
* that are set in createRequest, it may therefor contain a GET query
* string or not depending on the subclass requirements.
*
*@return bool true on success
*/
protected function submitRequest(){
if($this->UseCurl)
return $this->submitCurlRequest();
if($this->UseSsl)
$strProtocol = 'ssl://';
else
$strProtocol = 'http://';
//attempt to connect ..
$fp = fsockopen($strProtocol . $this->strRemoteDomainName,
$this->intPort,
$intError,
$strError,
$this->intTimeOut
);
//did we connect?
if (!$fp){
/* $this->blnHasErrors = true;
return 0;*/
throw new QCallerException("Payment Action base: Connection request failed: $strError ($intError) ");
}else{
//construct the request ..
switch( $this->strRequestType ){
case 'GET':
$out = "GET " . $this->strRemoteCgiUrl . $this->strGETRequest . " HTTP/1.1\r\n";
$out .= "Host:" . $this->strRemoteDomainName . "\r\n";
$out .= "User-Agent: QuintaCMS " . QUINTA_VERSION . "\r\n";
$out .= "Connection: Close\r\n\r\n";
break;
case 'POST':
$out = "POST " . $this->strRemoteCgiUrl . " HTTP/1.1\r\n";
$out .= "Host:" . $this->strRemoteDomainName . "\r\n";
$out .= "User-Agent: QuintaCMS " . QUINTA_VERSION . "\r\n";
// $out .= "MIME-Version: 1.0\r\n";
$out .= "Content-Type: application/x-www-form-urlencoded\r\n";
// $out .= "Accept: text/xml\r\n";
$out .= "Content-length: " . strlen($this->strPOSTRequest) . "\r\n";
$out .= "Cache-Control: no-cache\r\n";
$out .= "Connection: Close\r\n\r\n";
$out .= $this->strPOSTRequest . "\r\n\r\n";
break;
default:
throw new QCallerException('WebService RequestType unsupported: ' . $this->RequestType);
}
//send the request
fwrite($fp, $out );
$this->strResponse = '';
//store the response
while ( !feof($fp) )
$this->strResponse .= fgets($fp, 128);
$this->strResponse .= $out;
fclose($fp);
}
return true;
}
/**
* This is an alternate request submission method using CURL
* @return bool true on sucess
*/
protected function submitCurlRequest(){
if($this->UseSsl)
$strProtocol = 'https://';
else
$strProtocol = 'http://';
$objCurlHandle = curl_init();
curl_setopt($objCurlHandle, CURLOPT_USERAGENT, 'QuintaCMS ' . QUINTA_VERSION);
curl_setopt($objCurlHandle, CURLOPT_TIMEOUT, $this->intTimeOut);
curl_setopt ($objCurlHandle, CURLOPT_RETURNTRANSFER, 1);
switch( $this->strRequestType ){
case 'GET':
$strUri = $strProtocol . $this->strRemoteDomainName . $this->strRemoteCgiUrl . $this->strGETRequest;
curl_setopt($objCurlHandle, CURLOPT_URL, $strUri);
break;
case 'POST':
$strUri = $strProtocol . $this->strRemoteDomainName . $this->strRemoteCgiUrl;
curl_setopt($objCurlHandle, CURLOPT_POST, 1);
curl_setopt($objCurlHandle, CURLOPT_URL, $strUri);
curl_setopt($objCurlHandle, CURLOPT_POSTFIELDS, $this->strPOSTRequest);
break;
default:
throw new QCallerException('WebService RequestType unsupported: ' . $this->RequestType);
}
if($this->UseSsl){
curl_setopt($objCurlHandle, CURLOPT_SSL_VERIFYHOST, 0);
curl_setopt($objCurlHandle, CURLOPT_SSL_VERIFYPEER, 0);
}
if($this->UseSslCertificate)
curl_setopt($objCurlHandle, CURLOPT_SSLCERT,$this->strSslCertificateUri);
$this->strResponse = curl_exec($objCurlHandle);
if(false === $this->strResponse ){
$this->HasErrors = true;
$this->strErrors .= curl_error($objCurlHandle);
return false;
}
curl_close($objCurlHandle);
}
/**
*This function sets the order status for an order when payment is approved.
* It also inserts a new order_status_history and clears the shopping cart. The confirmation
* email is handled by Order when the status is set.
*/
public function completeOrder(){
/* this is now done with __set magic in Order and the notes seem superflous, disabled pending removal
$objOrderStatusHistory = new OrderStatusHistory();
$objOrderStatusHistory->OrderId = $this->objOrder->Id;
$objOrderStatusHistory->StatusId = OrderStatusType::Paid;
$objOrderStatusHistory->Notes = $this->objOrder->Notes;
$objOrderStatusHistory->Save();
*/
// Order can send its own email confirmations ..
$this->objOrder->SetStatus(OrderStatusType::Paid);
IndexPage::$objShoppingCart->DeleteAllShoppingCartItems();
}
public function __get($strName){
switch ($strName){
case 'TotalPrice':
return $this->fltTotalPrice;
case 'StatusText':
return $this->strStatusText;
case 'TemplateUri':
return $this->strTemplateUri ;
case 'Order':
return $this->objOrder ;
case 'OrderId':
return $this->objOrder->Id ;
case 'RemotePassword':
return $this->strRemotePassword ;
case 'RemoteAccountId':
return $this->strRemoteAccountId ;
case 'RemoteCgiUrl':
return $this->strRemoteCgiUrl ;
case 'RemoteDomainName':
return $this->strRemoteDomainName ;
case 'Errors':
return $this->strErrors ;
case 'Approved':
return $this->blnApproved ;
case 'HasErrors':
return $this->blnHasErrors ;
case 'UseCurl':
return $this->blnUseSsl ;
case 'UseSsl':
return $this->blnUseSsl ;
case 'UseSslCertificate':
return $this->blnUseSslCertificate ;
case 'SslCertificateUri':
return $this->strSslCertificateUri;
case 'TestMode':
return $this->blnTestMode ;
case 'CCNumber':
return $this->strCCNumber ;
case 'CCExpirationYear':
return $this->strCCExpirationYear ;
case 'CCExpirationMonth':
return $this->strCCExpirationMonth ;
case 'CCVNumber':
return $this->strCCVNumber ;
default:
throw new QCallerException('Payment Action - Access Unknown property: ' . $strName);
}
}
public function __set($strName, $mixValue){
switch ($strName){
case 'TemplateUri':
try {
return ($this->strTemplateUri = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'TransactionId':
try {
return ($this->strTransactionId = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'OrderId':
try {
return ($this->strOrderId = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'RemoteDomainName':
try {
return ($this->strRemoteDomainName = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'RemoteCgiUrl':
try {
return ($this->strRemoteCgiUrl = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'RemoteAccountId':
try {
return ($this->strRemoteAccountId = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'RemotePassword':
try {
return ($this->strRemotePassword = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'Approved':
try {
return ($this->blnApproved = QType::Cast($mixValue, QType::Boolean ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'Errors':
try {
return ($this->strErrors = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'HasErrors':
try {
return ($this->blnHasErrors = QType::Cast($mixValue, QType::Boolean ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'UseCurl':
try {
return ($this->blnUseCurl = QType::Cast($mixValue, QType::Boolean ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'UseSsl':
try {
return ($this->blnUseSsl = QType::Cast($mixValue, QType::Boolean ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'UseSslCertificate':
try {
return ($this->blnUseSslCertificate = QType::Cast($mixValue, QType::Boolean ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'SslCertificateUri':
try {
return ($this->strSslCertificateUri = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'TestMode':
try {
return ($this->blnTestMode = QType::Cast($mixValue, QType::Boolean ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'Port':
try {
return ($this->intPort = QType::Cast($mixValue, QType::Integer ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'TimeOut':
try {
return ($this->intTimeOut = QType::Cast($mixValue, QType::Integer ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'CCNumber':
try {
return ($this->strCCNumber = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'CCExpirationYear':
try {
return ($this->strCCExpirationYear = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'CCExpirationMonth':
try {
return ($this->strCCExpirationMonth = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
case 'CCVNumber':
try {
return ($this->strCCVNumber = QType::Cast($mixValue, QType::String ));
} catch (QInvalidCastException $objExc) {
$objExc->IncrementOffset();
throw $objExc;
}
default:
throw new QCallerException('Payment Action - Set Unknown property: ' . $strName);
}
}
}//end class
}//end define
?>