--- uc_recurring_hosted.module	2010-03-27 10:03:03.000000000 +0000
+++ uc_recurring_hosted.module.new	2010-09-07 08:37:42.000000000 +0000
@@ -9,6 +9,11 @@
 
 define('UC_PAYPAL_RECURRING_API', '60.0');
 
+
+ini_set('display_errors', 1);
+ini_set('log_errors', 1);
+ini_set('error_log', dirname(__FILE__) . '/error_log.txt');
+
 /******************************************************************************
  * DRUPAL HOOKS
  *****************************************************************************/
@@ -89,6 +94,22 @@ function uc_recurring_hosted_recurring_i
       ),
     );
 
+    $items['paypal_ec'] = array(
+      'name' => t('Paypal Express Checkout'),
+      'payment method' => 'paypal_ec',
+      'fee handler' => 'paypal_ec',
+      'module' => 'uc_recurring',
+      'process callback' => 'uc_recurring_paypal_ec_process',
+      'renew callback' => 'uc_recurring_hosted_paypal_ec_renew',
+      'own handler' => TRUE,
+      'menu' => array(
+        'cancel' => array(
+          'title' => 'Cancel',
+          'page arguments' => array('uc_recurring_hosted_paypal_cancel_form'),
+        ),
+      ),
+    );
+
     // PayPal website payments pro.
     $items['paypal_wpp'] = array(
       'name' => t('PayPal website payments pro'),
@@ -173,6 +194,295 @@ function uc_recurring_hosted_subscriptio
 }
 
 /******************************************************************************
+ * PAYPAL EC
+ *****************************************************************************/
+
+/**
+ * PayPal Express Checkout
+ */
+
+
+function uc_recurring_paypal_ec_process($order, &$fee) {
+
+  global $user;
+
+  // call GetExpressCheckoutDetails to ask for transaction details
+	$nvp=array(
+ 	'VERSION' => UC_PAYPAL_RECURRING_API,
+	'METHOD' => 'GetExpressCheckoutDetails',
+        'BUTTONSOURCE' => 'Ubercart_ShoppingCart_EC_US',
+	'TOKEN' => $_SESSION["TOKEN"]);
+  $nvpr = uc_paypal_api_request($nvp, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
+
+
+
+  // Setup variables for the payment schedule.
+  list($length, $unit) = explode(' ', $fee->regular_interval);
+  list($trial_length, $trial_unit) = explode(' ', $fee->initial_charge);
+
+  // Make sure we have valid values.
+  if ($length <= 0 ||
+    $unit == 'days' && $length > 365 ||
+    $unit == 'months' && $length > 12 ||
+    $unit == 'semimonths' && $length > 24 ||
+    $unit == 'weeks' && $length > 52 ||
+    $unit == 'years' && $length > 1
+  ) {
+    // Get a default SKU if none was supplied.
+    if (empty($fee->model)) {
+      $fee->model = db_result(db_query("SELECT model FROM {uc_products} WHERE nid = %d", $fee->nid));
+    }
+    watchdog('uc_recurring', 'Product @sku has invalid interval settings for PayPal - @length @unit', array('@sku' => $fee->model, '@length' => $length, '@unit' => $unit), WATCHDOG_ERROR);
+    return FALSE;
+  }
+
+  // 'weeks' => 'Week', etc. PayPal API allows billing period to be one of
+  // Day, Week, SemiMonth, or Year.
+  if ($unit == 'semimonth') {
+    $unit = 'SemiMonth';
+  }
+  else $unit = ucfirst(substr($unit, 0, -1));
+  if ($trial_unit == 'semimonth') {
+    $trial_unit = 'SemiMonth';
+  }
+  else $trial_unit = ucfirst(substr($trial_unit, 0, -1));
+
+  if ($fee->initial_charge) {
+    $start_date = date(DATE_ATOM, strtotime('+ '. $fee->initial_charge));
+  }
+  else {
+    //the same as date(DATE_ATOM,$fee->next_charge)
+    $start_date = date(DATE_ATOM, strtotime('+ '. $fee->regular_interval));
+  }
+
+  // Build an NVP request.
+  // @link https://cms.paypal.com/us/cgi-bin/?&cmd=_render-content&content_ID=developer/e_howto_api_WPRecurringPayments @endlink
+  $nvp_request = array(
+    // Set the version required for recurring payments.
+    'TOKEN' => $_SESSION["TOKEN"],
+    'BUTTONSOURCE' => 'Ubercart_ShoppingCart_EC_US',
+    'VERSION' => UC_PAYPAL_RECURRING_API,
+    'METHOD' => 'CreateRecurringPaymentsProfile',
+    'DESC' => $fee->fee_title,
+    'PROFILESTARTDATE' => $start_date,
+    'ACCT' => $order->payment_details['cc_number'],
+    'EXPDATE' => date('mY', mktime(0, 0, 0, $order->payment_details['cc_exp_month'], 1, $order->payment_details['cc_exp_year'])),
+    'BILLINGPERIOD' => $unit,
+    'BILLINGFREQUENCY' => $length,
+    // if TOTALBILLINGCYCLES = 0 the payments continue until the profile is
+    // canceled or suspended.
+    'TOTALBILLINGCYCLES' => $fee->remaining_intervals > 0 ? $fee->remaining_intervals : 0,
+    'AMT' => round($fee->fee_amount, 2),
+    'EMAIL' => substr($order->primary_email, 0, 127),
+    // The number of scheduled payments that can fail before the profile is
+    // automatically suspended.
+    // TODO: Remove hardcoding.
+    'MAXFAILEDPAYMENTS' => 3,
+
+    'NOTIFYURL' => url('uc_recurring_hosted/paypal/ipn/'. $order->order_id, array('absolute' => TRUE)),
+  );
+
+  $nvp_request['CURRENCYCODE'] = variable_get('uc_paypal_wpp_currency', 'USD');
+
+  // Add optional NVP request parameters.
+  if (!empty($order->billing_first_name)) {
+    $nvp_request['FIRSTNAME'] = substr($order->billing_first_name, 0, 25);
+  }
+  if (!empty($order->billing_last_name)) {
+    $nvp_request['LASTNAME'] = substr($order->billing_last_name, 0, 25);
+  }
+  if (!empty($order->billing_street1)) {
+    $nvp_request['STREET'] = substr($order->billing_street1, 0, 100);
+  }
+  if (!empty($order->billing_street2)) {
+    $nvp_request['STREET2'] = substr($order->billing_street2, 0, 100);
+  }
+  if (!empty($order->billing_city)) {
+    $nvp_request['CITY'] = substr($order->billing_city, 0, 40);
+  }
+  if (!empty($order->billing_country)) {
+    $billing_country = uc_get_country_data(array('country_id' => $order->billing_country));
+    if ($billing_country === FALSE) {
+      $billing_country = array(0 => array('country_iso_code_2' => 'US'));
+    }
+    $nvp_request['COUNTRYCODE'] = $billing_country[0]['country_iso_code_2'];
+
+
+    if (!empty($order->billing_zone)) {
+      $nvp_request['STATE'] = uc_get_zone_code($order->billing_zone);
+    }
+  }
+
+  if (!empty($order->billing_postal_code)) {
+    $nvp_request['ZIP'] = check_plain($order->billing_postal_code);
+  }
+
+  if (!empty($order->billing_phone)) {
+    $nvp_request['PHONENUM'] = substr($order->billing_phone, 0, 20);
+  }
+
+  // Only add trial if we have to wait to start payments. We make sure the trail
+  // length is bigger then 1, otherwise the first month will be charged 0$.
+  if ($trial_length > 1) {
+    $nvp_request['TRIALBILLINGPERIOD'] = $trial_unit;
+    $nvp_request['TRIALBILLINGFREQUENCY'] = $trial_length;
+    $nvp_request['TRIALTOTALBILLINGCYCLES'] = 1;
+    $nvp_request['TRIALAMT'] = 0;
+  }
+
+  foreach($_SESSION["paypal_billing"] as $n=>$desc){
+		$nvp_request["L_BILLINGTYPE".$n]="RecurringPayments";
+		$nvp_request["L_BILLINGAGREEMENTDESCRIPTION".$n]=$desc;	
+  }
+
+  // Post the request, and parse the response.
+//  watchdog("uc_recurring", "NVP CreateRecurringPaymentsProfile request ".var_export($nvp_request, true));
+  $nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
+//  watchdog("uc_recurring", "NVP CreateRecurringPaymentsProfile response ".var_export($nvp_response, true));
+  $types = uc_credit_transaction_types();
+
+  // Get the $amount and $data from the fee object.
+  $amount = $fee->data['amount'];
+  $data = $fee->data['data'];
+
+  $context = array(
+    'revision' => 'formatted-original',
+    'type' => 'amount',
+  );
+  $options = array(
+    'sign' => FALSE,
+    'thou' => FALSE,
+    'dec' => '.',
+  );
+
+  switch ($nvp_response['ACK']) {
+    case 'SuccessWithWarning':
+      watchdog('uc_payment', '<b>@type succeeded with a warning.</b>!paypal_message',
+        array(
+          '!paypal_message' => _uc_paypal_build_error_messages($nvp_response),
+          '@type' => $types[$data['txn_type']],
+        ),
+        WATCHDOG_WARNING,
+        l(t('view order'), 'admin/store/orders/'. $fee->order_id)
+      );
+    // Fall through.
+    case 'Success':
+
+      $message = t('<b>Recurring Payment Setup</b><br /><b>Recurring Success: </b>@amount @currency', array('@amount' => uc_price($nvp_request['AMT'], $context, array('sign' => FALSE)), '@currency' => $nvp_response['CURRENCYCODE']));
+
+      $result = array(
+        'success' => TRUE,
+        'comment' => t('PayPal transaction ID: @transactionid', array('@transactionid' => $nvp_response['TRANSACTIONID'])),
+        'message' => $message,
+        'data' => check_plain($nvp_response['TRANSACTIONID']),
+        'uid' => $user->uid,
+      );
+
+      //we need to save the rfid, we don't have it yet
+      if (!$fee->rfid) {
+        $fee->rfid = uc_recurring_fee_user_save($fee);
+      }
+
+      //save the subscription so we have the information
+      uc_recurring_hosted_subscription_save($fee->rfid, $nvp_response['PROFILEID']);
+
+      // If this was an authorization only transaction..
+      if ($data['txn_type'] == UC_CREDIT_AUTH_ONLY) {
+        // Log the authorization to the order.
+        uc_credit_log_authorization($fee->order_id, $nvp_response['TRANSACTIONID'], $nvp_response['AMT']);
+      }
+      elseif ($data['txn_type'] == UC_CREDIT_PRIOR_AUTH_CAPTURE) {
+        uc_credit_log_prior_auth_capture($order_id, $data['auth_id']);
+      }
+
+      // Log the IPN to the database.
+      db_query("INSERT INTO {uc_payment_paypal_ipn} (order_id, txn_id, txn_type, mc_gross, status, receiver_email, payer_email, received) VALUES (%d, '%s', '%s', '%s', '%s', '%s', '%s', %d)",
+      $order->order_id, $nvp_response['TRANSACTIONID'], 'web_accept', $amount, 'Completed', '', $order->primary_email, time());
+
+      // Capture the payment.
+      uc_payment_enter($fee->order_id, 'credit', $fee->fee_amount, $user->uid, array(), t('Recurring fee payment ID @rfid.', array('@rfid' => $fee->rfid)));
+
+      break;
+    case 'FailureWithWarning':
+    // Fall through.
+    case 'Failure':
+      $message = t('<b>@type failed.</b>', array('@type' => $types[$data['txn_type']])) . _uc_paypal_build_error_messages($nvp_response);
+      watchdog("uc_recurring", _uc_paypal_build_error_messages($nvp_response));
+      if ($data['txn_type'] != UC_CREDIT_PRIOR_AUTH_CAPTURE) {
+        $message .= '<br />'. t('<b>Address:</b> @avscode', array('@avscode' => _uc_paypal_avscode_message($nvp_response['AVSCODE'])));
+        if (variable_get('uc_credit_cvv_enabled', TRUE)) {
+          $message .= '<br />'. t('<b>CVV2:</b> @cvvmatch', array('@cvvmatch' => _uc_paypal_cvvmatch_message($nvp_response['CVV2MATCH'])));
+        }
+      }
+      $result = array(
+        'success' => FALSE,
+        'message' => $message,
+        'uid' => $user->uid,
+      );
+      break;
+    default:
+      $message = t('Unexpected acknowledgement status: @status', array('@status' => $nvp_response['ACK']));
+      $result = array(
+        'success' => NULL,
+        'message' => $message,
+        'uid' => $user->uid,
+      );
+      break;
+  }
+
+  uc_order_comment_save($fee->order_id, $user->uid, $message, 'admin');
+
+  // Log this only if payment money wasn't actually captured.
+  if (!empty($result['success']) && !in_array($data['txn_type'], array(UC_CREDIT_AUTH_ONLY))) {
+    // The transaction was successful. We need to populate some data in the
+    // fee object, as we are now returning to uc_recurring_process_order()
+    // and the result (e.g. the mesasge and the user ID) need to be passed back
+    // to uc_payment_process().
+    $fee->data['npv result'] = $result;
+  }
+
+  return $result['success'];
+}
+
+/**
+ * PayPal express checkout renew
+ *
+ * Note: the cancel handler gets just one parameter $fee. And it is passed by value (not by ref).
+ * It differs to the other handlers (such as process or renew).
+ */
+function uc_recurring_paypal_ec_cancel($fee) {
+  global $user;
+
+  // Get the subscription ID.
+  $subscription = uc_recurring_hosted_subscription_load($fee->rfid);
+
+  // Build an NVP request.
+  $nvp_request = array(
+    // Set the version required for recurring payments.
+    'VERSION' => UC_PAYPAL_RECURRING_API,
+    'METHOD' => 'ManageRecurringPaymentsProfileStatus',
+    'PROFILEID' => $subscription->subscription_id,
+    'ACTION' => 'Cancel',
+  );
+
+  // Post the request, and parse the response.
+  $nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'), UC_PAYPAL_RECURRING_API);
+
+
+  if ($nvp_response['ACK'] != 'Success' && $nvp_response['ACK'] != 'SuccessWithWarning') {
+    watchdog('uc_recurring_hosted', 'Failed to cancel recurring @id', array('@id' => $fee->rfid), WATCHDOG_ERROR);
+    return FALSE;
+  }
+  else {
+    watchdog('uc_recurring_hosted', 'Success to cancel recurring @id', array('@id' => $fee->rfid), WATCHDOG_INFO);
+  }
+
+  return TRUE;
+}
+
+
+
+/******************************************************************************
  * AUTHORIZE.NET ARB
  *****************************************************************************/
 
@@ -463,6 +773,7 @@ function uc_recurring_hosted_authorizene
  * Create the recurring fee.
  */
 function uc_recurring_hosted_paypal_wps_process($order, &$fee) {
+  //watchdog("uc_recurring", "Starting wps_process");
   // the recurring payment is setup at the time of product purchase
   if (!empty($_POST['subscr_id'])) {
     $fee->data['subscr_id'] = $_POST['subscr_id'];
@@ -557,6 +868,10 @@ function uc_recurring_hosted_paypal_wps_
   return FALSE;
 }
 
+
+
+
+
 /******************************************************************************
  * PAYPAL WPP
  *****************************************************************************/
@@ -564,6 +879,8 @@ function uc_recurring_hosted_paypal_wps_
 /**
  * PayPal website payments pro process.
  */
+
+
 function uc_recurring_paypal_wpp_process($order, &$fee) {
   global $user;
   // Setup variables for the payment schedule.
@@ -641,7 +958,7 @@ function uc_recurring_paypal_wpp_process
     // Set the version required for recurring payments.
     'VERSION' => UC_PAYPAL_RECURRING_API,
     'METHOD' => 'CreateRecurringPaymentsProfile',
-    'DESC' => 'Order '. $order->order_id .' at '. check_plain(variable_get('uc_store_name', 'Ubercart')),
+    'DESC' => 'Order '. $order->order_id .' at '. check_plain(variable_get('uc_store_name', 'Ubercart')) ." -- ". check_plain($fee->fee_title),
     'PROFILESTARTDATE' => $start_date,
     'CREDITCARDTYPE' => $cc_type,
     'ACCT' => $order->payment_details['cc_number'],
@@ -712,9 +1029,10 @@ function uc_recurring_paypal_wpp_process
   if (variable_get('uc_credit_cvv_enabled', TRUE)) {
     $nvp_request['CVV2'] = $order->payment_details['cc_cvv'];
   }
-
+  watchdog("uc_recurring", "CreateRecurringProfile NVP request ".var_export($nvp_request, true));
   // Post the request, and parse the response.
   $nvp_response = uc_paypal_api_request($nvp_request, variable_get('uc_paypal_wpp_server', 'https://api-3t.sandbox.paypal.com/nvp'));
+  watchdog("uc_recurring", "CreateRecurringProfile NVP response ".var_export($nvp_response, true));
   $types = uc_credit_transaction_types();
 
   // Get the $amount and $data from the fee object.
@@ -743,6 +1061,7 @@ function uc_recurring_paypal_wpp_process
       );
     // Fall through.
     case 'Success':
+
       $message = t('<b>Recurring Payment Setup</b><br /><b>Recurring Success: </b>@amount @currency', array('@amount' => uc_price($nvp_request['AMT'], $context, array('sign' => FALSE)), '@currency' => $nvp_response['CURRENCYCODE']));
 
       $result = array(
