_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 ""; foreach ($this->fields as $key => $value) { echo ""; } echo "
Field Name Value
$key".urldecode($value)." 

"; } 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 ""; $i = 0; foreach ($this->response as $key => $value) { echo ""; $i++; } echo "
Index  Field Name Value
$i $key $value 

"; } }