Display Credit Card Information Submission Fields on the Checkout Payment Page
*
- @return array
*/
function selection() {
global $order;
for ($i=1; $i<13; $i++) {
$expires_month[] = array('id' => sprintf('%02d', $i), 'text' => strftime('%B - (%m)',mktime(0,0,0,$i,1,2000)));
}
$today = getdate();
for ($i=$today['year']; $i < $today['year']+15; $i++) {
$expires_year[] = array('id' => strftime('%y',mktime(0,0,0,1,1,$i)), 'text' => strftime('%Y',mktime(0,0,0,1,1,$i)));
}
$onFocus = ' onfocus="methodSelect('pmt-' . $this->code . '')"';
$selection = array('id' => $this->code,
'module' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_CATALOG_TITLE,
'fields' => array(array('title' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_CREDIT_CARD_OWNER,
'field' => zen_draw_input_field('authorizenet_aim_cc_owner', $order->billing['firstname'] . ' ' . $order->billing['lastname'], 'id="'.$this->code.'-cc-owner"'. $onFocus . ' autocomplete="off"'),
'tag' => $this->code.'-cc-owner'),
array('title' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_CREDIT_CARD_NUMBER,
'field' => zen_draw_input_field('authorizenet_aim_cc_number', '', 'id="'.$this->code.'-cc-number"' . $onFocus . ' autocomplete="off"'),
'tag' => $this->code.'-cc-number'),
array('title' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_CREDIT_CARD_EXPIRES,
'field' => zen_draw_pull_down_menu('authorizenet_aim_cc_expires_month', $expires_month, strftime('%m'), 'id="'.$this->code.'-cc-expires-month"' . $onFocus) . ' ' . zen_draw_pull_down_menu('authorizenet_aim_cc_expires_year', $expires_year, '', 'id="'.$this->code.'-cc-expires-year"' . $onFocus),
'tag' => $this->code.'-cc-expires-month')));
if (MODULE_PAYMENT_AUTHORIZENET_AIM_USE_CVV == 'True') {
$selection['fields'][] = array('title' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_CVV,
'field' => zen_draw_input_field('authorizenet_aim_cc_cvv', '', 'size="4" maxlength="4"' . ' id="'.$this->code.'-cc-cvv"' . $onFocus . ' autocomplete="off"') . ' ' . '<a href="javascript:popupWindow(\'' . zen_href_link(FILENAME_POPUP_CVV_HELP) . '\')">' . MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_POPUP_CVV_LINK . '</a>',
'tag' => $this->code.'-cc-cvv');
}
return $selection;
}
/**
- Evaluates the Credit Card Type for acceptance and the validity of the Credit Card Number & Expiration Date
*/
function pre_confirmation_check() {
global $messageStack;
include(DIR_WS_CLASSES . 'cc_validation.php');
$cc_validation = new cc_validation();
$result = $cc_validation->validate($_POST['authorizenet_aim_cc_number'], $_POST['authorizenet_aim_cc_expires_month'], $_POST['authorizenet_aim_cc_expires_year'], $_POST['authorizenet_aim_cc_cvv']);
$error = '';
switch ($result) {
case -1:
$error = sprintf(TEXT_CCVAL_ERROR_UNKNOWN_CARD, substr($cc_validation->cc_number, 0, 4));
break;
case -2:
case -3:
case -4:
$error = TEXT_CCVAL_ERROR_INVALID_DATE;
break;
case false:
$error = TEXT_CCVAL_ERROR_INVALID_NUMBER;
break;
}
if ( ($result == false) || ($result < 1) ) {
$messageStack->add_session('checkout_payment', $error . '<!-- ['.$this->code.'] -->', 'error');
zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL', true, false));
}
$this->cc_card_type = $cc_validation->cc_type;
$this->cc_card_number = $cc_validation->cc_number;
$this->cc_expiry_month = $cc_validation->cc_expiry_month;
$this->cc_expiry_year = $cc_validation->cc_expiry_year;
}
/**
- Display Credit Card Information on the Checkout Confirmation Page
- @return array
/
function confirmation() {
$confirmation = array('fields' => array(array('title' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_CREDIT_CARD_TYPE,
'field' => $this->cc_card_type),
array('title' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_CREDIT_CARD_OWNER,
'field' => $_POST['authorizenet_aim_cc_owner']),
array('title' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_CREDIT_CARD_NUMBER,
'field' => substr($this->cc_card_number, 0, 4) . str_repeat('X', (strlen($this->cc_card_number) - 8)) . substr($this->cc_card_number, -4)),
array('title' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_CREDIT_CARD_EXPIRES,
'field' => strftime('%B, %Y', mktime(0,0,0,$_POST['authorizenet_aim_cc_expires_month'], 1, '20' . $_POST['authorizenet_aim_cc_expires_year']))) ));
return $confirmation;
}
/*
- Build the data and actions to process when the "Submit" button is pressed on the order-confirmation screen.
- This sends the data to the payment gateway for processing.
- (These are hidden fields on the checkout confirmation page)
- @return string
*/
function process_button() {
$process_button_string = zen_draw_hidden_field('cc_owner', $_POST['authorizenet_aim_cc_owner']) .
zen_draw_hidden_field('cc_expires', $this->cc_expiry_month . substr($this->cc_expiry_year, -2)) .
zen_draw_hidden_field('cc_type', $this->cc_card_type) .
zen_draw_hidden_field('cc_number', $this->cc_card_number);
if (MODULE_PAYMENT_AUTHORIZENET_AIM_USE_CVV == 'True') {
$process_button_string .= zen_draw_hidden_field('cc_cvv', $_POST['authorizenet_aim_cc_cvv']);
}
$process_button_string .= zen_draw_hidden_field(zen_session_name(), zen_session_id());
return $process_button_string;
}
/**
- Store the CC info to the order and process any results that come back from the payment gateway
/
function before_process() {
global $response, $db, $order, $messageStack;
$order->info['cc_type'] = $_POST['cc_type'];
$order->info['cc_owner'] = $_POST['cc_owner'];
$order->info['cc_number'] = str_pad(substr($_POST['cc_number'], -4), strlen($_POST['cc_number']), "X", STR_PAD_LEFT);
$order->info['cc_expires'] = ''; // $_POST['cc_expires'];
$order->info['cc_cvv'] = '**';
$sessID = zen_session_id();
// DATA PREPARATION SECTION
unset($submit_data); // Cleans out any previous data stored in the variable
// Create a string that contains a listing of products ordered for the description field
$description = '';
for ($i=0; $i<sizeof($order->products); $i++) {
$description .= $order->products[$i]['name'] . ' (qty: ' . $order->products[$i]['qty'] . ') + ';
}
// Remove the last "\n" from the string
$description = substr($description, 0, -2);
// Create a variable that holds the order time
$order_time = date("F j, Y, g:i a");
// Calculate the next expected order id (adapted from code written by Eric Stamper - 01/30/2004 Released under GPL)
$last_order_id = $db->Execute("select * from " . TABLE_ORDERS . " order by orders_id desc limit 1");
$new_order_id = $last_order_id->fields['orders_id'];
$new_order_id = ($new_order_id + 1);
// add randomized suffix to order id to produce uniqueness ... since it's unwise to submit the same order-number twice to authorize.net
$new_order_id = (string)$new_order_id . '-' . zen_create_random_value(6, 'chars');
// Populate an array that contains all of the data to be sent to Authorize.net
$submit_data = array(
'x_login' => trim(MODULE_PAYMENT_AUTHORIZENET_AIM_LOGIN),
'x_tran_key' => trim(MODULE_PAYMENT_AUTHORIZENET_AIM_TXNKEY),
'x_relay_response' => 'FALSE', // AIM uses direct response, not relay response
'x_delim_data' => 'TRUE',
'x_delim_char' => $this->delimiter, // The default delimiter is a comma
'x_encap_char' => $this->encapChar, // The divider to encapsulate response fields
'x_version' => '3.1', // 3.1 is required to use CVV codes
'x_type' => MODULE_PAYMENT_AUTHORIZENET_AIM_AUTHORIZATION_TYPE == 'Authorize' ? 'AUTH_ONLY': 'AUTH_CAPTURE',
'x_method' => 'CC',
'x_amount' => number_format($order->info['total'], 2),
'x_currency_code' => $order->info['currency'],
'x_card_num' => $_POST['cc_number'],
'x_exp_date' => $_POST['cc_expires'],
'x_card_code' => $_POST['cc_cvv'],
'x_email_customer' => MODULE_PAYMENT_AUTHORIZENET_AIM_EMAIL_CUSTOMER == 'True' ? 'TRUE': 'FALSE',
'x_email_merchant' => MODULE_PAYMENT_AUTHORIZENET_AIM_EMAIL_MERCHANT == 'True' ? 'TRUE': 'FALSE',
'x_cust_id' => $_SESSION['customer_id'],
'x_invoice_num' => (MODULE_PAYMENT_AUTHORIZENET_AIM_TESTMODE == 'Test' ? 'TEST-' : '') . $new_order_id,
'x_first_name' => $order->billing['firstname'],
'x_last_name' => $order->billing['lastname'],
'x_company' => $order->billing['company'],
'x_address' => $order->billing['street_address'],
'x_city' => $order->billing['city'],
'x_state' => $order->billing['state'],
'x_zip' => $order->billing['postcode'],
'x_country' => $order->billing['country']['title'],
'x_phone' => $order->customer['telephone'],
'x_email' => $order->customer['email_address'],
'x_ship_to_first_name' => $order->delivery['firstname'],
'x_ship_to_last_name' => $order->delivery['lastname'],
'x_ship_to_address' => $order->delivery['street_address'],
'x_ship_to_city' => $order->delivery['city'],
'x_ship_to_state' => $order->delivery['state'],
'x_ship_to_zip' => $order->delivery['postcode'],
'x_ship_to_country' => $order->delivery['country']['title'],
'x_description' => $description,
'x_recurring_billing' => 'NO',
'x_customer_ip' => zen_get_ip_address(),
'x_po_num' => date('M-d-Y h:i:s'), //$order->info['po_number'],
'x_freight' => number_format((float)$order->info['shipping_cost'],2),
'x_tax_exempt' => 'FALSE', /* 'TRUE' or 'FALSE' */
'x_tax' => number_format((float)$order->info['tax'],2),
'x_duty' => '0',
'x_allow_partial_Auth' => 'FALSE', // unable to accept partial authorizations at this time
// Additional Merchant-defined variables go here
'Date' => $order_time,
'IP' => zen_get_ip_address(),
'Session' => $sessID );
// force conversion to USD
if ($order->info['currency'] != 'USD') {
global $currencies;
$submit_data['x_amount'] = number_format($order->info['total'] * $currencies->get_value('USD'), 2);
$submit_data['x_currency_code'] = 'USD';
unset($submit_data['x_tax'], $submit_data['x_freight']);
}
unset($response);
$response = $this->_sendRequest($submit_data);
$response_code = $response[0];
$response_text = $response[3];
$this->auth_code = $response[4];
$this->transaction_id = $response[6];
$this->avs_response= $response[5];
$this->ccv_response= $response[38];
$response_msg_to_customer = $response_text . ($this->commError == '' ? '' : ' Communications Error - Please notify webmaster.');
$response['Expected-MD5-Hash'] = $this->calc_md5_response($response[6], $response[9]);
$response['HashMatchStatus'] = ($response[37] == $response['Expected-MD5-Hash']) ? 'PASS' : 'FAIL';
if ($response[0] == '3' && $response[2] == '103') $response['ErrorDetails'] = 'Invalid Transaction Key in AIM configuration.';
if ($response[0] == '2' && $response[2] == '44') $response['ErrorDetails'] = 'Declined due to CVV refusal by issuing bank.';
if ($response[0] == '2' && $response[2] == '45') $response['ErrorDetails'] = 'Declined due to AVS/CVV filters.';
if ($response[0] == '2' && $response[2] == '65') $response['ErrorDetails'] = 'Declined due to custom CVV filters.';
if ($response[0] == '3' && $response[2] == '66') $response['ErrorDetails'] = 'Transaction did not meet security guideline requirements.';
if ($response[0] == '3' && $response[2] == '128') $response['ErrorDetails'] = 'Refused by customers bank.';
if ($response[0] == '2' && $response[2] == '250') $response['ErrorDetails'] = 'Transaction submitted from a blocked IP address.';
if ($response[0] == '2' && $response[2] == '251') $response['ErrorDetails'] = 'Declined by Fraud Detection Suite filter.';
if ($response[0] == '4' && in_array($response[2], array('193', '252', '253'))) {
$this->order_status = 1;
$this->transaction_id .= ' ***NOTE: Held for review by merchant.';
$response['ErrorDetails'] = 'Transaction held for review by merchant or fraud detection suite.';
}
$this->_debugActions($response, $order_time, $sessID);
// If the MD5 hash doesn't match, then this transaction's authenticity cannot be verified.
// Thus, order will be placed in Pending status
if ($response['HashMatchStatus'] != 'PASS' && defined('MODULE_PAYMENT_AUTHORIZENET_AIM_MD5HASH') && MODULE_PAYMENT_AUTHORIZENET_AIM_MD5HASH != '') {
$this->order_status = 1;
$messageStack->add_session('header', MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_AUTHENTICITY_WARNING, 'caution');
}
// If the response code is not 1 (approved) then redirect back to the payment page with the appropriate error message
if ($response_code != '1') {
$messageStack->add_session('checkout_payment', $response_msg_to_customer . ' - ' . MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_DECLINED_MESSAGE, 'error');
zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL', true, false));
}
if ($response[88] != '') {
$_SESSION['payment_method_messages'] = $response[88];
}
}
/**
- Post-process activities. Updates the order-status history data with the auth code from the transaction.
- @return boolean
/
function after_process() {
global $insert_id, $db;
$sql = "insert into " . TABLE_ORDERS_STATUS_HISTORY . " (comments, orders_id, orders_status_id, customer_notified, date_added) values (:orderComments, :orderID, :orderStatus, -1, now() )";
$sql = $db->bindVars($sql, ':orderComments', 'Credit Card payment. AUTH: ' . $this->auth_code . '. TransID: ' . $this->transaction_id . '.', 'string');
$sql = $db->bindVars($sql, ':orderID', $insert_id, 'integer');
$sql = $db->bindVars($sql, ':orderStatus', $this->order_status, 'integer');
$db->Execute($sql);
return false;
}
/*
- Build admin-page components
- @param int $zf_order_id
- @return string
/
function admin_notification($zf_order_id) {
global $db;
$output = '';
$aimdata->fields = array();
require(DIR_FS_CATALOG . DIR_WS_MODULES . 'payment/authorizenet/authorizenet_admin_notification.php');
return $output;
}
/*
- Used to display error message details
- @return array
/
function get_error() {
$error = array('title' => MODULE_PAYMENT_AUTHORIZENET_AIM_TEXT_ERROR,
'error' => stripslashes(urldecode($_GET['error'])));
return $error;
}
/*
- Check to see whether module is installed
- @return boolean
/
function check() {
global $db;
if (!isset($this->_check)) {
$check_query = $db->Execute("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_PAYMENT_AUTHORIZENET_AIM_STATUS'");
$this->_check = $check_query->RecordCount();
}
return $this->_check;
}
/*
- Install the payment module and its configuration settings
*/
function install() {
global $db, $messageStack;
if (defined('MODULE_PAYMENT_AUTHORIZENET_AIM_STATUS')) {
$messageStack->add_session('Authorize.net (AIM) module already installed.', 'error');
zen_redirect(zen_href_link(FILENAME_MODULES, 'set=payment&module=authorizenet_aim', 'NONSSL'));
return 'failed';
}
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable Authorize.net (AIM) Module', 'MODULE_PAYMENT_AUTHORIZENET_AIM_STATUS', 'True', 'Do you want to accept Authorize.net payments via the AIM Method?', '6', '0', 'zen_cfg_select_option(array('True', 'False'), ', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Login ID', 'MODULE_PAYMENT_AUTHORIZENET_AIM_LOGIN', 'testing', 'The API Login ID used for the Authorize.net service', '6', '0', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, use_function) values ('Transaction Key', 'MODULE_PAYMENT_AUTHORIZENET_AIM_TXNKEY', 'Test', 'Transaction Key used for encrypting TP data<br />(See your Authorizenet Account->Security Settings->API Login ID and Transaction Key for details.)', '6', '0', now(), 'zen_cfg_password_display')");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added, use_function) values ('MD5 Hash', 'MODULE_PAYMENT_AUTHORIZENET_AIM_MD5HASH', 'Set A Hash Value at AuthNet Admin', 'Encryption key used for validating received transaction data (MAX 20 CHARACTERS, exactly as you entered in Authorize.net account settings). Or leave blank.', '6', '0', now(), 'zen_cfg_password_display')");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Transaction Mode', 'MODULE_PAYMENT_AUTHORIZENET_AIM_TESTMODE', 'Test', 'Transaction mode used for processing orders', '6', '0', 'zen_cfg_select_option(array('Test', 'Production'), ', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Authorization Type', 'MODULE_PAYMENT_AUTHORIZENET_AIM_AUTHORIZATION_TYPE', 'Authorize', 'Do you want submitted credit card transactions to be authorized only, or authorized and captured?', '6', '0', 'zen_cfg_select_option(array('Authorize', 'Authorize+Capture'), ', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Enable Database Storage', 'MODULE_PAYMENT_AUTHORIZENET_AIM_STORE_DATA', 'True', 'Do you want to save the gateway communications data to the database?', '6', '0', 'zen_cfg_select_option(array('True', 'False'), ', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Customer Notifications', 'MODULE_PAYMENT_AUTHORIZENET_AIM_EMAIL_CUSTOMER', 'False', 'Should Authorize.Net email a receipt to the customer?', '6', '0', 'zen_cfg_select_option(array('True', 'False'), ', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Merchant Notifications', 'MODULE_PAYMENT_AUTHORIZENET_AIM_EMAIL_MERCHANT', 'False', 'Should Authorize.Net email a receipt to the merchant?', '6', '0', 'zen_cfg_select_option(array('True', 'False'), ', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, date_added) values ('Request CVV Number', 'MODULE_PAYMENT_AUTHORIZENET_AIM_USE_CVV', 'True', 'Do you want to ask the customer for the card's CVV number', '6', '0', 'zen_cfg_select_option(array('True', 'False'), ', now())");