code = 'linkpoint_api';
$this->enabled = ((MODULE_PAYMENT_LINKPOINT_API_STATUS == 'True') ? true : false); // Whether the module is installed or not
if ($_GET['main_page'] != '' && !IS_ADMIN_FLAG === true) {
$this->title = MODULE_PAYMENT_LINKPOINT_API_TEXT_CATALOG_TITLE; // Payment module title in Catalog
} else {
$this->title = MODULE_PAYMENT_LINKPOINT_API_TEXT_ADMIN_TITLE; // Payment module title in Admin
if ($this->enabled && !function_exists('curl_init')) $messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_ERROR_CURL_NOT_FOUND, 'error');
}
$this->description = MODULE_PAYMENT_LINKPOINT_API_TEXT_DESCRIPTION; // Descriptive Info about module in Admin
$this->sort_order = MODULE_PAYMENT_LINKPOINT_API_SORT_ORDER; // Sort Order of this payment option on the customer payment page
$this->form_action_url = zen_href_link(FILENAME_CHECKOUT_PROCESS, '', 'SSL', false); // Page to go to upon submitting page info
$this->order_status = (int)DEFAULT_ORDERS_STATUS_ID;
if ((int)MODULE_PAYMENT_LINKPOINT_API_ORDER_STATUS_ID > 0) {
$this->order_status = MODULE_PAYMENT_LINKPOINT_API_ORDER_STATUS_ID;
}
if (MODULE_PAYMENT_LINKPOINT_API_AUTHORIZATION_MODE == 'Authorize Only' && (int)MODULE_PAYMENT_LINKPOINT_API_PREAUTH_ORDER_STATUS_ID > 0) {
$this->order_status = MODULE_PAYMENT_LINKPOINT_API_PREAUTH_ORDER_STATUS_ID;
}
$this->zone = (int)MODULE_PAYMENT_LINKPOINT_API_ZONE;
if (is_object($order)) $this->update_status();
$this->code_debug = (MODULE_PAYMENT_LINKPOINT_API_CODE_DEBUG=='debug') ? true : false;
// set error messages if misconfigured
if (MODULE_PAYMENT_LINKPOINT_API_STATUS == 'True') {
if (MODULE_PAYMENT_LINKPOINT_API_LOGIN == 'EnterYourStoreNumber') {
$this->title .= MODULE_PAYMENT_LINKPOINT_API_TEXT_NOT_CONFIGURED;
} elseif (MODULE_PAYMENT_LINKPOINT_API_LOGIN != '' && !file_exists(DIR_FS_CATALOG . DIR_WS_MODULES . 'payment/linkpoint_api/' . MODULE_PAYMENT_LINKPOINT_API_LOGIN . '.pem') ) {
$this->title .= MODULE_PAYMENT_LINKPOINT_API_TEXT_PEMFILE_MISSING;
} elseif (MODULE_PAYMENT_LINKPOINT_API_TRANSACTION_MODE_RESPONSE != 'LIVE: Production') {
$this->title .= MODULE_PAYMENT_LINKPOINT_API_TEXT_TEST_MODE;
}
}
$this->_logDir = defined('DIR_FS_LOGS') ? DIR_FS_LOGS : DIR_FS_SQL_CACHE;
}
// class methods
function update_status() {
global $order, $db;
// if store is not running in SSL, cannot offer credit card module, for PCI reasons
if (!defined('ENABLE_SSL') || ENABLE_SSL != 'true') $this->enabled = FALSE;
// check other reasons for the module to be deactivated:
if ( $this->enabled && $this->zone > 0 ) {
$check_flag = false;
$sql = "SELECT zone_id
FROM " . TABLE_ZONES_TO_GEO_ZONES . "
WHERE geo_zone_id = :zoneId
AND zone_country_id = :countryId
ORDER BY zone_id";
$sql = $db->bindVars($sql, ':zoneId', $this->zone, 'integer');
$sql = $db->bindVars($sql, ':countryId', $order->billing['country']['id'], 'integer');
$check = $db->Execute($sql);
while (!$check->EOF) {
if ($check->fields['zone_id'] < 1) {
$check_flag = true;
break;
} elseif ($check->fields['zone_id'] == $order->billing['zone_id']) {
$check_flag = true;
break;
}
$check->MoveNext();
}
if (!$check_flag) {
$this->enabled = false;
}
}
// if in code-debug mode and IP address is in the down-for-maint list, enable the module (leaves it invisible to non-testers)
if (strstr(EXCLUDE_ADMIN_IP_FOR_MAINTENANCE, $_SERVER['REMOTE_ADDR'])) {
if ($this->code_debug) $this->enabled=true;
}
}
// Validate the credit card information via javascript (Number, Owner, and CVV Lengths)
function javascript_validation() {
$js = ' if (payment_value == "' . $this->code . '") {' . "\n" .
' var cc_owner = document.checkout_payment.linkpoint_api_cc_owner.value;' . "\n" .
' var cc_number = document.checkout_payment.linkpoint_api_cc_number.value;' . "\n" .
' var cc_cvv = document.checkout_payment.linkpoint_api_cc_cvv.value;' . "\n" .
' if (cc_owner == "" || cc_owner.length < ' . CC_OWNER_MIN_LENGTH . ') {' . "\n" .
' error_message = error_message + "' . MODULE_PAYMENT_LINKPOINT_API_TEXT_JS_CC_OWNER . '";' . "\n" .
' error = 1;' . "\n" .
' }' . "\n" .
' if (cc_number == "" || cc_number.length < ' . CC_NUMBER_MIN_LENGTH . ') {' . "\n" .
' error_message = error_message + "' . MODULE_PAYMENT_LINKPOINT_API_TEXT_JS_CC_NUMBER . '";' . "\n" .
' error = 1;' . "\n" .
' }' . "\n" .
' if (cc_cvv == "" || cc_cvv.length < "3") {' . "\n".
' error_message = error_message + "' . MODULE_PAYMENT_LINKPOINT_API_TEXT_JS_CC_CVV . '";' . "\n" .
' error = 1;' . "\n" .
' }' . "\n" .
' }' . "\n";
return $js;
}
// Display Credit Card Information Submission Fields on the Checkout Payment Page
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' => $this->title,
'fields' => array(array('title' => MODULE_PAYMENT_LINKPOINT_API_TEXT_CREDIT_CARD_OWNER,
'field' => zen_draw_input_field('linkpoint_api_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_LINKPOINT_API_TEXT_CREDIT_CARD_NUMBER,
'field' => zen_draw_input_field('linkpoint_api_cc_number', $ccnum, 'id="'.$this->code.'-cc-number"' . $onFocus . ' autocomplete="off"'),
'tag' => $this->code.'-cc-number'),
array('title' => MODULE_PAYMENT_LINKPOINT_API_TEXT_CREDIT_CARD_EXPIRES,
'field' => zen_draw_pull_down_menu('linkpoint_api_cc_expires_month', $expires_month, strftime('%m'), 'id="'.$this->code.'-cc-expires-month"' . $onFocus) . ' ' . zen_draw_pull_down_menu('linkpoint_api_cc_expires_year', $expires_year, '', 'id="'.$this->code.'-cc-expires-year"' . $onFocus),
'tag' => $this->code.'-cc-expires-month'),
array('title' => MODULE_PAYMENT_LINKPOINT_API_TEXT_CVV,
'field' => zen_draw_input_field('linkpoint_api_cc_cvv', '', 'size="4" maxlength="4"'. ' id="'.$this->code.'-cc-cvv"' . $onFocus . ' autocomplete="off"') . ' ' . '' . MODULE_PAYMENT_LINKPOINT_API_TEXT_POPUP_CVV_LINK . '',
'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 $db, $messageStack;
include(DIR_WS_CLASSES . 'cc_validation.php');
$cc_validation = new cc_validation();
$result = $cc_validation->validate($_POST['linkpoint_api_cc_number'], $_POST['linkpoint_api_cc_expires_month'], $_POST['linkpoint_api_cc_expires_year']);
$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;
}
// save record of errors which occur during checkout_payment validation phase but haven't been sent to gateway yet
if ( ($result == false) || ($result < 1) ) {
$payment_error_return = 'payment_error=' . $this->code ;
$error_info2 = '&error=' . urlencode($error) . '&linkpoint_api_cc_owner=' . urlencode($_POST['linkpoint_api_cc_owner']) . '&linkpoint_api_cc_expires_month=' . $_POST['linkpoint_api_cc_expires_month'] . '&linkpoint_api_cc_expires_year=' . $_POST['linkpoint_api_cc_expires_year'];
$messageStack->add_session('checkout_payment', $error . '', 'error');
if (MODULE_PAYMENT_LINKPOINT_API_STORE_DATA == 'True') {
$cc_type = $cc_validation->cc_type;
$cc_number_clean = $cc_validation->cc_number;
$cc_expiry_month = $_POST['linkpoint_api_cc_expires_month'];
$cc_expiry_year = $_POST['linkpoint_api_cc_expires_year'];
$error_returned = $payment_error_return . $error_info2;
$cc_number = (strlen($cc_number_clean) > 8) ? substr($cc_number_clean, 0, 4) . str_repeat('X', (strlen($cc_number_clean) - 8)) . substr($cc_number_clean, -4) : substr($cc_number_clean, 0, 3) . '**short**';
while (strstr($error_returned, '%3A')) $error_returned = str_replace('%3A', ' ', $error_returned);
while (strstr($error_returned, '%2C')) $error_returned = str_replace('%2C', ' ', $error_returned);
while (strstr($error_returned, '+')) $error_returned = str_replace('+', ' ', $error_returned);
$error_returned = str_replace('&', ' &', $error_returned);
$cust_info = $error_returned;
$message = addslashes($message);
$cust_info = addslashes($cust_info);
$all_response_info = addslashes($all_response_info);
// Store Transaction history in Database
$sql_data_array= array(array('fieldName'=>'lp_trans_num', 'value'=>'', 'type'=>'string'),
array('fieldName'=>'order_id', 'value'=>0, 'type'=>'integer'),
array('fieldName'=>'approval_code', 'value'=>'N/A', 'type'=>'string'),
array('fieldName'=>'transaction_response_time', 'value'=>'N/A', 'type'=>'string'),
array('fieldName'=>'r_error', 'value'=>'**CC Info Failed Validation during pre-processing**', 'type'=>'string'),
array('fieldName'=>'customer_id', 'value'=>$_SESSION['customer_id'] , 'type'=>'integer'),
array('fieldName'=>'avs_response', 'value'=>'', 'type'=>'string'),
array('fieldName'=>'transaction_result', 'value'=>'*CUSTOMER ERROR*', 'type'=>'string'),
array('fieldName'=>'message', 'value'=>$message . ' -- ' . $all_response_info, 'type'=>'string'),
array('fieldName'=>'transaction_time', 'value'=>time(), 'type'=>'string'),
array('fieldName'=>'transaction_reference_number', 'value'=>'', 'type'=>'string'),
array('fieldName'=>'fraud_score', 'value'=>0, 'type'=>'integer'),
array('fieldName'=>'cc_number', 'value'=>$cc_number, 'type'=>'string'),
array('fieldName'=>'cust_info', 'value'=>$cust_info, 'type'=>'string'),
array('fieldName'=>'chargetotal', 'value'=>0, 'type'=>'string'),
array('fieldName'=>'cc_expire', 'value'=>$cc_month . '/' . $cc_year, 'type'=>'string'),
array('fieldName'=>'ordertype', 'value'=>'N/A', 'type'=>'string'),
array('fieldName'=>'date_added', 'value'=>'now()', 'type'=>'noquotestring'));
$db->perform(TABLE_LINKPOINT_API, $sql_data_array);
}
zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL', true, false));
}
// if no error, continue with validated data:
$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
function confirmation() {
$confirmation = array('title' => $this->title . ': ' . $this->cc_card_type,
'fields' => array(array('title' => MODULE_PAYMENT_LINKPOINT_API_TEXT_CREDIT_CARD_OWNER,
'field' => $_POST['linkpoint_api_cc_owner']),
array('title' => MODULE_PAYMENT_LINKPOINT_API_TEXT_CREDIT_CARD_NUMBER,
'field' => str_repeat('X', (strlen($this->cc_card_number) - 4)) . substr($this->cc_card_number, -4)),
array('title' => MODULE_PAYMENT_LINKPOINT_API_TEXT_CREDIT_CARD_EXPIRES,
'field' => strftime('%B, %Y', mktime(0,0,0,$_POST['linkpoint_api_cc_expires_month'], 1, '20' . $_POST['linkpoint_api_cc_expires_year'])))));
return $confirmation;
}
/**
* Prepare the hidden fields comprising the parameters for the Submit button on the checkout confirmation page
*/
function process_button() {
// These are hidden fields on the checkout confirmation page
$process_button_string = zen_draw_hidden_field('cc_owner', $_POST['linkpoint_api_cc_owner']) .
zen_draw_hidden_field('cc_expires', $this->cc_expiry_month . substr($this->cc_expiry_year, -2)) .
zen_draw_hidden_field('cc_expires_month', $this->cc_expiry_month) .
zen_draw_hidden_field('cc_expires_year', 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) .
zen_draw_hidden_field('cc_cvv', $_POST['linkpoint_api_cc_cvv']);
$process_button_string .= zen_draw_hidden_field(zen_session_name(), zen_session_id());
return $process_button_string;
}
/**
* Prepare and submit the authorization to the gateway
*/
function before_process() {
global $order, $order_totals, $db, $messageStack, $lp_avs, $lp_trans_num;
$myorder = array();
// Calculate the next expected order id
$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 the gateway
$new_order_id = (string)$new_order_id . '-' . zen_create_random_value(6);
// Build Info to send to Gateway
$myorder["result"] = "LIVE";
switch (MODULE_PAYMENT_LINKPOINT_API_TRANSACTION_MODE_RESPONSE) {
case "TESTING: Successful": $myorder["result"] = "GOOD"; break;
case "TESTING: Decline" : $myorder["result"] = "DECLINE"; break;
case "TESTING: Duplicate" : $myorder["result"] = "DUPLICATE"; break;
}
// "oid" - Order ID number must be unique. If not set, gateway will assign one.
//$oid = zen_create_random_value(16, 'digits'); // Create a UID for the order
$myorder["oid"] = $new_order_id; //""; // time(); ????
// prepare totals for submission
$surcharges = 0;
$creditsApplied = 0;
global $order_totals;
reset($order_totals);
$myorder['subtotal'] = $myorder['tax'] = $myorder['shipping'] = $myorder['chargetotal'] = 0;
for ($i=0, $n=sizeof($order_totals); $i<$n; $i++) {
if ($order_totals[$i]['code'] == '') continue;
if (in_array($order_totals[$i]['code'], array('ot_total','ot_subtotal','ot_tax','ot_shipping'))) {
if ($order_totals[$i]['code'] == 'ot_subtotal') $myorder["subtotal"] = round($order_totals[$i]['value'],2);
if ($order_totals[$i]['code'] == 'ot_tax') $myorder["tax"] += round($order_totals[$i]['value'],2);
if ($order_totals[$i]['code'] == 'ot_shipping') $myorder["shipping"] = round($order_totals[$i]['value'],2);
if ($order_totals[$i]['code'] == 'ot_total') $myorder["chargetotal"] = round($order_totals[$i]['value'],2);
} else {
global $$order_totals[$i]['code'];
if (substr($order_totals[$i]['text'], 0, 1) == '-' || (isset($$order_totals[$i]['code']->credit_class) && $$order_totals[$i]['code']->credit_class == true)) {
$creditsApplied += round($order_totals[$i]['value'],2);
} else {
$surcharges += round($order_totals[$i]['value'],2);
}
}
}
foreach(array('subtotal', 'tax', 'chargetotal', 'shipping') as $i) {
if (isset($myorder[$i])) $myorder[$i] = number_format($myorder[$i], 2, '.', '');
}
if ($surcharges == 0 && $creditsApplied == 0 && $order->info['total'] >= $order->info['subtotal'] && sizeof($order->products) <= 20) {
// itemized contents
$num_line_items = 0;
reset($order->products);
for ($i=0, $n=sizeof($order->products); $i<$n; $i++) {
$num_line_items++;
$myorder["items"][$num_line_items]['id'] = $order->products[$i]['id'];
$myorder["items"][$num_line_items]['description'] = substr(htmlentities($order->products[$i]['name'], ENT_QUOTES, 'UTF-8'), 0, 128);
$myorder["items"][$num_line_items]['quantity'] = $order->products[$i]['qty'];
$myorder["items"][$num_line_items]['price'] = number_format(zen_add_tax($order->products[$i]['final_price'], $order->products[$i]['tax']), 2, '.', '');
// check and adjust for fractional quantities, which cannot be submitted as line-item details
$q = $order->products[$i]['qty']; $q1 = strval($q); $q2 = (int)$q; $q3 = strval($q2);
if ($q1 != $q3 || $myorder["items"][$num_line_items]['quantity'] * $myorder["items"][$num_line_items]['price'] != number_format($order->products[$i]['qty'] * $order->products[$i]['final_price'], 2, '.', '')) {
$myorder["items"][$num_line_items]['quantity'] = 1;
$myorder["items"][$num_line_items]['price'] = number_format(zen_round(zen_add_tax($order->products[$i]['final_price'], $order->products[$i]['tax']), $decimals) * $order->products[$i]['qty'], 2, '.', '');
$myorder["items"][$num_line_items]['description'] = '(' . $order->products[$i]['qty'] . ' x )' . substr($myorder["items"][$num_line_items]['description'], 115);
}
if (isset($order->products[$i]['attributes'])) {
$options_text_length = 0;
for ($j=0, $m=sizeof($order->products[$i]['attributes']); $j<$m; $j++) {
$options_text_length += strlen($order->products[$i]['attributes'][$j]['option'] . $order->products[$i]['attributes'][$j]['value']);
}
if ($options_text_length < 128) {
for ($j=0, $m=sizeof($order->products[$i]['attributes']); $j<$m; $j++) {
$myorder["items"][$num_line_items]['options' . $j]['name'] = substr(htmlentities($order->products[$i]['attributes'][$j]['option'], ENT_QUOTES, 'UTF-8'), 0, 128);
$myorder["items"][$num_line_items]['options' . $j]['value'] = substr(htmlentities($order->products[$i]['attributes'][$j]['value'], ENT_QUOTES, 'UTF-8'), 0, 128);
}
}
}
// track one-time charges
if ($order->products[$i]['onetime_charges'] != 0 ) {
$num_line_items++;
$myorder["items"][$num_line_items]['id'] = 'OTC';
$myorder["items"][$num_line_items]['description'] = 'One Time Charges';
$myorder["items"][$num_line_items]['quantity'] = 1;
$myorder["items"][$num_line_items]['price'] = number_format(zen_add_tax($order->products[$i]['onetime_charges'], $order->products[$i]['tax']), 2, '.', '');
}
}
/*
// deal with surcharges/fees
$num_line_items++;
$myorder["items"][$num_line_items]['id'] = 'Surcharge';
$myorder["items"][$num_line_items]['description'] = $order_totals[$i]['title'];
$myorder["items"][$num_line_items]['quantity'] = 1;
$myorder["items"][$num_line_items]['price'] = number_format($order_totals[$i]['value'], 2, '.', '');
$myorder["subtotal"] += $surcharges;
*/
// FirstData can't accept more than 20 line-item submissions per transaction
if ($num_line_items > 20) {
unset($myorder["items"]);
$num_line_items = 0;
}
// Verify that the line-item math works
for ($i=1, $n=$num_line_items; $i <= $n; $i++) {
$sum2 += ($myorder["items"][$i]['quantity'] * $myorder["items"][$i]['price']);
}
if (strval($sum2) != strval($myorder['subtotal'])) {
unset($myorder['items']);
$num_line_items = 0;
}
}
// Subtotal Sanity Check in case there are addon modules affecting calculations
$sum1 = strval($myorder['subtotal'] + $myorder['shipping'] + $myorder['tax']);
if ($sum1 > $myorder['chargetotal']) {
foreach(array('subtotal', 'tax', 'shipping', 'items') as $i) {
if (isset($myorder[$i])) unset($myorder[$i]);
}
} elseif ($sum1 < $myorder['chargetotal']) {
if ($num_line_items > 0 && $num_line_items < 20 && isset($myorder['items'])) {
$num_line_items++;
$myorder["items"][$num_line_items]['id'] = 'Adj';
$myorder["items"][$num_line_items]['description'] = 'Rounding Adjustment';
$myorder["items"][$num_line_items]['quantity'] = 1;
$myorder["items"][$num_line_items]['price'] = number_format($myorder['chargetotal'] - $sum1, 2, '.', '');
$myorder['subtotal'] += round($myorder['chargetotal'] - $sum1, 2);
$myorder['subtotal'] = number_format($myorder['subtotal'], 2, '.', '');
} else {
foreach(array('subtotal', 'tax', 'shipping', 'items') as $i) {
if (isset($myorder[$i])) unset($myorder[$i]);
}
}
}
// clean up zeros
foreach(array('subtotal', 'tax', 'shipping') as $i) {
if (isset($myorder[$i]) && $myorder[$i] == 0) unset($myorder[$i]);
}
$myorder["ip"] = current(explode(':', str_replace(',', ':', zen_get_ip_address())));
$myorder["ponumber"] = "";
// CARD INFO
$myorder["cardnumber"] = $_POST['cc_number'];
$myorder["cardexpmonth"] = $_POST['cc_expires_month'];
$myorder["cardexpyear"] = $_POST['cc_expires_year'];
$myorder["cvmindicator"] = "provided";
$myorder["cvmvalue"] = $_POST['cc_cvv'];
// BILLING INFO
$myorder["userid"] = $_SESSION['customer_id'];
$myorder["customerid"] = $_SESSION['customer_id'];
$myorder["name"] = htmlentities($_POST['cc_owner'], ENT_QUOTES, 'UTF-8'); //$order->billing['firstname'] . ' ' . $order->billing['lastname']);
$myorder["company"] = htmlentities($order->billing['company'], ENT_QUOTES, 'UTF-8');
$myorder["address1"] = htmlentities($order->billing['street_address'], ENT_QUOTES, 'UTF-8');
$myorder["address2"] = htmlentities($order->billing['suburb'], ENT_QUOTES, 'UTF-8');
$myorder["city"] = $order->billing['city'];
$myorder["state"] = $order->billing['state'];
$myorder["country"] = $order->billing['country']['iso_code_2'];
$myorder["phone"] = $order->customer['telephone'];
//$myorder["fax"] = $order->customer['fax'];
$myorder["email"] = $order->customer['email_address'];
$myorder["addrnum"] = $order->billing['street_address']; // Required for AVS. If not provided, transactions will downgrade.
$myorder["zip"] = $order->billing['postcode']; // Required for AVS. If not provided, transactions will downgrade.
// SHIPPING INFO
$myorder["sname"] = htmlentities($order->delivery['firstname'] . ' ' . $order->delivery['lastname'], ENT_QUOTES, 'UTF-8');
$myorder["saddress1"] = htmlentities($order->delivery['street_address'], ENT_QUOTES, 'UTF-8');
$myorder["saddress2"] = htmlentities($order->delivery['suburb'], ENT_QUOTES, 'UTF-8');
$myorder["scity"] = $order->delivery['city'];
$myorder["sstate"] = $order->delivery['state'];
$myorder["szip"] = $order->delivery['postcode'];
$myorder["scountry"] = $order->delivery['country']['iso_code_2'];
// MISC
$myorder["comments"] = "Website Order";
// $myorder["referred"] = "";
$myorder["ordertype"] = (MODULE_PAYMENT_LINKPOINT_API_AUTHORIZATION_MODE == 'Authorize Only' ? 'PREAUTH': 'SALE');
$this->payment_status = $myorder["ordertype"];
// send request to gateway
$result = $this->_sendRequest($myorder);
// alert to customer if communication failure
if (!is_array($result)) {
$messageStack->add_session('checkout_payment', MODULE_PAYMENT_LINKPOINT_API_TEXT_FAILURE_MESSAGE, 'error');
zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL', true, false));
}
// resubmit without subtotals if subtotal error occurs
if ($result["r_approved"] != "APPROVED" && !($result["r_approved"] == "SUBMITTED" && $result["r_message"] == 'APPROVED')) {
if (in_array(substr($result['r_error'],0,10), array('SGS-002301', 'SGS-010503', 'SGS-005003'))) {
foreach(array('items', 'subtotal', 'tax', 'shipping') as $i) {
if (isset($myorder[$i])) unset($myorder[$i]);
}
$myorder["oid"] .= '-b';
$myorder["chargetotal"] = ($myorder["chargetotal"] - 0.01);
$result = $this->_sendRequest($myorder);
if (!is_array($result)) {
$messageStack->add_session('checkout_payment', MODULE_PAYMENT_LINKPOINT_API_TEXT_FAILURE_MESSAGE, 'error');
zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL', true, false));
}
}
}
// PARSE Results
$all_response_info = '';
foreach($result as $key=>$value) {
$all_response_info .= ' ' .$key . '='.$value;
}
if ($this->code_debug) $messageStack->add_session('header', $all_response_info, 'caution');
$chargetotal = $myorder["chargetotal"];
// prepare transaction logging info
$cust_info = '';
$cc_number = substr($myorder["cardnumber"], 0, 4) . str_repeat('X', abs(strlen($myorder["cardnumber"]) - 8)) . substr($myorder["cardnumber"], -4);
foreach($myorder as $key=>$value) {
if ($key != 'cardnumber') {
if ($key == 'cvmvalue') {
$value = '****';
}
if ($key == 'cardexpmonth') {
$cc_month = $value;
}
if ($key == 'cardexpyear') {
$cc_year = $value;
}
if (is_array($value)) $value = print_r($value, true);
if (!in_array($key, array('keyfile', 'configfile', 'transactionorigin', 'terminaltype', 'host', 'port'))) $cust_info .= ' ' .$key . '=' . $value . ';';
} else {
$cust_info .= ' ' .$key . '=' . $cc_number . ';';
}
}
// store last 4 digits of CC number
// $order->info['cc_number'] = str_repeat('X', (strlen($myorder["cardnumber"]) - 4)) . substr($myorder["cardnumber"], -4);
// store first and last 4 digits of CC number ... which is the Visa-standards-compliant approach, same as observed by Linkpoint's services
$order->info['cc_number'] = $cc_number;
$order->info['cc_type'] = $_POST['cc_type'];
$order->info['cc_owner'] = $_POST['cc_owner'];
$order->info['cc_cvv'] = '***';
$order->info['cc_expires'] = '';// $_POST['cc_expires'];
$lp_trans_num = $result['r_ordernum'];
$transaction_tax = $result['r_tax']; // The calculated tax for the order, when the ordertype is calctax.
$transaction_shipping = $result['r_shipping']; // The calculated shipping charges for the order, when the ordertype is calcshipping.
$this->response_codes = $result['r_avs']; // AVS Response for transaction
// these are used to update the order-status-history upon order completion
$this->transaction_id = $result['r_tdate'] . ' Order Number/Code: ' . $result['r_ordernum'];
$this->auth_code = $result['r_code']; // The approval code for this transaction.
// Store Transaction history in Database
$sql_data_array= array(array('fieldName'=>'lp_trans_num', 'value' => $result['r_ordernum'], 'type'=>'string'), // The order number associated with this transaction.
array('fieldName'=>'order_id', 'value' => $result['r_ordernum'], 'type'=>'integer'),
array('fieldName'=>'approval_code', 'value' => $result['r_code'], 'type'=>'string'), // The approval code for this transaction.
array('fieldName'=>'transaction_response_time', 'value' => $result['r_time'], 'type'=>'string'), // The time+date of the transaction server response.
array('fieldName'=>'r_error', 'value' => $result['r_error'], 'type'=>'string'),
array('fieldName'=>'customer_id', 'value' => $_SESSION['customer_id'] , 'type'=>'integer'),
array('fieldName'=>'avs_response', 'value' => $result['r_avs'], 'type'=>'string'), // AVS Response for transaction
array('fieldName'=>'transaction_result', 'value' => $result['r_approved'], 'type'=>'string'), // Transaction result: APPROVED, DECLINED, or FRAUD.
array('fieldName'=>'message', 'value' => $result['r_message'] . "\n" . $all_response_info, 'type'=>'string'), // Any message returned by the processor; e.g., CALL VOICE CENTER.
array('fieldName'=>'transaction_time', 'value' => $result['r_tdate'], 'type'=>'string'), // A server time-date stamp for this transaction.
array('fieldName'=>'transaction_reference_number', 'value' => $result['r_ref'], 'type'=>'string'), // Reference number returned by the CC processor.
array('fieldName'=>'fraud_score', 'value' => $result['r_score'], 'type'=>'integer'), // LinkShield fraud risk score.
array('fieldName'=>'cc_number', 'value' => $cc_number, 'type'=>'string'),
array('fieldName'=>'cust_info', 'value' => $cust_info, 'type'=>'string'),
array('fieldName'=>'chargetotal', 'value' => $chargetotal, 'type'=>'string'),
array('fieldName'=>'cc_expire', 'value' => $cc_month . '/' . $cc_year, 'type'=>'string'),
array('fieldName'=>'ordertype', 'value' => $myorder['ordertype'], 'type'=>'string'), // transaction type: PREAUTH or SALE
array('fieldName'=>'date_added', 'value' => 'now()', 'type'=>'noquotestring'));
if (MODULE_PAYMENT_LINKPOINT_API_STORE_DATA == 'True') {
$db->perform(TABLE_LINKPOINT_API, $sql_data_array);
}
// Begin check of specific error conditions
if ($result["r_approved"] != "APPROVED" && !($result["r_approved"] == "SUBMITTED" && $result["r_message"] == 'APPROVED')) {
if (substr($result['r_error'],0,10) == 'SGS-020005') $messageStack->add_session('checkout_payment', $result['r_error'], 'error'); // Error (Merchant config file is missing, empty or cannot be read)
if (substr($result['r_error'],0,10) == 'SGS-005000') $messageStack->add_session('checkout_payment', MODULE_PAYMENT_LINKPOINT_API_TEXT_GENERAL_ERROR . '
' . $result['r_error'], 'error'); // The server encountered a database error
if (substr($result['r_error'],0,10) == 'SGS-000001' || strstr($result['r_error'], 'D:Declined') || strstr($result['r_error'], 'R:Referral')) $messageStack->add_session('checkout_payment', MODULE_PAYMENT_LINKPOINT_API_TEXT_DECLINED_MESSAGE . '
' . $result['r_error'], 'error');
if (substr($result['r_error'],0,10) == 'SGS-005005' || strstr($result['r_error'], 'Duplicate transaction')) $messageStack->add_session('checkout_payment', MODULE_PAYMENT_LINKPOINT_API_TEXT_DUPLICATE_MESSAGE . '
' . $result['r_error'], 'error');
if (substr($result['r_error'],0,10) == 'SGS-002301') $messageStack->add_session('checkout_payment', 'Subtotal miscalculation. Please notify the storeowner.' . '
' . $result['r_error'], 'error');
}
// End specific error conditions
// Begin Transaction Status does not equal APPROVED
if ($result["r_approved"] != "APPROVED") {
// alert to customer:
$messageStack->add_session('checkout_payment', MODULE_PAYMENT_LINKPOINT_API_TEXT_DECLINED_MESSAGE, 'caution');
zen_redirect(zen_href_link(FILENAME_CHECKOUT_PAYMENT, '', 'SSL', true, false));
}
// End Transaction Status does not equal APPROVED
$avs_meanings = array();
$avs_meanings['YY'] = ' - Street Address and Zip Code match.';
$avs_meanings['YN'] = ' - Street Address matches but Zip Code does NOT match.';
$avs_meanings['YX'] = ' - Street Address matches, but Zip Code comparison unavailable.';
$avs_meanings['NY'] = ' - Street Address DOES NOT match, but Zip Code matches.';
$avs_meanings['XY'] = ' - Street Address check not available, but Zip Code matches.';
$avs_meanings['NN'] = ' - Street Address DOES NOT MATCH and Zip Code DOES NOT MATCH.';
$avs_meanings['NX'] = ' - Street Address DOES NOT MATCH and Zip Code comparison unavailable.';
$avs_meanings['XN'] = ' - Street Address check not available. Zip Code DOES NOT MATCH.';
$avs_meanings['XX'] = ' - No validation for address or zip code could be performed (not available from issuing bank).';
// Possible Fraud order. Allow transaction to process, but notify shop for owner to take appropriate action on order
if (($result["r_approved"] == "APPROVED") && (substr($result['r_code'], 17, 2) != "YY") && MODULE_PAYMENT_LINKPOINT_API_FRAUD_ALERT == 'Yes') {
//DEBUG: $messageStack->add_session('header', 'possible fraud situation--> ' . $result['r_code'], 'caution');
$message = 'Potential Fraudulent Order - Bad Address - Action Required' . "\n" .
'This alert occurs because the "Approval Code" below does not contain the expected YY response.' . "\n" .
'Thus, you might want to verify the address with the customer prior to shipping, or be sure to use Registered Mail with Signature Required in case they file a chargeback.' . "\n\n" .
'Customer Name: ' . $order->customer['firstname'] . ' ' . $order->customer['lastname'] . "\n\n" .
'AVS Result: ' . $result['r_avs'] . $avs_meanings[substr($result['r_avs'],0,2)] . "\n\n" .
'Order Number: ' . $lp_trans_num . "\n" .
'Transaction Date and Time: ' . $result['r_time'] . "\n" .
'Approval Code: ' . $result['r_code'] . "\n" .
'Reference Number: ' . $result['r_ref'] . "\n\n" .
'Error Message: ' . $result['r_error'] . "\n\n" .
'Transaction Result: ' . $result['r_approved'] . "\n\n" .
'Message: ' . $result['r_message'] . "\n\n" .
'Fraud Score: ' . ($result['r_score'] == '' ? 'Not Enabled' : $result['r_score']) . "\n\n" .
'AVS CODE MEANINGS: ' . "\n" .
'YY** = Street Address and Zip Code match.' . "\n" .
'YN** = Street Address matches but Zip Code does NOT match.' . "\n" .
'YX** = Street Address matches, but Zip Code comparison unavailable.' . "\n" .
'NY** = Street Address DOES NOT match, but Zip Code matches.' . "\n" .
'XY** = Street Address check not available, but Zip Code matches.' . "\n" .
'NN** = Street Address DOES NOT MATCH and Zip Code DOES NOT MATCH.' . "\n" .
'NX** = Street Address DOES NOT MATCH and Zip Code comparison unavailable.' . "\n" .
'XN** = Street Address check not available. Zip Code DOES NOT MATCH.' . "\n" .
'XX** = Neither validation is available.' . "\n";
$html_msg['EMAIL_MESSAGE_HTML'] = nl2br($result['r_message']);
zen_mail(STORE_NAME, STORE_OWNER_EMAIL_ADDRESS, 'Potential Fraudulent Order - Bad Address - Action Required - ' . $lp_trans_num, $message, STORE_NAME, EMAIL_FROM, $html_msg, 'fraudalert');
}
// end fraud alert
}
function after_process() {
global $insert_id, $db;
$comments = (MODULE_PAYMENT_LINKPOINT_API_AUTHORIZATION_MODE == 'Authorize Only' ? ALERT_LINKPOINT_API_PREAUTH_TRANS : '');
switch (MODULE_PAYMENT_LINKPOINT_API_TRANSACTION_MODE_RESPONSE) {
case "LIVE: Production": $comments .= ''; break;
case "TESTING: Successful": $comments .= ' ' . ALERT_LINKPOINT_API_TEST_FORCED_SUCCESSFUL; break;
case "TESTING: Decline": $comments .= ' ' . ALERT_LINKPOINT_API_TEST_FORCED_DECLINED; break;
}
if ($this->auth_code && $this->transaction_id) $comments .= " " . $this->cc_card_type . " AUTH: " . $this->auth_code . ". TransID: " . $this->transaction_id;
$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. ' . $comments, 'string');
$sql = $db->bindVars($sql, ':orderID', $insert_id, 'integer');
$sql = $db->bindVars($sql, ':orderStatus', $this->order_status, 'integer');
$db->Execute($sql);
return false;
}
function after_order_create($zf_order_id) {
global $db, $lp_avs, $lp_trans_num;
$db->execute("update " . TABLE_ORDERS . " set lp_avs ='" . $lp_avs . "' where orders_id = '" . $zf_order_id ."'");
$db->execute("update " . TABLE_ORDERS . " set lp_trans_num ='" . $lp_trans_num . "' where orders_id = '" . $zf_order_id ."'");
$db->execute("update " . TABLE_LINKPOINT_API . " set order_id ='" . $zf_order_id . "' where lp_trans_num = '" . $lp_trans_num ."'");
}
function admin_notification($zf_order_id) {
global $db;
if (MODULE_PAYMENT_LINKPOINT_API_STORE_DATA=='False') return '';
$output = '';
$sql = "select * from " . TABLE_LINKPOINT_API . " where order_id = '" . $zf_order_id . "' and transaction_result = 'APPROVED' order by date_added";
$lp_api = $db->Execute($sql);
if ($lp_api->RecordCount() > 0) require(DIR_FS_CATALOG. DIR_WS_MODULES . 'payment/linkpoint_api/linkpoint_api_admin_notification.php');
return $output;
}
function get_error() {
$error = array('title' => MODULE_PAYMENT_LINKPOINT_API_TEXT_ERROR,
'error' => stripslashes(urldecode($_GET['error'])));
return $error;
}
function check() {
global $db;
if (IS_ADMIN_FLAG === true) {
global $sniffer;
if ($sniffer->table_exists(TABLE_LINKPOINT_API)) {
if ($sniffer->field_exists(TABLE_LINKPOINT_API, 'zen_order_id')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " CHANGE COLUMN zen_order_id order_id int(11) NOT NULL default '0'");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'ordertype')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD ordertype varchar(8) NOT NULL default '' after cc_expire");
}
}
if (!isset($this->_check)) {
$check_query = $db->Execute("select configuration_value from " . TABLE_CONFIGURATION . " where configuration_key = 'MODULE_PAYMENT_LINKPOINT_API_STATUS'");
$this->_check = $check_query->RecordCount();
}
return $this->_check;
}
function install() {
global $db, $messageStack;
if (defined('MODULE_PAYMENT_LINKPOINT_API_STATUS')) {
$messageStack->add_session('FirstData/Linkpoint module already installed.', 'error');
zen_redirect(zen_href_link(FILENAME_MODULES, 'set=payment&module=linkpoint_api', '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 FirstData/Linkpoint Module', 'MODULE_PAYMENT_LINKPOINT_API_STATUS', 'True', 'Do you want to accept FirstData/Linkpoint credit card payments?', '6', 121, '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 ('FirstData/Linkpoint/YourPay Merchant ID', 'MODULE_PAYMENT_LINKPOINT_API_LOGIN', 'EnterYourStoreNumber', 'Please enter your FirstData/Linkpoint/YourPay Merchant ID.
This is the same as the number in the PEM digital certificate filename for your account.', '6', 121, 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 ('FirstData/LinkPoint Transaction Mode Response', 'MODULE_PAYMENT_LINKPOINT_API_TRANSACTION_MODE_RESPONSE', 'LIVE: Production', 'Production: Use this for live stores.
or, select these options if you wish to test the module:
Successful: Use to TEST by forcing a Successful transaction
Decline: Use to TEST forcing a Failed transaction', '6', 121, 'zen_cfg_select_option(array(\'LIVE: Production\', \'TESTING: Successful\', \'TESTING: Decline\'), ', 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_LINKPOINT_API_AUTHORIZATION_MODE', 'Authorize Only', 'Do you want submitted credit card transactions to be authorized only, or immediately charged/captured?
In most cases you will want to do an Immediate Charge to capture payment immediately. In some situations, you may prefer to simply Authorize transactions, and then manually use your Merchant Terminal to formally capture the payments (esp if payment amounts may fluctuate between placing the order and shipping it)', '6', 121, 'zen_cfg_select_option(array(\'Authorize Only\', \'Immediate Charge/Capture\'), ', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Set Order Status', 'MODULE_PAYMENT_LINKPOINT_API_ORDER_STATUS_ID', 2, 'Set the status of orders made with this payment module to this value
(this affects all Captured / Charged / Approved orders)
Recommended: Processing', '6', 121, 'zen_cfg_pull_down_order_statuses(', 'zen_get_order_status_name', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('PREAUTH Order Status', 'MODULE_PAYMENT_LINKPOINT_API_PREAUTH_ORDER_STATUS_ID', 1, 'When this module is set to PREAUTH mode (Authorization), which order-status do you want the purchase to be set to?
Recommended: Pending', '6', 121, 'zen_cfg_pull_down_order_statuses(', 'zen_get_order_status_name', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, set_function, use_function, date_added) values ('Refund/Void Order Status', 'MODULE_PAYMENT_LINKPOINT_API_REFUNDED_ORDER_STATUS_ID', '1', 'When orders are refunded or voided from this Admin area, which order-status do you want the transaction to be set to?
Recommended: Pending or cancelled/refunded', '6', '0', 'zen_cfg_pull_down_order_statuses(', 'zen_get_order_status_name', now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, date_added) values ('Sort order of display.', 'MODULE_PAYMENT_LINKPOINT_API_SORT_ORDER', '0', 'Any value greater than zero will cause this payment method to appear in the specified sort order on the checkout-payment page.', '6', 121, now())");
$db->Execute("insert into " . TABLE_CONFIGURATION . " (configuration_title, configuration_key, configuration_value, configuration_description, configuration_group_id, sort_order, use_function, set_function, date_added) values ('Payment Zone (restrict to)', 'MODULE_PAYMENT_LINKPOINT_API_ZONE', '0', 'If you want only customers from a particular zone to be able to use this payment module, select that zone here.', '6', 121, 'zen_get_zone_class_title', 'zen_cfg_pull_down_zone_classes(', 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 ('Fraud Alerts', 'MODULE_PAYMENT_LINKPOINT_API_FRAUD_ALERT', 'Yes', 'Do you want to be notified by email of suspected fraudulent Credit Card activity?
(sends to Store Owner Email Address)', '6', 121, 'zen_cfg_select_option(array(\'Yes\', \'No\'), ', 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_LINKPOINT_API_STORE_DATA', 'True', 'If you enable this option, extended details of each transaction will be stored, enabling you to more effectively conduct audits of fraudulent activity or even track/match order information between Zen Cart and your FirstData/LinkPoint records. You can view this data in Admin->Customers->Linkpoint CC Review.', '6', 121, '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 ('Debug Mode', 'MODULE_PAYMENT_LINKPOINT_API_DEBUG', 'Off', 'Would you like to enable debug mode? Choosing Alert mode will email logs of failed transactions to the store owner.', '6', '0', 'zen_cfg_select_option(array(\'Off\', \'Failure Alerts Only\', \'Log File\', \'Log and Email\'), ', 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 ('Transaction Mode', 'MODULE_PAYMENT_LINKPOINT_API_TRANSACTION_MODE', 'Production', 'Transaction mode used for processing orders', '6', 121, 'zen_cfg_select_option(array(\'Production\',\'DevelopersTest\'), ', now())");
// Now do database-setup:
global $sniffer;
if (!$sniffer->table_exists(TABLE_LINKPOINT_API)) {
$sql = "CREATE TABLE " . TABLE_LINKPOINT_API . " (
id int(11) unsigned NOT NULL auto_increment,
customer_id varchar(11) NOT NULL default '',
lp_trans_num varchar(64) NOT NULL default '',
order_id int(11) NOT NULL default '0',
avs_response varchar(4) NOT NULL default '',
r_error varchar(250) NOT NULL default '',
approval_code varchar(254) NOT NULL default '',
transaction_result varchar(25) NOT NULL default '',
message text NOT NULL,
transaction_response_time varchar(25) NOT NULL default '',
transaction_time varchar(50) NOT NULL default '',
transaction_reference_number varchar(64) NOT NULL default '',
fraud_score int(11) NOT NULL default '0',
cc_number varchar(20) NOT NULL default '',
cc_expire varchar(12) NOT NULL default '',
ordertype varchar(8) NOT NULL default '',
cust_info text,
chargetotal decimal(15,4) NOT NULL default '0.0000',
date_added datetime NOT NULL default '0001-01-01 00:00:00',
PRIMARY KEY (id),
KEY idx_customer_id_zen (customer_id) )";
$db->Execute($sql);
} else {
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'customer_id')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD customer_id varchar(11) NOT NULL default '' after id");
if ($sniffer->field_exists(TABLE_LINKPOINT_API, 'lp_order_id')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " CHANGE COLUMN lp_order_id lp_trans_num varchar(64) NOT NULL default ''");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'r_error')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD r_error varchar(250) NOT NULL default '' after avs_response");
if ($sniffer->field_exists(TABLE_LINKPOINT_API, 'approval_code')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " CHANGE COLUMN approval_code approval_code varchar(254) NOT NULL default ''");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'transaction_result')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD transaction_result varchar(25) NOT NULL default '' after approval_code");
if ($sniffer->field_exists(TABLE_LINKPOINT_API, 'message')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " CHANGE COLUMN message message text NOT NULL");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'transaction_response_time')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD transaction_response_time varchar(25) NOT NULL default '' after message");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'transaction_time')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD transaction_time varchar(50) NOT NULL default '' after transaction_response_time");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'transaction_reference_number')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD transaction_reference_number varchar(64) NOT NULL default '' after transaction_time");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'fraud_score')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD fraud_score int(11) NOT NULL default '0' after transaction_reference_number");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'cc_number')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD cc_number varchar(20) NOT NULL default '' after fraud_score");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'cc_expire')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD cc_expire varchar(12) NOT NULL default '' after cc_number");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'ordertype')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD ordertype varchar(8) NOT NULL default '' after cc_expire");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'cust_info')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD cust_info text after ordertype");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'chargetotal')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD chargetotal decimal(15,4) NOT NULL default '0.0000' after cust_info");
if (!$sniffer->field_exists(TABLE_LINKPOINT_API, 'date_added')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " ADD date_added datetime NOT NULL default '0001-01-01 00:00:00' after chargetotal");
if ($sniffer->field_exists(TABLE_LINKPOINT_API, 'zen_order_id')) $db->Execute("ALTER TABLE " . TABLE_LINKPOINT_API . " CHANGE COLUMN zen_order_id order_id int(11) NOT NULL default '0'");
}
if (!$sniffer->field_exists(TABLE_ORDERS, 'lp_avs')) {
$sql = "ALTER TABLE " . TABLE_ORDERS . " ADD lp_avs VARCHAR( 25 ) NOT NULL DEFAULT ''";
$db->Execute($sql);
}
if (!$sniffer->field_exists(TABLE_ORDERS, 'lp_trans_num')) {
$sql = "ALTER TABLE " . TABLE_ORDERS . " ADD lp_trans_num varchar(64) NOT NULL DEFAULT ''";
$db->Execute($sql);
}
if ($sniffer->field_type(TABLE_ORDERS, 'lp_trans_num', 'varchar(64)', true) !== true) {
$sql = "ALTER TABLE " . TABLE_ORDERS . " CHANGE lp_trans_num lp_trans_num varchar(64) NOT NULL DEFAULT ''";
$db->Execute($sql);
}
}
function remove() {
global $db, $sniffer;
$db->Execute("delete from " . TABLE_CONFIGURATION . " where configuration_key like 'MODULE\_PAYMENT\_LINKPOINT\_API\_%'");
// cleanup database if contains no data
if ($sniffer->table_exists(TABLE_LINKPOINT_API)) {
$result = $db->Execute("select count(id) as count from " . TABLE_LINKPOINT_API);
if ($result->RecordCount() == 0) $db->Execute("DROP TABLE " . TABLE_LINKPOINT_API);
}
}
function keys() {
$keys_list = array(
'MODULE_PAYMENT_LINKPOINT_API_STATUS',
'MODULE_PAYMENT_LINKPOINT_API_LOGIN',
'MODULE_PAYMENT_LINKPOINT_API_TRANSACTION_MODE_RESPONSE',
'MODULE_PAYMENT_LINKPOINT_API_AUTHORIZATION_MODE',
'MODULE_PAYMENT_LINKPOINT_API_ORDER_STATUS_ID',
'MODULE_PAYMENT_LINKPOINT_API_PREAUTH_ORDER_STATUS_ID',
'MODULE_PAYMENT_LINKPOINT_API_REFUNDED_ORDER_STATUS_ID',
'MODULE_PAYMENT_LINKPOINT_API_SORT_ORDER',
'MODULE_PAYMENT_LINKPOINT_API_ZONE',
'MODULE_PAYMENT_LINKPOINT_API_FRAUD_ALERT',
'MODULE_PAYMENT_LINKPOINT_API_STORE_DATA' );
$keys_list[] = 'MODULE_PAYMENT_LINKPOINT_API_DEBUG';
if (IS_ADMIN_FLAG === true && isset($_GET['debug']) && $_GET['debug']=='on' && MODULE_PAYMENT_LINKPOINT_API_CODE_DEBUG=='debug') $keys_list[] = 'MODULE_PAYMENT_LINKPOINT_API_TRANSACTION_MODE';
return $keys_list;
}
function _log($msg, $suffix = '') {
static $key;
if (!isset($key) || $key == '') $key = time() . '_' . zen_create_random_value(4);
$file = $this->_logDir . '/' . 'Linkpoint_Debug_' . $suffix . '_' . $key . '.log';
if ($fp = @fopen($file, 'a')) {
@fwrite($fp, $msg);
@fclose($fp);
}
}
/**
* Send transaction to gateway
*/
function _sendRequest($myorder) {
$myorder["host"] = "secure.linkpt.net";
if (MODULE_PAYMENT_LINKPOINT_API_TRANSACTION_MODE == 'DevelopersTest') {
$myorder["host"] = "staging.linkpt.net";
}
$myorder["port"] = "1129";
$myorder["keyfile"] =(DIR_FS_CATALOG . DIR_WS_MODULES . 'payment/linkpoint_api/' . MODULE_PAYMENT_LINKPOINT_API_LOGIN . '.pem');
$myorder["configfile"] = MODULE_PAYMENT_LINKPOINT_API_LOGIN; // This is your store number
// set to ECI and UNSPECIFIED for ecommerce transactions:
$myorder["transactionorigin"] = "ECI";
$myorder["terminaltype"] = "UNSPECIFIED";
// debug - for testing communication only
if (MODULE_PAYMENT_LINKPOINT_API_DEBUG != 'Off') {
}
if (MODULE_PAYMENT_LINKPOINT_API_CODE_DEBUG=='debug') {
$myorder["debugging"] = "true"; // for development only - not intended for production use
$myorder["debug"] = "true"; // for development only - not intended for production use
$myorder["webspace"] = "true"; // for development only - not intended for production use
}
include(DIR_FS_CATALOG . DIR_WS_MODULES . 'payment/linkpoint_api/class.linkpoint_api.php');
$mylphp = new lphp;
// Send transaction, using cURL
$result = $mylphp->curl_process($myorder);
// do debug output
$errorMessage = date('M-d-Y h:i:s') . "\n=================================\n\n" . ($mylphp->commError !='' ? $mylphp->commError . "\n\n" : '') . 'Response Code: ' . $result["r_approved"] . ' ' . $result["r_error"] . "\n\n=================================\n\n" . 'Sending to Gateway: ' . "\n" . $mylphp->sendData . "\n\n" . 'Result: ' . substr(print_r($result, true), 5) . "\n\n";
if ($mylphp->commError != '') $errorMessage .= $mylphp->commError . "\n" . 'CURL info: ' . print_r($mylphp->commInfo, true) . "\n";
if (CURL_PROXY_REQUIRED == 'True') $errorMessage .= 'Using CURL Proxy: [' . CURL_PROXY_SERVER_DETAILS . '] with Proxy Tunnel: ' .($proxy_tunnel_flag ? 'On' : 'Off') . "\n";
$failure = (!is_array($result) || $result["r_approved"] != "APPROVED") ? true : false;
// handle logging
if (strstr(MODULE_PAYMENT_LINKPOINT_API_DEBUG, 'Log')) {
$this->_log($errorMessage, $myorder["oid"] . ($failure ? '_FAILED' : ''));
// $this->_log($errorMessage . print_r($myorder, true) . print_r($mylphp->xmlString, true), $myorder["oid"] . ($failure ? '_FAILED' : ''));
}
if (strstr(MODULE_PAYMENT_LINKPOINT_API_DEBUG, 'Email') || ($failure && strstr(MODULE_PAYMENT_LINKPOINT_API_DEBUG, 'Alert'))) {
zen_mail(STORE_NAME, STORE_OWNER_EMAIL_ADDRESS, 'Linkpoint Debug Data' . ($failure ? ' - FAILURE' : ''), $errorMessage, STORE_OWNER, STORE_OWNER_EMAIL_ADDRESS, array('EMAIL_MESSAGE_HTML'=>nl2br($errorMessage)), 'debug');
}
//DEBUG ONLY:$this->_log($errorMessage /*. print_r($myorder, true) . print_r($mylphp->xmlString, true)*/, $myorder["oid"]);
if ($myorder['debugging'] == 'true') exit;
return $result;
}
/**
* Update order status and order status history based on admin changes sent to gateway
*/
function _updateOrderStatus($oID, $new_order_status, $comments) {
global $db;
$sql_data_array= array(array('fieldName'=>'orders_id', 'value' => $oID, 'type'=>'integer'),
array('fieldName'=>'orders_status_id', 'value' => $new_order_status, 'type'=>'integer'),
array('fieldName'=>'date_added', 'value' => 'now()', 'type'=>'noquotestring'),
array('fieldName'=>'comments', 'value' => $comments, 'type'=>'string'),
array('fieldName'=>'customer_notified', 'value' => 0, 'type'=>'integer'));
$db->perform(TABLE_ORDERS_STATUS_HISTORY, $sql_data_array);
$db->Execute("update " . TABLE_ORDERS . "
set orders_status = '" . (int)$new_order_status . "'
where orders_id = '" . (int)$oID . "'");
}
/**
* Used to submit a refund for a given transaction.
*/
function _doRefund($oID, $amount = 0) {
global $db, $messageStack;
$_POST['refamt'] = preg_replace('/[^0-9.%]/', '', $_POST['refamt']);
$new_order_status = (int)MODULE_PAYMENT_LINKPOINT_API_REFUNDED_ORDER_STATUS_ID;
if ($new_order_status == 0) $new_order_status = 1;
$proceedToRefund = true;
$refundNote = strip_tags(zen_db_input($_POST['refnote']));
if (isset($_POST['refconfirm']) && $_POST['refconfirm'] != 'on') {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_REFUND_CONFIRM_ERROR, 'error');
$proceedToRefund = false;
}
if (isset($_POST['buttonrefund']) && $_POST['buttonrefund'] == MODULE_PAYMENT_LINKPOINT_API_ENTRY_REFUND_BUTTON_TEXT) {
$refundAmt = (float)$_POST['refamt'];
$new_order_status = (int)MODULE_PAYMENT_LINKPOINT_API_REFUNDED_ORDER_STATUS_ID;
if ($refundAmt == 0) {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_INVALID_REFUND_AMOUNT, 'error');
$proceedToRefund = false;
}
}
if (isset($_POST['cc_number']) && (int)trim($_POST['cc_number']) == 0) {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_CC_NUM_REQUIRED_ERROR, 'error');
}
if (isset($_POST['trans_id']) && (int)trim($_POST['trans_id']) == 0) {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_TRANS_ID_REQUIRED_ERROR, 'error');
$proceedToRefund = false;
}
$sql = "select lp_trans_num, transaction_time from " . TABLE_LINKPOINT_API . " where order_id = " . (int)$oID . " and transaction_result = 'APPROVED' order by transaction_time DESC";
$query = $db->Execute($sql);
if ($query->RecordCount() < 1) {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_NO_MATCHING_ORDER_FOUND, 'error');
$proceedToRefund = false;
}
/**
* Submit refund request to gateway
*/
if ($proceedToRefund) {
unset($myorder);
$myorder["ordertype"] = 'CREDIT';
$myorder["oid"] = $query->fields['lp_trans_num'];
if ($_POST['trans_id'] != '') $myorder["tdate"] = $_POST['trans_id'];
$myorder["chargetotal"] = number_format($refundAmt, 2, '.', '');
$myorder["comments"] = htmlentities($refundNote, ENT_QUOTES, 'UTF-8');
$result = $this->_sendRequest($myorder);
$response_alert = $result['r_approved'] . ' ' . $result['r_error'] . ($this->commError == '' ? '' : ' Communications Error - Please notify webmaster.');
$this->reportable_submit_data['Note'] = $refundNote;
$failure = ($result["r_approved"] != "APPROVED");
if ($failure) {
$messageStack->add_session($response_alert, 'error');
} else {
// Success, so save the results
$this->_updateOrderStatus($oID, $new_order_status, 'REFUND INITIATED. Order ID:' . $result['r_ordernum'] . ' - ' . 'Trans ID: ' . $result['r_tdate'] . "\n" . 'Amount: ' . $myorder["chargetotal"] . "\n" . $refundNote);
$messageStack->add_session(sprintf(MODULE_PAYMENT_LINKPOINT_API_TEXT_REFUND_INITIATED, $result['r_tdate'], $result['r_ordernum']), 'success');
return true;
}
}
return false;
}
/**
* Used to capture part or all of a given previously-authorized transaction.
*/
function _doCapt($oID, $amt = 0, $currency = 'USD') {
global $db, $messageStack;
$_POST['captamt'] = preg_replace('/[^0-9.%]/', '', $_POST['captamt']);
//@TODO: Read current order status and determine best status to set this to
$new_order_status = (int)MODULE_PAYMENT_LINKPOINT_API_ORDER_STATUS_ID;
if ($new_order_status == 0) $new_order_status = 1;
$proceedToCapture = true;
$captureNote = strip_tags(zen_db_input($_POST['captnote']));
if (isset($_POST['captconfirm']) && $_POST['captconfirm'] == 'on') {
} else {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_CAPTURE_CONFIRM_ERROR, 'error');
$proceedToCapture = false;
}
$lp_trans_num = (isset($_POST['captauthid']) && $_POST['captauthid'] != '') ? strip_tags(zen_db_input($_POST['captauthid'])) : '';
$sql = "select lp_trans_num, chargetotal from " . TABLE_LINKPOINT_API . " where order_id = " . (int)$oID . " and transaction_result = 'APPROVED' order by date_added";
if ($lp_trans_num != '') $sql = "select lp_trans_num, chargetotal from " . TABLE_LINKPOINT_API . " where lp_trans_num = :trans_num: and transaction_result = 'APPROVED' order by date_added";
$sql = $db->bindVars($sql, ':trans_num:', $lp_trans_num, 'string');
$query = $db->Execute($sql);
if ($query->RecordCount() < 1) {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_NO_MATCHING_ORDER_FOUND, 'error');
$proceedToCapture = false;
}
$captureAmt = (isset($_POST['captamt']) && $_POST['captamt'] != '') ? (float)strip_tags(zen_db_input($_POST['captamt'])) : $query->fields['chargetotal'];
if (isset($_POST['btndocapture']) && $_POST['btndocapture'] == MODULE_PAYMENT_LINKPOINT_API_ENTRY_CAPTURE_BUTTON_TEXT) {
if ($captureAmt == 0) {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_INVALID_CAPTURE_AMOUNT, 'error');
$proceedToCapture = false;
}
}
/**
* Submit capture request to Gateway
*/
if ($proceedToCapture) {
unset($myorder);
$myorder["ordertype"] = 'POSTAUTH';
$myorder["oid"] = $query->fields['lp_trans_num'];
$myorder["chargetotal"] = number_format($captureAmt, 2, '.', '');
$myorder["comments"] = htmlentities($captureNote, ENT_QUOTES, 'UTF-8');
$result = $this->_sendRequest($myorder);
$response_alert = $result['r_approved'] . ' ' . $result['r_error'] . ($this->commError == '' ? '' : ' Communications Error - Please notify webmaster.');
$failure = ($result["r_approved"] != "APPROVED");
if ($failure) {
$messageStack->add_session($response_alert, 'error');
} else {
// Success, so save the results
$this->_updateOrderStatus($oID, $new_order_status, 'FUNDS COLLECTED. Auth Code: ' . substr($result['r_code'], 0, 6) . ' - ' . 'Trans ID: ' . $result['r_tdate'] . "\n" . ' Amount: ' . number_format($captureAmt, 2) . "\n" . $captureNote);
$messageStack->add_session(sprintf(MODULE_PAYMENT_LINKPOINT_API_TEXT_CAPT_INITIATED, $captureAmt, $result['r_tdate'], substr($result['r_code'], 0, 6)), 'success');
return true;
}
}
return false;
}
/**
* Used to void a given previously-authorized transaction.
*/
function _doVoid($oID, $note = '') {
global $db, $messageStack;
$new_order_status = (int)MODULE_PAYMENT_LINKPOINT_API_REFUNDED_ORDER_STATUS_ID;
if ($new_order_status == 0) $new_order_status = 1;
$voidNote = strip_tags(zen_db_input($_POST['voidnote'] . $note));
$voidAuthID = trim(strip_tags(zen_db_input($_POST['voidauthid'])));
$proceedToVoid = true;
if (isset($_POST['ordervoid']) && $_POST['ordervoid'] == MODULE_PAYMENT_LINKPOINT_API_ENTRY_VOID_BUTTON_TEXT) {
if (isset($_POST['voidconfirm']) && $_POST['voidconfirm'] != 'on') {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_VOID_CONFIRM_ERROR, 'error');
$proceedToVoid = false;
}
}
if ($voidAuthID == '') {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_TRANS_ID_REQUIRED_ERROR, 'error');
$proceedToVoid = false;
}
$sql = "select lp_trans_num, transaction_time from " . TABLE_LINKPOINT_API . " where order_id = " . (int)$oID . " and transaction_result = 'APPROVED' order by date_added";
$query = $db->Execute($sql);
if ($query->RecordCount() < 1) {
$messageStack->add_session(MODULE_PAYMENT_LINKPOINT_API_TEXT_NO_MATCHING_ORDER_FOUND, 'error');
$proceedToVoid = false;
}
/**
* Submit void request to Gateway
*/
if ($proceedToVoid) {
unset($myorder);
$myorder["ordertype"] = 'VOID';
$myorder["oid"] = $query->fields['lp_trans_num'];
if ($voidAuthID != '') $myorder["tdate"] = $voidAuthID;
$myorder["comments"] = htmlentities($voidNote, ENT_QUOTES, 'UTF-8');
$result = $this->_sendRequest($myorder);
$response_alert = $result['r_approved'] . ' ' . $result['r_error'] . ($this->commError == '' ? '' : ' Communications Error - Please notify webmaster.');
$failure = ($result["r_approved"] != "APPROVED");
if ($failure) {
$messageStack->add_session($response_alert, 'error');
} else {
// Success, so save the results
$this->_updateOrderStatus($oID, $new_order_status, 'VOIDED. OrderNo: ' . $result['r_ordernum'] . ' - Trans ID: ' . $result['r_tdate'] . "\n" . $voidNote);
$messageStack->add_session(sprintf(MODULE_PAYMENT_LINKPOINT_API_TEXT_VOID_INITIATED, $result['r_tdate'], $result['r_ordernum']), 'success');
return true;
}
}
return false;
}
}