TimeTrex/classes/pear/Payment/Process/LinkPoint.php

517 lines
16 KiB
PHP

<?php
/* vim: set expandtab tabstop=4 shiftwidth=4: */
/**
* LinkPoint processor
*
* 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 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: $Revision: 1.13 $
* @link http://pear.php.net/package/Payment_Process
* @link http://www.linkpoint.net/
*/
require_once('Payment/Process.php');
require_once('Payment/Process/Common.php');
require_once('Net/Curl.php');
require_once('XML/Parser.php');
$GLOBALS['_Payment_Process_LinkPoint'] = array(
PAYMENT_PROCESS_ACTION_NORMAL => 'SALE',
PAYMENT_PROCESS_ACTION_AUTHONLY => 'PREAUTH',
PAYMENT_PROCESS_ACTION_POSTAUTH => 'POSTAUTH'
);
/**
* Payment_Process_LinkPoint
*
* This is a processor for LinkPoint's merchant payment gateway.
* (http://www.linkpoint.net/)
*
* *** WARNING ***
* This is BETA code, and has not been fully tested. It is not recommended
* that you use it in a production envorinment without further testing.
*
* @package Payment_Process
* @author Joe Stump <joe@joestump.net>
* @version @version@
*/
class Payment_Process_LinkPoint extends Payment_Process_Common
{
/**
* Front-end -> back-end field map.
*
* This array contains the mapping from front-end fields (defined in
* the Payment_Process class) to the field names DPILink requires.
*
* @see _prepare()
* @access private
*/
var $_fieldMap = array(
// Required
'login' => 'configfile',
'action' => 'ordertype',
'invoiceNumber' => 'oid',
'customerId' => 'x_cust_id',
'amount' => 'chargetotal',
'name' => '',
'zip' => 'zip',
// Optional
'company' => 'company',
'address' => 'address1',
'city' => 'city',
'state' => 'state',
'country' => 'country',
'phone' => 'phone',
'email' => 'email',
'ip' => 'ip',
);
/**
* $_typeFieldMap
*
* @author Joe Stump <joe@joestump.net>
* @access protected
*/
var $_typeFieldMap = array(
'CreditCard' => array(
'cardNumber' => 'cardnumber',
'cvv' => 'cvm',
'expDate' => 'expDate'
),
'eCheck' => array(
'routingCode' => 'routing',
'accountNumber' => 'account',
'type' => 'type',
'bankName' => 'bank',
'name' => 'name',
'driversLicense' => 'dl',
'driversLicenseState' => 'dlstate'
)
);
/**
* Default options for this processor.
*
* @see Payment_Process::setOptions()
* @access private
*/
var $_defaultOptions = array(
'host' => 'secure.linkpt.net',
'port' => '1129',
'result' => 'LIVE'
);
/**
* Has the transaction been processed?
*
* @type boolean
* @access private
*/
var $_processed = false;
/**
* The response body sent back from the gateway.
*
* @access private
*/
var $_responseBody = '';
/**
* Constructor.
*
* @param array $options Class options to set.
* @see Payment_Process::setOptions()
* @return void
*/
function __construct($options = false)
{
parent::__construct($options);
$this->_driver = 'LinkPoint';
}
/**
* Process the transaction.
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @return mixed Payment_Process_Result on success, PEAR_Error on failure
*/
function &process()
{
if (!strlen($this->_options['keyfile']) ||
!file_exists($this->_options['keyfile'])) {
return PEAR::raiseError('Invalid key file');
}
// Sanity check
$result = $this->validate();
if (PEAR::isError($result)) {
return $result;
}
// Prepare the data
$result = $this->_prepare();
if (PEAR::isError($result)) {
return $result;
}
// Don't die partway through
PEAR::pushErrorHandling(PEAR_ERROR_RETURN);
$xml = $this->_prepareQueryString();
if (PEAR::isError($xml)) {
return $xml;
}
$url = 'https://'.$this->_options['host'].':'.$this->_options['port'].
'/LSGSXML';
$curl = new Net_Curl($url);
$result = $curl->create();
if (PEAR::isError($result)) {
return $result;
}
$curl->type = 'POST';
$curl->fields = $xml;
$curl->sslCert = $this->_options['keyfile'];
// LinkPoint's staging server has a boned certificate. If they are
// testing against staging we need to turn off SSL host verification.
if ($this->_options['host'] == 'staging.linkpt.net') {
$curl->verifyPeer = false;
$curl->verifyHost = 0;
}
$curl->userAgent = 'PEAR Payment_Process_LinkPoint 0.1';
$result = &$curl->execute();
if (PEAR::isError($result)) {
return PEAR::raiseError('cURL error: '.$result->getMessage());
} else {
$curl->close();
}
$this->_responseBody = trim($result);
$this->_processed = true;
// Restore error handling
PEAR::popErrorHandling();
$response = &Payment_Process_Result::factory($this->_driver,
$this->_responseBody,
$this);
if (!PEAR::isError($response)) {
$response->parse();
}
return $response;
}
/**
* Prepare the POST query string.
*
* @access private
* @return string The query string
*/
function _prepareQueryString()
{
$data = array_merge($this->_options,$this->_data);
$xml = '<!-- Payment_Process order -->'."\n";
$xml .= '<order>'."\n";
$xml .= '<merchantinfo>'."\n";
$xml .= ' <configfile>'.$data['configfile'].'</configfile>'."\n";
$xml .= ' <keyfile>'.$data['keyfile'].'</keyfile>'."\n";
$xml .= ' <host>'.$data['authorizeUri'].'</host>'."\n";
$xml .= ' <appname>PEAR Payment_Process</appname>'."\n";
$xml .= '</merchantinfo>'."\n";
$xml .= '<orderoptions>'."\n";
$xml .= ' <ordertype>'.$data['ordertype'].'</ordertype>'."\n";
$xml .= ' <result>'.$data['result'].'</result>'."\n";
$xml .= '</orderoptions>'."\n";
$xml .= '<payment>'."\n";
$xml .= ' <subtotal>'.$data['chargetotal'].'</subtotal>'."\n";
$xml .= ' <tax>0.00</tax>'."\n";
$xml .= ' <shipping>0.00</shipping>'."\n";
$xml .= ' <chargetotal>'.$data['chargetotal'].'</chargetotal>'."\n";
$xml .= '</payment>'."\n";
// Set payment method to eCheck if our payment type is eCheck.
// Default is Credit Card.
$data['x_method'] = 'CC';
switch ($this->_payment->getType())
{
case 'eCheck':
return PEAR::raiseError('eCheck not currently supported',
PAYMENT_PROCESS_ERROR_NOTIMPLEMENTED);
$xml .= '<telecheck>'."\n";
$xml .= ' <routing></routing>'."\n";
$xml .= ' <account></account>'."\n";
$xml .= ' <checknumber></checknumber>'."\n";
$xml .= ' <bankname></bankname>'."\n";
$xml .= ' <bankstate></bankstate>'."\n";
$xml .= ' <dl></dl>'."\n";
$xml .= ' <dlstate></dlstate>'."\n";
$xml .= ' <accounttype>pc|ps|bc|bs</accounttype>'."\n";
$xml .= '<telecheck>'."\n";
break;
case 'CreditCard':
$xml .= '<creditcard>'."\n";
$xml .= ' <cardnumber>'.$data['cardnumber'].'</cardnumber>'."\n";
list($month,$year) = explode('/',$data['expDate']);
if (strlen($year) == 4) {
$year = substr($year,2);
}
$month = sprintf('%02d',$month);
$xml .= ' <cardexpmonth>'.$month.'</cardexpmonth>'."\n";
$xml .= ' <cardexpyear>'.$year.'</cardexpyear>'."\n";
if (strlen($data['cvm'])) {
$xml .= ' <cvmvalue>'.$data['cvm'].'</cvmvalue>'."\n";
$xml .= ' <cvmindicator>provided</cvmindicator>'."\n";
}
$xml .= '</creditcard>'."\n";
}
if (isset($this->_payment->firstName) &&
isset($this->_payment->lastName)) {
$xml .= '<billing>'."\n";
$xml .= ' <userid>'.$this->_payment->customerId.'</userid>'."\n";
$xml .= ' <name>'.$this->_payment->firstName.' '.$this->_payment->lastName.'</name>'."\n";
$xml .= ' <company>'.$this->_payment->company.'</company>'."\n";
$xml .= ' <address1>'.$this->_payment->address.'</address1>'."\n";
$xml .= ' <city>'.$this->_payment->city.'</city>'."\n";
$xml .= ' <state>'.$this->_payment->state.'</state>'."\n";
$xml .= ' <zip>'.$this->_payment->zip.'</zip>'."\n";
$xml .= ' <country>'.$this->_payment->country.'</country>'."\n";
$xml .= ' <phone>'.$this->_payment->phone.'</phone>'."\n";
$xml .= ' <email>'.$this->_payment->email.'</email>'."\n";
$xml .= ' <addrnum>'.$this->_payment->address.'</addrnum>'."\n";
$xml .= '</billing>'."\n";
}
$xml .= '</order>'."\n";
return $xml;
}
}
/**
* Payment_Process_Result_LinkPoint
*
* LinkPoint result class
*
* @author Joe Stump <joe@joestump.net>
* @package Payment_Process
*/
class Payment_Process_Result_LinkPoint extends Payment_Process_Result
{
var $_statusCodeMap = array('APPROVED' => PAYMENT_PROCESS_RESULT_APPROVED,
'DECLINED' => PAYMENT_PROCESS_RESULT_DECLINED,
'FRAUD' => PAYMENT_PROCESS_RESULT_FRAUD);
/**
* LinkPoint status codes
*
* This array holds many of the common response codes. There are over 200
* response codes - so check the LinkPoint manual if you get a status
* code that does not match (see "Response Reason Codes & Response
* Reason Text" in the AIM manual).
*
* @see getStatusText()
* @access private
*/
var $_statusCodeMessages = array(
'APPROVED' => 'This transaction has been approved.',
'DECLINED' => 'This transaction has been declined.',
'FRAUD' => 'This transaction has been determined to be fraud.');
var $_avsCodeMap = array(
'YY' => PAYMENT_PROCESS_AVS_MATCH,
'YN' => PAYMENT_PROCESS_AVS_MISMATCH,
'YX' => PAYMENT_PROCESS_AVS_ERROR,
'NY' => PAYMENT_PROCESS_AVS_MISMATCH,
'XY' => PAYMENT_PROCESS_AVS_MISMATCH,
'NN' => PAYMENT_PROCESS_AVS_MISMATCH,
'NX' => PAYMENT_PROCESS_AVS_MISMATCH,
'XN' => PAYMENT_PROCESS_AVS_MISMATCH,
'XX' => PAYMENT_PROCESS_AVS_ERROR
);
var $_avsCodeMessages = array(
'YY' => 'Address matches, zip code matches',
'YN' => 'Address matches, zip code does not match',
'YX' => 'Address matches, zip code comparison not available',
'NY' => 'Address does not match, zip code matches',
'XY' => 'Address comparison not available, zip code matches',
'NN' => 'Address comparison does not match, zip code does not match',
'NX' => 'Address does not match, zip code comparison not available',
'XN' => 'Address comparison not available, zip code does not match',
'XX' => 'Address comparison not available, zip code comparison not available'
);
var $_cvvCodeMap = array('M' => PAYMENT_PROCESS_CVV_MATCH,
'N' => PAYMENT_PROCESS_CVV_MISMATCH,
'P' => PAYMENT_PROCESS_CVV_ERROR,
'S' => PAYMENT_PROCESS_CVV_ERROR,
'U' => PAYMENT_PROCESS_CVV_ERROR,
'X' => PAYMENT_PROCESS_CVV_ERROR
);
var $_cvvCodeMessages = array(
'M' => 'Card Code Match',
'N' => 'Card code does not match',
'P' => 'Not processed',
'S' => 'Merchant has indicated that the card code is not present on the card',
'U' => 'Issuer is not certified and/or has not proivded encryption keys',
'X' => 'No response from the credit card association was received'
);
var $_fieldMap = array('r_approved' => 'code',
'r_error' => 'message',
'r_code' => 'approvalCode',
'r_ordernum' => 'transactionId'
);
function Payment_Process_Response_LinkPoint($rawResponse)
{
$this->Payment_Process_Response($rawResponse);
}
/**
* parse
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @return void
*/
function parse()
{
$xml = new Payment_Processor_LinkPoint_XML_Parser();
$xml->parseString('<response>'.$this->_rawResponse.'</response>');
if (is_array($xml->response) && count($xml->response)) {
$this->avsCode = substr($xml->response['r_avs'],0,2);
$this->cvvCode = substr($xml->response['r_avs'],2,1);
$this->customerId = $this->_request->customerId;
$this->invoiceNumber = $this->_request->invoiceNumber;
$this->_mapFields($xml->response);
// switch to DECLINED since a duplicate isn't *really* fraud
if(eregi('duplicate',$this->message)) {
$this->messageCode = 'DECLINED';
}
}
}
}
/**
* Payment_Processor_LinkPoint_XML_Parser
*
* XML Parser for the LinkPoint response
*
* @author Joe Stump <joe@joestump.net>
* @package Payment_Process
*/
class Payment_Processor_LinkPoint_XML_Parser extends XML_Parser
{
/**
* $response
*
* @var array $response Raw response as an array
* @access public
*/
var $response = array();
/**
* $log
*
* @var string $tag Current tag
* @access private
*/
var $tag = null;
/**
* Payment_Processor_LinkPoint_XML_Parser
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @return void
* @see XML_Parser
*/
function __construct()
{
$this->XML_Parser();
}
/**
* startHandler
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @param resource $xp XML processor handler
* @param string $elem Name of XML entity
* @return void
*/
function startHandler($xp, $elem, &$attribs)
{
$this->tag = $elem;
}
/**
* endHandler
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @param resource $xp XML processor handler
* @param string $elem Name of XML entity
* @return void
*/
function endHandler($xp, $elem)
{
}
/**
* defaultHandler
*
* @author Joe Stump <joe@joestump.net>
* @access public
* @param resource $xp XML processor handler
* @param string $data
* @return void
*/
function defaultHandler($xp,$data)
{
$this->response[strtolower($this->tag)] = $data;
}
}
?>