<?php
// $Id:$

/********************************************************************
 * Drupal Hooks
 ********************************************************************/

/**
 * Implementation of hook_menu().
 */
function linkpoint_menu($may_cache) {

  $items = array();

  if ($may_cache) {
   $items[] = array('path' => 'linkpoint/form', 
      'title' => t('Credit Card Payment'),
      'callback' => 'linkpoint_form', 
      'access' => true, 
      'type' => MENU_CALLBACK
    );
   $items[] = array('path' => 'linkpoint/invoice', 
      'title' => t('Transaction Complete'),
      'callback' => 'linkpoint_invoice', 
      'access' => true, 
      'type' => MENU_CALLBACK
    );
  }
  return $items;
}

/**
 * Implementation of hook_help().
 */
function linkpoint_help($section = 'admin/help#linkpoint') {

  switch ($section) {
    case 'admin/modules#description':
      return t('Process payments using linkpoint.');

    case 'admin/settings/linkpoint':
      return t("You need to have a linkpoint merchant account in order to use this module. See <a href=\"http://www.linkpoint.com\">here</a> for more information.");

    case 'linkpoint/form_submit_guidlines':
      return t("Do not submit this form twice, or you may be double billed!");
  }
}

function linkpoint_ec_settings() {
  $form['linkpoint_http_url'] = array(
    '#type' => 'textfield',
    '#title' => t('HTTP url'),
    '#default_value' => variable_get('linkpoint_http_url',url('',NULL,NULL,true)),
  ); 
  $form['linkpoint_https_url'] = array(
    '#type' => 'textfield',
    '#title' => t('HTTPS url'),
    '#default_value' => variable_get('linkpoint_https_url',str_replace('http','https',url('',NULL,NULL,true))),
  ); 
  $form['linkpoint_help'] = array(
    '#type' => 'textarea',
    '#title' => t('Explanation or submission guidelines'),
    '#default_value' => variable_get('linkpoint_help', linkpoint_help('linkpoint/form_submit_guidlines')),
    '#cols' => 70,
    '#rows' => 5,
    '#description' => t('This text will be displayed at the top of the credit card submission form.'),
  );

  $form['linkpoint_store_number'] = array(
    '#type' => 'textfield',
    '#title' => t('Merchant store number'),
    '#default_value' => variable_get('linkpoint_store_number', ''),
    '#size' => 70,
    '#maxlength' => 180,
    '#description' => t("The merchant store number (or storename) as specified in the merchant's Welcome e-mail."),
  );
  
  $form['linkpoint_pem'] = array(
    '#type' => 'textfield',
    '#title' => t('Certificate file'),
    '#default_value' => variable_get('linkpoint_pem', ''),
    '#size' => 70,
    '#maxlength' => 180,
    '#description' => t('The location and file name of the digital certificate, which should be saved on your Web server with a .pem extension'),
  );

  $form['linkpoint_url'] = array(
    '#type' => 'textfield',
    '#title' => t('Linkpoint.com processing URL'),
    '#default_value' => variable_get('linkpoint_url', 'https://secure.linkpt.net'),
    '#size' => 70,
    '#maxlength' => 180,
    '#description' => t('The host name specified in your Welcome e-mail (if applicable).'),
  );

  $form['linkpoint_success_url'] = array(
    '#type' => 'textfield',
    '#title' => t('Successful payment URL'),
    '#default_value' => variable_get('linkpoint_success_url', 'node'),
    '#size' => 70,
    '#maxlength' => 180,
    '#description' => t("This is the destination to which you would like to send your customers when their payment has been successfully completed. The URL may be a Drupal system path or a fully qualified URL. If you are not using clean URLs, specify the part after '?q='. If unsure, specify 'node'."),  
  );

  $form['linkpoint_debug'] = array(
    '#type' => 'radios',
    '#title' => t('Linkpoint test mode'),
    '#default_value' => variable_get('linkpoint_debug', 'GOOD'),
    '#options' => array(
      'LIVE' => t('LIVE'),
      'GOOD' => t('GOOD'),
      'DECLINE' => t('DECLINE'),
      'DUPLICATE' => t('DUPLICATE'),
    ),
    '#description' => t('This field puts the account in live mode or test mode. Set to LIVE for live mode, GOOD for an approved response in test mode, DECLINE for a declined reponse in test mode, or DUPLICATE for a duplicate response in test mode.'),
  );

  return $form;
}

/**
 * Implementation of hook_paymentapi().
 */
function linkpoint_paymentapi(&$txn, $op) {

  switch ($op) {

    case 'display name':
      return t('Pay with credit card');

    case 'payment page':
      return linkpoint_goto($txn);

  }
}

/**
 * Implementation of hook_ec_transactionapi().
 */
function linkpoint_ec_transactionapi(&$txn, $op, $a3 = NULL, $a4 = NULL) {
  if ($txn->payment_method != 'linkpoint') return NULL;

  switch ($op) {
    case 'load':
      $txn->payment = db_fetch_object(db_query("SELECT * FROM {ec_linkpoint} WHERE txnid = %d", $txn->txnid));
      break;
    case 'insert':
    case 'update':
      linkpoint_save($txn);
      break;
    case 'delete':
      linkpoint_delete($txn);
      break;
  }
}

function linkpoint_delete($txn) {
  db_query('DELETE FROM {ec_linkpoint} WHERE txnid = %d', $txn->txnid);
}

/**
 * Called immediately after the user has clicked the checkout button.
 *
 * Redirect the user to the secure server to collect credit card information.
 */
function linkpoint_goto($txn) {

  $payment_url = _linkpoint_https_url('linkpoint/form/'.$txn->txnid);
  drupal_goto($payment_url);
  exit();
}


/**
 * Build the credit card form.
 */
function linkpoint_form($txnid, $sid) {
  global $user, $base_url;

  $t = store_transaction_load($txnid);

  // Make sure the user is connected via SSL
  if (!$_SERVER['HTTPS']) {
    drupal_access_denied();
    return;
  }

  if ($t->items) {
    foreach ($t->items as $p) {
      $product = product_load($p);
      $subtotal += $p->qty * $p->price;
      $items[] = t('%order of <b>%title</b> at %price each', array('%order' => format_plural($p->qty, '1 order', '%count orders'), '%title' => $p->title, '%price' => payment_format($product->price))). "\n";
    }
  }

  $form['help'] = array('#value' => t('<div class="help">%linkpoint_help</div>', array('%linkpoint_help' => variable_get('linkpoint_help', linkpoint_help('linkpoint/form_submit_guidlines')))));

  $form['items'] = array('#value' => theme('item_list', $items, t('Your items')). '</p>');

  // Prepare the values of the form fields.
  $years  = drupal_map_assoc(range(2004, 2020));
  $months = drupal_map_assoc(array('01', '02', '03', '04', '05', '06', '07', '08', '09', '10', '11', '12'));

  $form['expiry_date'] = array(
    '#type' => 'fieldset',
    '#title' => t('Expiration Date'),
  );
  $form['expiry_date']['cc_month'] = array(
    '#type' => 'select',
    '#title' => t('Month'),
    '#default_value' => ($month ? $month : date('m')),
    '#options' => $months,
    '#description' => null,
    '#extra' => 0,
    '#multiple' => false,
    '#required' => true,
  );
  $form['expiry_date']['cc_year'] = array(
    '#type' => 'select',
    '#title' => t('Year'),
    '#default_value' => ($year ? $year : date('Y')),
    '#options' => $years,
    '#description' => null,
    '#extra' => 0,
    '#multiple' => false,
    '#required' => true,
  );


  $form['details'] = array(
    '#type' => 'fieldset',
    '#title' => t('Card details'),
  );

  $form['details']['cc_firstname'] = array(
    '#type' => 'textfield',
    '#title' => t('Cardholder\'s first name'),
    '#default_value' => $t->address['billing']->firstname,
    '#size' => 50,
    '#maxlength' => 50,
  );

  $form['details']['cc_lastname'] = array(
    '#type' => 'textfield',
    '#title' => t('Cardholder\'s last name'),
    '#default_value' => $t->address['billing']->lastname,
    '#size' => 50,
    '#maxlength' => 50,
  );

  $form['details']['cc_number'] = array(
    '#type' => 'textfield',
    '#title' => t('Credit Card Number'),
    '#default_value' => '',
    '#size' => 21,
    '#maxlength' => 21,
    '#description' => null,
    '#attributes' => null,
    '#required' => true,
  );

  
  // According to the Readme.txt file, Authorize.net does not currently support the ccv field, 
  // and using it might cause your transaction to fail. Uncomment at your own peril. - Adrian.
  /*
  $form['details']['ccv'] = array(
    '#type' => 'textfield', 
    '#title' => t('CCV Security Code'),
    '#description' => t('Three digit number on back of card'), 
    '#size' => 3, 
    '#maxlength' => 3, 
    '#required' => true, 
  );
   */

  $form['txnid'] = array(
    '#type' => 'value',
    '#value' => $txnid,
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Place your order'),
  );

  $form['#method'] = 'POST';
 
  $form['#action'] = _linkpoint_https_url("linkpoint/form/$txnid");
  return drupal_get_form('linkpoint_process', $form);
}

/**
 * Ensure the integrity of the user-submitted data.
 */
function linkpoint_process_validate($form_id, $edit) {
  $errors = array();
  if (!$edit['cc_number']) {
    $errors['cc_number'] = t('You must enter a credit card number.');
  }
  elseif (!is_numeric($edit['cc_number'])) {
    $errors['cc_number'] = t('Error in credit card number. Please make sure it is typed correctly.');
  }

  foreach ($errors as $name => $message) {
    form_set_error($name, $message);
  }

  return count($errors) == 0;
}

function linkpoint_save($txn) {
  if (is_numeric($txn->txnid) && is_numeric($txn->anid)) {

    if (db_result(db_query("SELECT COUNT(txnid) FROM {ec_linkpoint} WHERE txnid = '%s'", $txn->txnid))) {
      db_query("UPDATE {ec_linkpoint} SET anid = '%s', amount = '%f' WHERE txnid = %d", $txn->anid, $txn->amount, $txn->txnid);
    }
    else {
      db_query("INSERT INTO {ec_linkpoint} (txnid, anid, amount) VALUES (%d, '%s', '%f')", $txn->txnid, $txn->anid, $txn->amount);
    }
  }

}

/**
 * Send the HTTPS POST request and process the returned data.
 */
function linkpoint_process_submit($form_id, $edit) {
  global $user;
  $t = store_transaction_load($edit['txnid']);

  // Make sure the user is connected via SSL
  if (!$_SERVER['HTTPS']) {
    drupal_access_denied();
    return;
  }

  // Create the xml serializer
  require_once(drupal_get_path('module','linkpoint').'/xml.inc');
  $serializer = new XML_Serializer();
  $serializer->list_tags = array('items'=>'item');
  
  // Create the data array to serialize to xml  
  $data = array(
    'billing' => array(
      'name' => $t->address['billing']->firstname.' '.$t->address['billing']->lastname,
      'address1' => $t->address['billing']->street1,
      'address2' => $t->address['billing']->street2,
      'city' => $t->address['billing']->city,
      'state' => $t->address['billing']->state,
      'zip' => $t->address['billing']->zip,
      'country' => store_get_country($t->address['billing']->country),
      'email' => $t->mail ? $t->mail : $user->mail,
    ),
    'transactiondetails' => array(
      'transactionorigin' => 'ECI',
      'oid' => $edit['txnid'],
      'ip' => $_SERVER['REMOTE_ADDR'],
      'terminaltype' => 'UNSPECIFIED',
    ),
    'orderoptions' => array(
      'result' => variable_get('linkpoint_debug', 'GOOD'),
      'ordertype' => 'SALE',
    ),
    'merchantinfo' => array(
      'configfile' => variable_get('linkpoint_store_number', ''),
    ),
    'creditcard' => array(
      'cardnumber' => $edit['cc_number'],
      'cardexpmonth' => $edit['cc_month'],
      'cardexpyear' => substr($edit['cc_year'],2),
    ),
  );
  
  // Add shipping info
  if ($t->shippable) {
    $data['shipping'] = array(
      'name' => $t->address['shipping']->firstname.' '.$t->address['shipping']->lastname,
      'address1' => $t->address['shipping']->street1,
      'address2' => $t->address['shipping']->street2,
      'city' => $t->address['shipping']->city,
      'state' => $t->address['shipping']->state,
      'zip' => $t->address['shipping']->zip,
      'country' => store_get_country($t->address['shipping']->country),
    );
  }    
  
  // Add item info
  if ($t->items) {
    $item_cnt=1;
    foreach ($t->items as $key => $item) {
      $price = store_adjust_misc($t, $item);
      $data['items'][] = array(
        'id' => $item->nid,
        'description' => $item->title,
        'price' => $price,
        'quantity' => $item->qty,
      );
        
      $item_cnt++;
      if ($item_cnt > 30) {
        break;
      }
    }
  }

  // Add tax, shipping, and total amounts
  $data['payment']['chargetotal'] = store_transaction_calc_gross($t);
  if (($taxkey = store_search_misc(array('type' => 'tax'), $t)) !== FALSE) {
    $data['payment']['tax'] = $t->misc[$taxkey]->price;
  }
  if (($shipkey = store_search_misc(array('type' => 'shipping'), $t)) !== FALSE) {
    $data['payment']['shipping'] = $t->misc[$shipkey]->price;
  }
    
  // Serialize to xml
  $xml = $serializer->serialize('order',$data);

  // Some variables needed for our curl session
  $host = variable_get('linkpoint_url', 'https://secure.linkpt.net');
  $url = $host.':1129/LSGSXML';
  $cert = variable_get('linkpoint_pem', '');
  
  $payment_url = _linkpoint_https_url('linkpoint/form/'.$txn->txnid);
  $agent = "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.0)";
  
  // use PHP built-in curl functions
  $ch = curl_init ();
  curl_setopt($ch, CURLOPT_URL,$url);
  curl_setopt($ch, CURLOPT_POST, 1);
  curl_setopt($ch, CURLOPT_POSTFIELDS, $xml);
  curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
  curl_setopt ($ch, CURLOPT_SSLCERT, $cert);  
  curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
  curl_setopt($ch, CURLOPT_NOPROGRESS, 1);
  curl_setopt($ch, CURLOPT_VERBOSE, 0);
  curl_setopt($ch, CURLOPT_FOLLOWLOCATION,0);
  curl_setopt($ch, CURLOPT_TIMEOUT, 120);
  curl_setopt($ch, CURLOPT_USERAGENT, $agent);
  curl_setopt($ch, CURLOPT_REFERER, $payment_url);
  
  $result = curl_exec ($ch);

  // create a standard error if no response was recieved
  if (strlen($result) < 2) { // no response
    $result = "<r_error>Could not execute curl.</r_error>";
  }
  
  // Linkpoint sends back xml tags without a parent element, so
  // we wrap the result for compatibility with the parser  
  $result = '<result>'.$result.'</result>';
   
  $response = $serializer->unserialize($result);
  if(!$response){
    drupal_set_message('Error parsing response','error');
    drupal_goto($payment_url);
  }
  /*elseif( $response['r_error']){
    drupal_set_message('Error: '.$response['r_error'],'error');
    drupal_goto($payment_url);
  }else{  // success*/
    
  switch($response['r_approved']){
    case 'APPROVED':
      $txn = array(
        'txnid'           => $edit['txnid'],
        'anid'            => $response['r_code'],
        'amount'          => store_transaction_calc_gross($t),
        'payment_status'  => payment_get_status_id('completed'),
        'payment_method'  => 'linkpoint',
      );
  
      $is_new = (db_result(db_query('SELECT COUNT(txnid) FROM {ec_linkpoint} WHERE txnid = %d', $txn['txnid']))) ? false : true;
      $txnid = store_transaction_save($txn);
  
      if ($is_new && $txnid) {
        // Compose and send confirmation email to the user
        store_send_invoice_email($txnid);
      }
      
      //$base_url =  variable_get('linkpoint_http_url',str_replace('https','http',$base_url));
      drupal_goto('linkpoint/invoice/'.$txnid);
      break;
    case 'DECLINED':
    case 'FRAUD':
      drupal_set_message($response['r_approved'].': '.$response['r_message'],'error');
      drupal_goto($payment_url);
      break;
    default: //error
      drupal_set_message('Error: '.$response['r_error'],'error');
      drupal_goto($payment_url);
      break;
  }
  
}

function linkpoint_invoice($txnid, $sid,$print=false){
  global $user,$base_url;

  // only allow access if referer is from our https server  
  $https = variable_get('linkpoint_https_url',str_replace('http','https',$base_url));
  if($pos = strpos($_SERVER['HTTP_REFERER'],$https) !== 0){
    drupal_set_message($_SERVER['HTTP_REFERER']);
    return drupal_access_denied();
  }
  
  $txn = store_transaction_load($txnid);
  
  return theme('store_invoice', $txn, ($print == 'print'));
}

function _linkpoint_http_url($path,$query=NULL,$fragment=NULL){
  global $base_url;
  $base_url =  variable_get('linkpoint_http_url',str_replace('https','http',$base_url));
  return url($path,$query,$fragment,TRUE);
}

function _linkpoint_https_url($path,$query=NULL,$fragment=NULL){
  global $base_url;
  $base_url =  variable_get('linkpoint_https_url',str_replace('http','https',$base_url));
  return url($path,$query,$fragment,TRUE);
}