_errors = array();
$this->_jqErrors = array();
// some default values
$this->addField('TransactionCode', 32); //2-digit value: 30 = Authorize only, 32 = Authorize & Settle
$this->addField('InstallmentNum', '01'); //2-digit value representing the current payment being charged to customer
$this->addField('InstallmentOf', '01'); //2-digit value representing the total number of payments that'll be charged
$this->addField('eCommerce', 'Y'); //Y or N to indicate if this is from the Internet (required if so)
//set display name
$this->_displayName = "TransFirst";
//copied from transfirst_ipn stuff in event espresso plugin
//list of possible fields- - taken from eLink_CNP_User_Guide_3 7 0.pdf
#region ---------- REQUIRED ------- see pg 21 --------
$this->_requiredFields = array(
'ePayAccountNum' //8-digit number from TransFirst //formerly 'MerchantID'
, 'Password' //merchant supplied < 10-digit account password //formerly , 'RegKey'
, 'TransactionCode' //2-digit value: 30 = Authorize only, 32 = Authorize & Settle
, 'OrderNum' //up to 20-char value to identify an order (arbitrary)
, 'TransactionAmount' //####.## up to 8-digit dollar amount
, 'CardAccountNum' //credit card num
, 'ExpirationDate' //4-digit value MMYY of credit card expiration date
, 'CardHolderZip' //5- or 9-digit zipcode, required for AVS (address verification services)
, 'InstallmentNum' //2-digit value representing the current payment being charged to customer
, 'InstallmentOf' //2-digit value representing the total number of payments that'll be charged
);
#endregion ---------- REQUIRED ---------------
#region ---------- OPTIONAL ------- see pg 21 --------
$this->_optionalFields = array(
//kinda required?
'eCommerce' //Y or N to indicate if this is from the Internet (required if so)
, 'CVV2' //up to 4-char verification code (shouldn't this be required?)
, 'CardHolderIDSource' //1-digit value used to denote method for identifying customer (required outside ePay system)
//useful
, 'TestTransaction' //Y or N - testmode
, 'DuplicateChecking' //Y or N -- look for existing transaction, if so return it
//etc
, 'CardHolderName' //up to 30-char
, 'CardHolderAddress' //up to 25 char; required if participating in Response Code Monitoring Program???
, 'CardHolderCity' //up to 16-char
, 'CardHolderState' //up to 2-char
, 'CardHolderEmail' //up to 30-char
, 'CardHolderPhone' //up to 10-char
, 'CustomerNum' //up to 20-char identification, assigned by merchant (arbitrary?)
, 'Misc1' //up to 20-char for miscellaneous data to pass to TransLink
, 'Misc2' //up to 20-char for miscellaneous data to pass to TransLink
//other fields exist in list....but ignoring for now
);
#endregion ---------- OPTIONAL ---------------
$this->_possibleFields = array_merge($this->_requiredFields, $this->_optionalFields);
#region ---------- Authorization Response Message ---------------
//as given on pg 23-24 of eLink doc
$this->_authResponseKeys = array(
'MessageFormat' //ePay internal message format = "R1"
,'ePayAccountNum' //account number (assigned by TransFirst)
,'TransactionCode' //transaction code from the original request message
,'SequenceCode' //currently zero-filled
,'MailOrderIdentifier' //always 1
,'AccountNum' //credit card account number - from original request
,'ExpirationDate' //4-digit MMYY, from request
,'AuthorizedAmount' //8-digit amount as passed to ePay, but returned without the decimal
,'AuthorizationDate' //6-digit value for date auth attempted (YYMMDD)
,'AuthorizationTime' //6-digit value for time auth attempted (HHMMSS)
,'TransactionStatus' //2-digit value of result code -- see Appendix A
,'CustomerNum' //20-digit customer number (from request)
,'OrderNum' //20-digit number (from request)
,'ReferenceNum' //unique 14-digit number from eLink to identify individual transaction
,'AuthorizationResponseCode' //6-digit from issuing bank indicating valid and reserved funds
,'AuthorizationSource' //1-digit defines where auth captured - see Appendix B
,'AuthorizationCharacteristicIndicator' //1-digit defines qualification level of auth - see Appendix C
,'TransactionID' //15-digit from Visa/Mastercard to identify+link related details for transaction
,'ValidationCode' //4-digit number assigned by VIP system to determine accuracy of auth data
,'SICCatCode' //4-digit merchant industry classification
,'CountryCode' //3-digit -- always 840 to indicate US Currency (at this time)
,'AVSResponseCode' //1-digit level of Address Verification - see Appendix D
,'MerchantStoreNum' //8-digit value to identify specific terminal
,'CVV2ResponseCode' //1-digit result of card verification - see Appendix F
,'CAVVCODE' //1-digit result code for CAVV value passed - present = Y, not = ""
,'CrossReferenceNum' //14-digit unique eLink number to associate with subsequent transactions
,'ExtendedTransactionStatus' //3-digit indicates result of auth request -- auth, declined, timeout - see Appendix A
,'CAVVResponseCode' //1-digit response code for result of CAVV request - Appendix G
,'XID' //40-char value passed in original request
,'ECIValue' //2-digit value indicates type fo eCommerce Transaction - see Appendix H
);
$this->_authSuccessCodes = array( 0, 11 ); //Appendix A
#endregion ---------- Authorization Response Message ---------------
}
/**
* Return an array of accepted credit card types where the keys are the diplay values and the values are the gateway values
*
* @return array
*/
public function getCreditCardTypes() {
$cardTypes = array();
$setting = new Cart66Setting();
$cards = Cart66Setting::getValue('auth_card_types');
if($cards) {
$cards = explode('~', $cards);
if(in_array('mastercard', $cards)) {
$cardTypes['MasterCard'] = 'mastercard';
}
if(in_array('visa', $cards)) {
$cardTypes['Visa'] = 'visa';
}
if(in_array('amex', $cards)) {
$cardTypes['American Express'] = 'amex';
}
if(in_array('discover', $cards)) {
$cardTypes['Discover'] = 'discover';
}
}
return $cardTypes;
}
public function addField($field, $value) {
if(in_array($key, $this->_possibleFields)){
$this->fields[$field] = $value;
}
}
public function initCheckout($total) {
$p = $this->getPayment();
$b = $this->getBilling();
///TODO - sanitize credit-card details
Cart66Common::log("Payment info for checkout: " . print_r($p, true));
// Load gateway url from Cart66 settings
$gatewayUrl = Cart66Setting::getValue('auth_url');
if($gatewayUrl == 'other') {
$gatewayUrl = Cart66Setting::getValue('auth_url_other');
}
$this->gateway_url = $gatewayUrl;
$mapFields = array(
'CardHolderName' => sprintf('%s %s', $b['firstName'], $b['lastName'])
, 'TransactionAmount' => number_format($total,2, '.', '') //####.## up to 8-digit dollar amount
, 'CardHolderAddress' => $b['address'] //substr( sprintf('%s %s', $address, $address2), 0, 25 )
, 'CardHolderEmail' => $p['email'] //substr($attendee_email, 0, 30)
, 'CardHolderCity' => $b['city'] //substr( $city, 0, 16 )
, 'CardHolderState' => $b['state'] //substr($state, 0, 2)
, 'CardHolderZip' => $b['zip'] //5- or 9-digit zipcode, required for AVS (address verification services)
//post fields
, 'OrderNum' => Cart66Common::getRandString() //substr( $_POST['order-num'], 0, 20 ) //up to 20-char value to identify an order (arbitrary)
//arbitrary or mandatory fields
, 'ePayAccountNum' => Cart66Setting::getValue('auth_username') //8-digit number from TransFirst //formerly 'MerchantID'
, 'Password' => Cart66Setting::getValue('auth_trans_key') //merchant supplied < 10-digit account password //formerly , 'RegKey'
/* already set in init
, 'TransactionCode' => 32 //2-digit value: 30 = Authorize only, 32 = Authorize & Settle
, 'InstallmentNum' => '01' //2-digit value representing the current payment being charged to customer
, 'InstallmentOf' => '01' //2-digit value representing the total number of payments that'll be charged
, 'eCommerce' => 'Y' //Y or N to indicate if this is from the Internet (required if so)
*/
, 'CardAccountNum' => $p['cardNumber'] //credit card num
, 'ExpirationDate' => sprintf('%02d%02d', $p['cardExpirationMonth'], substr($p['cardExpirationYear'], -2)) //4-digit value MMYY of credit card expiration date
//other optional fields
, 'CardHolderPhone' => $p['phone']
, 'CVV2' => $p['securityId']
);
//now loop and add 'em
foreach($mapFields as $key => $value){
$this->addField($key, $value);
}
}//---- function initCheckout
/**
* Split the Gateway response into meaningful data
*/
public function parseResponse(){
$authResponseValues = explode('|', $this->response_string);
//push the data back on to the internal storage
//combine available values from response?? lengths don't match
$i = -1;
while( ++$i < count($authResponseValues) ){
$this->response[ $this->_authResponseKeys[$i] ] = $authResponseValues[$i];
}
# $this->response = array_combine( $this->_authResponseKeys, $authResponseValues );
if(in_array( intval( $this->response['TransactionStatus'] ), $this->_authSuccessCodes ) ){
$reason = "Card Accepted Successfully."; //where is this saved?
array_push( $this->response['Response Reason Text'], $reason);
// Valid IPN transaction.
#$this->logResults(true);
return true;
}
else{
$reason = 'Card Declined: '.self::gatewayResponseDescription($this->response['TransactionStatus']); //where is this saved?
// Invalid IPN transaction. Check the log for details.
array_push($this->response['Response Reason Text'], "IPN Validation Failed -- $reason");
return false;
}
}//---- function parseResponse
function doSale() {
// This function actually processes the payment. This function will
// load the $response array with all the returned information.
// The response code values are:
// 1 - Approved
// 2 - Declined
// 3 - Error
$sale = false;
if($this->fields[$this->_amountFieldKey] > 0) {
// Construct the fields string to pass to authorize.net
/*
foreach( $this->fields as $key => $value ) {
$this->field_string .= "$key=" . urlencode( $value ) . "&";
}
*/
$this->field_string = http_build_query( $this->fields );
// Execute the HTTPS post via CURL
$ch = curl_init($this->gateway_url);
curl_setopt($ch, CURLOPT_HEADER, 0);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, rtrim( $this->field_string, "& " ));
// Do not worry about checking for SSL certs
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
$this->response_string = urldecode(curl_exec($ch));
$this->response['Response Reason Text'] = array();
if (curl_errno($ch)) {
array_push($this->response['Response Reason Text'], curl_error($ch));
}
else {
curl_close ($ch);
}
//figure out if it worked or not
$success = $this->parseResponse();
//mash up responses
$this->response['Response Reason Text'] = implode('
', $this->response['Response Reason Text']);
// Prepare to return the transaction id for this sale.
if($success){
$sale = $this->response['ReferenceNum']; //originally 'Transaction ID' for auth.net
//could also be OrderNum, TransactionID, CrossReferenceNum?
$this->response['Transaction ID'] = $sale;
}
}
// Process free orders without sending to the Auth.net gateway
else {
$this->response['Transaction ID'] = 'MT-' . $this->fields['OrderNum'];
$sale = $this->response['Transaction ID'];
}
return $sale;
}
function getResponseReasonText() {
return $this->response['Response Reason Text'];
}
function getTransactionId() {
return $this->response['Transaction ID'];
}
public function getTransactionResponseDescription() {
$description = $this->getResponseReasonText();
$this->_logFields();
$this->_logResponse();
return $description;
}
protected function _logResponse() {
$out = __CLASS__ . " Response Log\n";
foreach ($this->response as $key => $value) {
$out .= "\t$key = $value\n";
}
Cart66Common::log('[' . str_replace($_SERVER['DOCUMENT_ROOT'], '', __FILE__) . ' - line ' . __LINE__ . "] $out");
}
protected function _logFields() {
///TODO - sanitize credit-card details
$out = "Authorize.net Field Log\n";
foreach ($this->fields as $key => $value) {
$out .= "\t$key = $value\n";
}
Cart66Common::log('[' . basename(__FILE__) . ' - line ' . __LINE__ . "] $out");
}
function dumpFields() {
// Used for debugging, this function will output all the field/value pairs
// that are currently defined in the instance of the class using the
// add_field() function.
echo "
".__CLASS__."->dump_fields() Output:
";
echo "
Field Name |
Value |
";
foreach ($this->fields as $key => $value) {
echo "$key | ".urldecode($value)." |
";
}
echo "
";
}
function dumpResponse() {
// Used for debugging, this function will output all the response field
// names and the values returned for the payment submission. This should
// be called AFTER the process() function has been called to view details
// about authorize.net's response.
echo "".__CLASS__."_class->dump_response() Output:
";
echo "
Index |
Field Name |
Value |
";
$i = 0;
foreach ($this->response as $key => $value) {
echo "
$i |
$key |
$value |
";
$i++;
}
echo "
";
}
}