TimeTrex/classes/pear/Payment/Process.php

676 lines
18 KiB
PHP

<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* Main package file
*
* Process.php is a unified OOP abstraction layer for credit card and echeck
* processing gateways (similar to what DB does for database calls).
*
* PHP versions 4 and 5
*
* LICENSE: This source file is subject to version 3.0 of the PHP license
* that is available through the world-wide-web at the following URI:
* http://www.php.net/license/3_0.txt. If you did not receive a copy of
* the PHP License and are unable to obtain it through the web, please
* send a note to license@php.net so we can mail you a copy immediately.
*
* @category Payment
* @package Payment_Process
* @author Ian Eure <ieure@php.net>
* @author Joe Stump <joe@joestump.net>
* @copyright 1997-2005 The PHP Group
* @license http://www.php.net/license/3_0.txt PHP License 3.0
* @version CVS: $Id: Process.php,v 1.49 2005/12/16 18:45:57 ieure Exp $
* @link http://pear.php.net/package/Payment_Process
*/
require_once('PEAR.php');
require_once('Validate.php');
require_once('Validate/Finance/CreditCard.php');
require_once('Payment/Process/Type.php');
/**
* Error codes
*/
define('PAYMENT_PROCESS_ERROR_NOTIMPLEMENTED', -100);
define('PAYMENT_PROCESS_ERROR_NOFIELD', -101);
define('PAYMENT_PROCESS_ERROR_NOPROCESSOR', -102);
define('PAYMENT_PROCESS_ERROR_INCOMPLETE', -1);
define('PAYMENT_PROCESS_ERROR_INVALID', -2);
define('PAYMENT_PROCESS_ERROR_AVS', -3);
define('PAYMENT_PROCESS_ERROR_CVV', -4);
/**
* Transaction actions
*/
/**
* A normal transaction
*/
define('PAYMENT_PROCESS_ACTION_NORMAL', 200);
/**
* Authorize only. No funds are transferred.
*/
define('PAYMENT_PROCESS_ACTION_AUTHONLY', 201);
/**
* Credit funds back from a previously-charged transaction.
*/
define('PAYMENT_PROCESS_ACTION_CREDIT', 202);
/**
* Post-authorize an AUTHONLY transaction.
*/
define('PAYMENT_PROCESS_ACTION_POSTAUTH', 203);
/**
* Clear a previous transaction
*/
define('PAYMENT_PROCESS_ACTION_VOID', 204);
/**
* Transaction sources
*/
define('PAYMENT_PROCESS_SOURCE_POS', 300);
define('PAYMENT_PROCESS_SOURCE_ONLINE', 301);
/**
* Result codes
*/
define('PAYMENT_PROCESS_RESULT_APPROVED', 400);
define('PAYMENT_PROCESS_RESULT_DECLINED', 401);
define('PAYMENT_PROCESS_RESULT_OTHER', 402);
define('PAYMENT_PROCESS_RESULT_FRAUD', 403);
define('PAYMENT_PROCESS_RESULT_DUPLICATE',404);
define('PAYMENT_PROCESS_RESULT_REVIEW', 405);
define('PAYMENT_PROCESS_AVS_MATCH', 500);
define('PAYMENT_PROCESS_AVS_MISMATCH', 501);
define('PAYMENT_PROCESS_AVS_ERROR', 502);
define('PAYMENT_PROCESS_AVS_NOAPPLY', 503);
define('PAYMENT_PROCESS_CVV_MATCH', 600);
define('PAYMENT_PROCESS_CVV_MISMATCH', 601);
define('PAYMENT_PROCESS_CVV_ERROR', 602);
define('PAYMENT_PROCESS_CVV_NOAPPLY', 603);
/**
* Payment_Process
*
* @author Ian Eure <ieure@php.net>
* @package Payment_Process
* @category Payment
* @version @version@
*/
class Payment_Process {
/**
* Return an instance of a specific processor.
*
* @param string $type Name of the processor
* @param array $options Options for the processor
* @return mixed Instance of the processor object, or a PEAR_Error object.
*/
// static function,avoid PHP strict error
static function &factory($type, $options = false)
{
$class = "Payment_Process_".$type;
if (include_once "Payment/Process/{$type}.php") {
if (class_exists($class)) {
$object = new $class($options);
return $object;
}
}
$ret = PEAR::raiseError('"'.$type.'" processor does not exist',
PAYMENT_PROCESS_ERROR_NOPROCESSOR);
return $ret;
}
/**
* Determine if a field is required.
*
* @param string $field Field to check
* @return boolean true if required, false if optional.
*/
function isRequired($field)
{
return (isset($this->_required[$field]));
}
/**
* Determines if a field exists.
*
* @author Ian Eure <ieure@php.net>
* @param string $field Field to check
* @return boolean true if field exists, false otherwise
*/
function fieldExists($field)
{
return @in_array($field, $this->getFields());
}
/**
* Get a list of fields.
*
* This function returns an array containing all the possible fields which
* may be set.
*
* @author Ian Eure <ieure@php.net>
* @access public
* @return array Array of valid fields.
*/
function getFields()
{
$vars = array_keys(get_class_vars(get_class($this)));
foreach ($vars as $idx => $field) {
if ($field[0] == '_') {
unset($vars[$idx]);
}
}
return $vars;
}
/**
* Set class options.
*
* @author Ian Eure <ieure@php.net>
* @param Array $options Options to set
* @param Array $defaultOptions Default options
* @return void
*/
function setOptions($options = false, $defaultOptions = false)
{
$defaultOptions = $defaultOptions ? $defaultOptions : $this->_defaultOptions;
$this->_options = @array_merge($defaultOptions, $options);
}
/**
* Get an option value.
*
* @author Ian Eure <ieure@php.net>
* @param string $option Option to get
* @return mixed Option value
*/
function getOption($option)
{
return @$this->_options[$option];
}
/**
* Set an option value
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @param string $option Option name to set
* @param mixed $value Value to set
*/
function setOption($option,$value)
{
return ($this->_options[$option] = $value);
}
/**
* See if a value is a defined constant.
*
* This function checks to see if $value is defined in one of
* PAYMENT_PROCESS_{$class}_*. It's used to verify that e.g.
* $object->action is one of PAYMENT_PROCESS_ACTION_NORMAL,
* PAYMENT_PROCESS_ACTION_AUTHONLY etc.
*
* @access private
* @param mixed $value Value to check
* @param mixed $class Constant class to check
* @return boolean true if it is defined, false otherwise.
*/
function _isDefinedConst($value, $class)
{
$constClass = 'PAYMENT_PROCESS_'.strtoupper($class).'_';
$length = strlen($constClass);
$consts = get_defined_constants();
$found = false;
foreach ($consts as $constant => $constVal) {
if (strncmp($constClass, $constant, $length) === 0
&& $constVal == $value) {
$found = true;
break;
}
}
return $found;
}
/**
* Statically check a Payment_Result class for success
*
* @param mixed $obj
* @return bool
* @access public
* @static
* @author Joe Stump <joe@joestump.net>
*/
function isSuccess($obj)
{
if (is_a($obj, 'Payment_Process_Result')) {
if ($obj->getCode() == PAYMENT_PROCESS_RESULT_APPROVED) {
return true;
}
}
return false;
}
/**
* Statically check a Payment_Result class for error
*
* @param mixed $obj
* @return bool
* @access public
* @author Joe Stump <joe@joestump.net>
*/
function isError($obj)
{
if (PEAR::isError($obj)) {
return true;
}
if (is_a($obj, 'Payment_Process_Result')) {
if ($obj->getCode() != PAYMENT_PROCESS_RESULT_APPROVED) {
return true;
}
}
return false;
}
}
/**
* Payment_Process_Result
*
* The core result class that should be returned from each driver's process()
* function. This should be extended as Payment_Process_Result_DriverName and
* then have the appropriate fields mapped out accordingly.
*
* Take special care to appropriately create a parse() function in your result
* class. You can then call _mapFields() with a resultArray (ie. exploded
* result) to map your results from parse() into the member variables.
*
* Please note that this class keeps your original codes intact so they can
* be accessed directly and then uses the function wrappers to return uniform
* Payment_Process codes.
*
* @author Joe Stump <joe@joestump.net>
* @package Payment_Process
* @category Payment
* @version @version@
*/
class Payment_Process_Result {
/**
* Processor instance which this result was instantiated from.
*
* This should contain a reference to the requesting Processor.
*
* @author Ian Eure <ieure@php.net>
* @access private
* @var Object
*/
var $_request;
/**
* The raw response (ie. from cURL)
*
* @author Joe Stump <joe@joestump.net>
* @access protected
* @var string $_rawResponse
*/
var $_rawResponse = null;
/**
* The approval/decline code
*
* The value returned by your gateway as approved/declined should be mapped
* into this variable. Valid results should then be mapped into the
* appropriate PAYMENT_PROCESS_RESULT_* code using the $_statusCodeMap
* array. Values returned into $code should be mapped as keys in the map
* with PAYMENT_PROCESS_RESULT_* as the values.
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var mixed $code
* @see PAYMENT_PROCESS_RESULT_APPROVED, PAYMENT_PROCESS_RESULT_DECLINED
* @see PAYMENT_PROCESS_RESULT_OTHER, $_statusCodeMap
*/
var $code;
/**
* Message/Response Code
*
* Along with the response (yes/no) you usually get a response/message
* code that translates into why it was approved/declined. This is where
* you map that code into. Your $_statusCodeMessages would then be keyed by
* valid messageCode values.
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var mixed $messageCode
* @see $_statusCodeMessages
*/
var $messageCode;
/**
* Message from gateway
*
* Map the textual message from the gateway into this variable. It is not
* currently returned or used (in favor of the $_statusCodeMessages map, but
* can be accessed directly for debugging purposes.
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var string $message
* @see $_statusCodeMessages
*/
var $message = 'No message from gateway';
/**
* Authorization/Approval code
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var string $approvalCode
*/
var $approvalCode;
/**
* Address verification code
*
* The AVS code returned from your gateway. This should then be mapped to
* the appropriate PAYMENT_PROCESS_AVS_* code using $_avsCodeMap. This value
* should also be mapped to the appropriate textual message via the
* $_avsCodeMessages array.
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var string $avsCode
* @see PAYMENT_PROCESS_AVS_MISMATCH, PAYMENT_PROCESS_AVS_ERROR
* @see PAYMENT_PROCESS_AVS_MATCH, PAYMENT_PROCESS_AVS_NOAPPLY, $_avsCodeMap
* @see $_avsCodeMessages
*/
var $avsCode;
/**
* Transaction ID
*
* This is the unique transaction ID, which is used by gateways to modify
* transactions (credit, update, etc.). Map the appropriate value into this
* variable.
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var string $transactionId
*/
var $transactionId;
/**
* Invoice Number
*
* Unique internal invoiceNumber (ie. your company's order/invoice number
* that you assign each order as it is processed). It is always a good idea
* to pass this to the gateway (which is usually then echo'd back).
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var string $invoiceNumber
*/
var $invoiceNumber;
/**
* Customer ID
*
* Unique internal customer ID (ie. your company's customer ID used to
* track individual customers).
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var string $customerId
*/
var $customerId;
/**
* CVV Code
*
* The CVV code is the 3-4 digit number on the back of most credit cards.
* This value should be mapped via the $_cvvCodeMap variable to the
* appropriate PAYMENT_PROCESS_CVV_* values.
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var string $cvvCode
*/
var $cvvCode = PAYMENT_PROCESS_CVV_NOAPPLY;
/**
* CVV Message
*
* Your cvvCode value should be mapped to appropriate messages via the
* $_cvvCodeMessage array. This value is merely here to hold the value
* returned from the gateway (if any).
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @var string $cvvMessage
*/
var $cvvMessage = 'No CVV message from gateway';
function __construct($rawResponse, $request)
{
$this->_rawResponse = $rawResponse;
$this->_request = $request;
}
/**
* factory
*
* @author Joe Stump <joe@joestump.net>
* @author Ian Eure <ieure@php.net>
* @param string $type
* @param string $rawResponse
* @param mixed $request
* @return mixed Payment_Process_Result on succes, PEAR_Error on failure
*/
static function &factory($type, $rawResponse, $request)
{
$class = 'Payment_Process_Result_'.$type;
if (class_exists($class)) {
$ret = new $class($rawResponse, $request);
return $ret;
}
$ret = PEAR::raiseError('Invalid response type: '.$type.'('.$class.')');
return $ret;
}
/**
* validate
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @return mixed
*/
function validate()
{
if ($this->_request->getOption('avsCheck') === true) {
if ($this->getAVSCode() != PAYMENT_PROCESS_AVS_MATCH) {
return PEAR::raiseError('AVS check failed',
PAYMENT_PROCESS_ERROR_AVS);
}
}
$paymentType = $this->_request->_payment->_type;
if ($this->_request->getOption('cvvCheck') === true &&
$paymentType == 'CreditCard') {
if ($this->getCvvCode() != PAYMENT_PROCESS_CVV_MATCH) {
return PEAR::raiseError('CVV check failed',
PAYMENT_PROCESS_ERROR_CVV);
}
}
if ($this->getCode() != PAYMENT_PROCESS_RESULT_APPROVED) {
return PEAR::raiseError($this->getMessage(),
PAYMENT_PROCESS_RESULT_DECLINED);
}
return true;
}
/**
* parse
*
* @abstract
* @author Joe Stump <joe@joestump.net>
* @access public
*/
function parse()
{
return PEAR::raiseError('parse() not implemented',
PAYMENT_PROCESS_ERROR_NOTIMPLEMENTED);
}
/**
* parseCallback
*
* @abstract
* @author Joe Stump <joe@joestump.net>
* @access public
* @see Payment_Process_Common::processCallback()
*/
function parseCallback()
{
return PEAR::raiseError('parse() not implemented',
PAYMENT_PROCESS_ERROR_NOTIMPLEMENTED);
}
/**
* isLegitimate
*
* Some gateways allow you to validate the response to make sure it's
* actually them that are sending the response. Override this in the
* driver result class and implement this if your gateway provides such
* a mechanism. Any information required should be passed via $options.
*
* @abstract
* @author Joe Stump <joe@joestump.net>
* @access public
*/
function isLegitimate()
{
return PEAR::raiseError('parse() not implemented',
PAYMENT_PROCESS_ERROR_NOTIMPLEMENTED);
}
/**
* getCode
*
* @author Joe Stump <joe@joestump.net>
* @access public
*/
function getCode()
{
if (isset($this->_statusCodeMap[$this->code])) {
return $this->_statusCodeMap[$this->code];
} else {
return PAYMENT_PROCESS_RESULT_DECLINED;
}
}
/**
* getMessage
*
* Return the message from the code map, or return the raw message if
* there is one. Otherwise, return a worthless message.
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @return string
*/
function getMessage()
{
if (isset($this->_statusCodeMessages[$this->messageCode])) {
return $this->_statusCodeMessages[$this->messageCode];
} elseif(strlen($this->message)) {
return $this->message;
} else {
return 'No message reported';
}
}
function getAVSCode()
{
return $this->_avsCodeMap[$this->avsCode];
}
function getAVSMessage()
{
return $this->_avsCodeMessages[$this->avsCode];
}
function getCvvCode()
{
return $this->_cvvCodeMap[$this->cvvCode];
}
function getCvvMessage()
{
return $this->_cvvCodeMessages[$this->cvvCode];
}
/**
* _mapFields
*
* @author Joe Stump <joe@joestump.net>
* @access private
* @param mixed $responseArray
*/
function _mapFields($responseArray) {
foreach($this->_fieldMap as $key => $val) {
$this->$val = $responseArray[$key];
}
}
/**
* Accept an object
*
* @param object $object Object to accept
* @return boolean true if accepted, false otherwise
*/
function accept(&$object)
{
if (is_a($object, 'Log')) {
$this->_log = $object;
return true;
}
return false;
}
/**
* Log a message
*
* @param string $message Message to log
* @param string $priority Message priority
* @return mixed Return value of Log::log(), or false if no Log instance
* has been accepted.
*/
function log($message, $priority = null)
{
if (isset($this->_log) && is_object($this->_log)) {
return $this->_log->log($message, $priority);
}
return false;
}
}
?>