? .svn
? cs.diff
? uc_cs_hop.diff
Index: uc_cybersource.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ubercart/payment/uc_cybersource/uc_cybersource.info,v
retrieving revision 1.4.2.3
diff -u -p -r1.4.2.3 uc_cybersource.info
--- uc_cybersource.info	7 Nov 2008 21:13:27 -0000	1.4.2.3
+++ uc_cybersource.info	26 May 2010 14:36:04 -0000
@@ -1,6 +1,6 @@
 ; $Id: uc_cybersource.info,v 1.4.2.3 2008/11/07 21:13:27 islandusurper Exp $
 name = CyberSource
-description = Enable to process payments using CyberSource Silent Order POST.
+description = Enable to process payments using the CyberSource Silent Order POST and Hosted Order Page services.
 dependencies[] = uc_payment
 dependencies[] = uc_credit
 package = Ubercart - payment
Index: uc_cybersource.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ubercart/payment/uc_cybersource/uc_cybersource.install,v
retrieving revision 1.1.2.5
diff -u -p -r1.1.2.5 uc_cybersource.install
--- uc_cybersource.install	19 Jan 2009 20:31:21 -0000	1.1.2.5
+++ uc_cybersource.install	26 May 2010 14:36:05 -0000
@@ -44,6 +44,13 @@ function uc_cybersource_requirements($ph
   return $requirements;
 }
 
+/*
+ * Iplementation of hook_install.
+ */
+function uc_cybersource_install() {
+  drupal_install_schema('uc_cybersource');
+}
+
 function uc_cybersource_uninstall() {
   // Delete related variables all at once.
   db_query("DELETE FROM {variable} WHERE name LIKE 'uc_cybersource_%%' OR name LIKE 'cs_ship_from_%%'");
@@ -70,3 +77,76 @@ function uc_cybersource_update_1() {
   return array();
 }
 
+/*
+ * Implementation of hook_schema.
+ */
+function uc_cybersource_schema() {
+  $schema['uc_payment_cybersource_hop_post'] = array(
+    'fields' => array(
+      'order_id' => array(
+        'description' => t('The order ID as provided by the store.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'request_id' => array(
+        'description' => t('Uniqe id assigned by CyberSource that identifies payment request.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'request_token' => array(
+        'description' => t('Verification token associated with the request ID.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'reconciliation_id' => array(
+        'description' => t('Reference number generated by CyberSource that you use to reconcile your CyberSource reports with your processor reports.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'gross' => array(
+        'description' => t('The approved payment amount from CyberSource.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'decision' => array(
+        'description' => t('CyberSource decision for payment request.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'reason_code' => array(
+        'description' => t('A code for decision.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+      'payer_email' => array(
+        'description' => t('The e-mail address of the buyer.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+     'received' => array(
+        'description' => t('The IPN receipt timestamp.'),
+        'type' => 'int', 
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+  );
+  return $schema;
+}
\ No newline at end of file
Index: uc_cybersource.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/ubercart/payment/uc_cybersource/uc_cybersource.module,v
retrieving revision 1.4.2.12
diff -u -p -r1.4.2.12 uc_cybersource.module
--- uc_cybersource.module	1 Apr 2010 18:28:25 -0000	1.4.2.12
+++ uc_cybersource.module	26 May 2010 14:36:06 -0000
@@ -5,7 +5,9 @@
  * @file
  * A module used for CyberSource's Silent Order POST method of payment.
  *
- * Development sponsored by Acquia - http://acquia.com
+ * Development sponsored by:
+ * Acquia - http://acquia.com
+ * Growing Venture Solutions - http://growingventuresolutions.com
  */
 
 
@@ -13,12 +15,13 @@
  * Implementation of hook_menu().
  */
 function uc_cybersource_menu() {
-  $items['cs/receipt'] = array(
+  $items['cybersource/hop-post'] = array(
     'title' => 'Payment received',
-    'page callback' => 'uc_cybersource_receipt',
-    'access callback' => 'uc_cybersource_receipt_access',
+    'page callback' => 'uc_cybersource_hop_post',
+    'access callback' => 'uc_cybersource_hop_post_access',
     'type' => MENU_CALLBACK,
   );
+  
   $items['admin/store/orders/%uc_order/cs_tax'] = array(
     'title' => 'Order Taxes',
     'page callback' => 'uc_cybersource_tax_test',
@@ -30,7 +33,7 @@ function uc_cybersource_menu() {
   return $items;
 }
 
-function uc_cybersource_receipt_access() {
+function uc_cybersource_hop_post_access() {
   return TRUE;
 }
 
@@ -41,6 +44,15 @@ function uc_cybersource_form_alter(&$for
   if ($form_id == 'uc_payment_gateways_form') {
     $form['#submit'][] = 'uc_cybersource_payment_gateway_settings_submit';
   }
+  // Add to the review page hidden form fields with data to post to CyberSource HOP.
+  if ($form_id == 'uc_cart_checkout_review_form' && ($order_id = intval($_SESSION['cart_order'])) > 0) {
+    $order = uc_order_load($order_id);
+    if ($order->payment_method == 'cybersource_hop') {
+      unset($form['submit']);
+      $form['#prefix'] = '<table style="display: inline; padding-top: 1em;"><tr><td>';
+      $form['#suffix'] = '</td><td>'. drupal_get_form('uc_cybersource_hop_form', $order) .'</td></tr></table>';
+    }
+  }
 }
 
 // Submit handler for payment gateway settings form to encrypt fields.
@@ -83,9 +95,72 @@ function uc_cybersource_payment_gateway(
   return $gateways;
 }
 
-// Validates a return receipt from CyberSource. Currently not functional.
-function uc_cybersource_receipt() {
-  drupal_goto('<front>');
+// Validates a return receipt via the Cybersource Hosted Order Page API.
+function uc_cybersource_hop_post() {
+  $hop = drupal_get_path('module', 'uc_cybersource_hop') .'/HOP.php';
+  require_once($hop);
+  $verify = VerifyTransactionSignature($_POST);
+  watchdog('uc_cybersource_hop', 'Receiving payment notification at URL for order @orderNumber',
+    array('@orderNumber' => $_POST['orderNumber'] ));
+
+  if (!isset($_POST['orderNumber'])) {
+    watchdog('uc_cybersource_hop', 'CS HOP attempted with invalid order number.', array(), WATCHDOG_ERROR);
+    return;
+  }
+
+  if (!$verify) {
+    watchdog('uc_cybersource_hop', 'Receiving invalid payment notification at URL for order @orderNumber. <pre>@debug</pre>',
+    array('@orderNumber' => $_POST['orderNumber'], '@debug' => print_r($_POST, TRUE) ));
+    return;
+  }
+
+  // Assign posted variables to local variables
+  $decision = check_plain($_POST['decision']);
+  $reason_code = check_plain($_POST['reasonCode']);
+  $reason = _parse_cs_reason_code($reason_code);
+  $payment_amount = check_plain($_POST['orderAmount']);
+  $payment_currency = check_plain($_POST['paymentCurrency']);
+  $request_id = check_plain($_POST['requestID']);
+  $request_token = check_plain($_POST['orderPage_requestToken']);
+  $reconciliation_id = check_plain($_POST['reconciliationID']);
+  $order_id = check_plain($_POST['orderNumber']);
+  $payer_email = check_plain($_POST['billTo_email']);
+  $order = uc_order_load($_POST['orderNumber']);
+
+  switch ($decision) {
+    case 'ACCEPT':
+      watchdog('uc_cybersource_hop', 'Cybersource verified successful payment.');
+      $duplicate = db_result(db_query("SELECT COUNT(*) FROM {uc_payment_cybersource_hop_post} WHERE order_id = '%s' AND decision = 'ACCEPT'", $order_id));
+        if ($duplicate > 0) {
+        watchdog('uc_cybersource_hop', 'CS HOP transaction for order @order-id has been processed before.', array('@order_id' => $order_id), WATCHDOG_NOTICE);
+        return;
+      }
+      $sql = "INSERT INTO {uc_payment_cybersource_hop_post} (order_id, request_id, request_token, reconciliation_id, gross, decision, reason_code, payer_email, received) VALUES (%d, '%s', '%s', '%s', %f, '%s', '%s', '%s', %d)";
+        
+      db_query($sql, $order_id, $request_id, $request_token, $reconciliation_id, $payment_amount, $decision, $reason_code, $payer_email, time());
+      $context = array(
+        'revision' => 'formatted-original',
+        'type' => 'amount',
+      );
+      $options = array(
+        'sign' => FALSE,
+      );
+      $comment = t('CyberSource request ID: @txn_id', array('@txn_id' => $request_id));
+      uc_payment_enter($order_id, 'cybersource_hop', $payment_amount, $order->uid, NULL, $comment);
+      uc_cart_complete_sale($order);
+      uc_order_comment_save($order_id, 0, t('Payment of @amount @currency submitted through Cybersource with request ID @rid.', array('@amount' => uc_price($payment_amount, $context, $options), '@currency' => $payment_currency, '@rid' => $request_id)), 'order', 'payment_received');
+      break;
+    case 'ERROR':
+      uc_order_comment_save($order_id, 0, t("Payment error:@reason with request ID @rid", array('@reason' => $reason, '@rid' => '@request_id')), 'admin');
+      break;
+    case 'REJECT':
+      uc_order_comment_save($order_id, 0, t("Payment is rejected:@reason with request ID @rid", array('@reason' => $reason, '@rid' => '@request_id')), 'admin');
+      break;
+    case 'REVIEW':
+      uc_order_update_status($order_id, 'review');
+      uc_order_comment_save($order_id, 0, t('Payment is in review & not complete: @reason. Request ID @rid', array('@reason' => $reason, '@rid' => '@request_id')), 'admin');
+      break;
+  }
 }
 
 // Adds the CyberSource fields to the payment gateway settings form.
@@ -213,6 +288,182 @@ function uc_cybersource_settings_form() 
   return $form;
 }
 
+
+/**
+ * Defines payment method properties.
+ *
+ * @return
+ *  Returnes an array with properity and value pairs of CyberSource payment method.
+ */
+function uc_cybersource_payment_method() {
+  $methods[] = array(
+    'id' => 'cybersource_hop',
+    'name' => t('CyberSource Hosted Order Page'),
+    'title' => "Credit/Debit Cards Payment",
+    'review' => t('CyberSource'),
+    'desc' => t('Payment with CyberSource HOP Service.'),
+    'callback' => 'uc_payment_method_cybersource_hop',
+    'weight' => 1,
+    'checkout' => FALSE,
+    'no_gateway' => TRUE,
+  );
+
+  return $methods;
+}
+
+function uc_payment_method_cybersource_hop($op, $arg1) {
+  if ($op == 'settings') {
+    $form['uc_cybersource_hop_posturl'] = array(
+      '#type' => 'value',
+      '#title' => t('Cybersource HOP Post URL http://yourdomain/cybersource/hop-post'),
+      '#default_value' => variable_get('uc_cybersource_hop_posturl', 'cybersource/hop-post'),
+      '#description' => t('The post url in your Cybersource account need to be set to http://yourdomain/cybersource/hop-post'),
+    );
+    $form['uc_cybersource_hop_server'] = array(
+      '#type' => 'select',
+      '#title' => t('Cybersource HOP server'),
+      '#description' => t('Select between production/live or test mode.'),
+      '#options' => array(
+        'https://orderpagetest.ic3.com/hop/orderform.jsp' => t('Test Center'),
+        'https://orderpage.ic3.com/hop/orderform.jsp' => t('Production/Live')),
+      '#default_value' => variable_get('uc_cybersource_hop_server', 'https://orderpagetest.ic3.com/hop/orderform.jsp'),
+    );
+    $form['uc_cybersource_hop_receipt_path'] = array(
+      '#type' => 'textfield',
+      '#title' => t('Cybersource HOP Receipt path'),
+      '#description' => t('Users are redirected to this path after a CyberSource sucessfully process their payment.'),
+      '#default_value' => variable_get('uc_cybersource_hop_receipt_path', ''),
+    );
+    $form['uc_cybersource_cs_hop_button_text'] = array(
+      '#type' => 'textfield',
+      '#title' => t('CyberSource "Buy button" text'),
+      '#description' => t('This text appears on the button users press to process their payment on the Hosted Order Page.'),
+      '#default_value' => variable_get('uc_cybersource_cs_hop_button_text', 'Process payment'),
+    );
+    return $form;
+  }
+}
+/**
+ * Define values to be posted to CyberSource.
+ *
+ * @retun
+ *  Transaction data arrays are returned as hidden form values.
+ *
+ */
+function uc_cybersource_hop_form($form_state, $order) {
+  $hop = drupal_get_path('module', 'uc_cybersource_hop') .'/HOP.php';
+  require_once($hop);
+  if (!file_exists($hop)) {
+    drupal_set_message(t('Hosted Order Page requires the HOP.php provided by CyberSource.'));
+    return array('success' => FALSE);
+  }
+
+  $billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
+  $delivery_country = uc_get_country_data(array('country_id' => $order->delivery_country));
+  $data = array(
+    'billTo_firstName' => $order->billing_first_name,
+    'billTo_lastName' => $order->billing_last_name,
+    'billTo_street1' => $order->billing_street1,
+    'billTo_city' => $order->billing_city,
+    'billTo_country' => $billing_country[0]['country_iso_code_2'],
+    'billTo_state' => uc_get_zone_code($order->billing_zone),
+    'billTo_postalCode' => $order->billing_postal_code,
+    'billTo_email' => $order->primary_email,
+    'shipTo_firstName' => $order->delivery_first_name,
+    'shipTo_lastName' => $order->delivery_last_name,
+    'shipTo_street1' => $order->delivery_street1,
+    'shipTo_street2' => $order->delivery_street2,
+    'shipTo_city' => $order->delivery_city,
+    'shipTo_country' => $delivery_country[0]['country_iso_code_2'],
+    'shipTo_state' => uc_get_zone_code($order->delivery_zone),
+    'shipTo_postalCode' => $order->delivery_postal_code,
+
+  );
+  $shipping = 0;
+
+  foreach ($order->line_items as $item) {
+    if ($item['type'] == 'shipping') {
+      $shipping += $item['amount'];
+    }
+  }
+
+  $tax = 0;
+
+  if (module_exists('uc_taxes')) {
+    foreach (uc_taxes_calculate($order) as $tax_item) {
+      $tax += $tax_item->amount;
+    }
+  }
+
+  $context = array(
+    'revision' => 'formatted-original',
+    'type' => 'amount',
+  );
+  $options = array(
+    'sign' => FALSE,
+    'thou' => FALSE,
+    'dec' => '.',
+  );
+  $amount = uc_price($order->order_total - $shipping - $tax, $context, $options);
+  $currency = variable_get('uc_cybersource_hop_currency', 'USD');
+  $merchantID = getMerchantID();
+  $timestamp = getmicrotime();
+  $datax = $merchantID . $amount . $currency . $timestamp;
+  $pub = getPublicKey();
+  $serialNumber = getSerialNumber();
+  $pub_digest = hopHash($datax, $pub);
+  $data['amount'] = $amount;
+  $data['currency'] = $currency;
+  $data['merchantID'] = $merchantID;
+  $data['orderNumber'] = $order->order_id;
+  $data['orderPage_timestamp'] = $timestamp;
+  $data['orderPage_ignoreAVS'] = variable_get('uc_cybersource_hop_avs', 'true') == 'true' ? 'false' : 'true';
+  $data['orderPage_signaturePublic'] = $pub_digest;
+  $data['orderPage_version'] = '4';
+  $data['orderPage_serialNumber'] = $serialNumber;
+  $data['orderPage_transactionType'] = variable_get('uc_cybersource_hop_transaction_type', 'sale');
+  $data['orderPage_sendMerchantReceiptEmail'] = variable_get('uc_cybersource_hop_merchant_receipt_email', 'true');
+  $data['orderPage_sendMerchantURLPost'] = 'true';
+  //CyberSource posts payment confirmation to this URL. 
+  $data['orderPage_merchantURLPostAddress']= url('cybersource/hop-post', array('absolute' => TRUE));
+  $data['orderPage_buyButtonText'] = t('Checkout');
+  $receipt_url = variable_get('uc_cybersource_hop_receipt_path', '');
+  $receipt_url = url($receipt_url, array('absolute' => TRUE));
+  $data['orderPage_receiptResponseURL'] = $receipt_url;
+  $data['orderPage_buyButtonText'] = t(variable_get('uc_cybersource_cs_hop_button_text', 'Process payment'));
+  $data['comments'] = t('Order @order-id at @store-name', array('@order-id' => $order->order_id, '@store-name' => variable_get('uc_store_name', 'Our store')));
+  foreach ($data as $name => $value) {
+    if (!empty($value)) {
+      $form[$name] = array('#type' => 'hidden', '#value' => $value);
+    }
+  }
+
+  $form['#action'] = variable_get('uc_cybersource_hop_server', 'https://orderpagetest.ic3.com/hop/orderform.jsp');
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => variable_get('uc_cybersource_hop_checkout_button', t('Submit Order')),
+  );
+
+  // Invoke hook_order with case 'submit'.
+  foreach (module_implements('order') as $module) {
+    $result = module_invoke($module, 'order', 'submit', $order, NULL);
+    $msg_type = 'status';
+    if ($result[0]['pass'] === FALSE) {
+      $error = TRUE;
+      $msg_type = 'error';
+    }
+    if (!empty($result[0]['message'])) {
+      drupal_set_message($result[0]['message'], $msg_type);
+    }
+    if ($error) {
+      $_SESSION['do_review'] = TRUE;
+      drupal_goto('cart/checkout/review');
+    }
+  }
+
+  
+  return $form;
+}
 function uc_cybersource_charge($order_id, $amount, $data) {
   global $user;
 
