Index: uc_recurring.admin.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/uc_recurring/uc_recurring.admin.inc,v
retrieving revision 1.1.4.3
diff -u -p -r1.1.4.3 uc_recurring.admin.inc
--- uc_recurring.admin.inc	14 Jun 2009 06:14:35 -0000	1.1.4.3
+++ uc_recurring.admin.inc	29 Jun 2009 02:33:49 -0000
@@ -7,7 +7,9 @@
  *
  */
 
-// Displays a table for the administration of recurring fees.
+/**
+ * Displays a table for the administration of recurring fees.
+ */
 function uc_recurring_admin() {
   $output = drupal_get_form('uc_recurring_admin_filter_form');
 
@@ -44,6 +46,7 @@ function uc_recurring_admin() {
 
     // Get the $ops from the module implementing the handler.
     $callback = 'uc_recurring_'. $fee['fee_handler'] .'_fee_ops';
+
     if (function_exists($callback)) {
       $ops = $callback('fee_admin', $fee);
     }
@@ -70,7 +73,9 @@ function uc_recurring_admin() {
   return $output;
 }
 
-// Filter by a specific order ID.
+/**
+ * Filter by a specific order ID.
+ */
 function uc_recurring_admin_filter_form() {
   $form['type'] = array(
     '#type' => 'select',
@@ -104,7 +109,10 @@ function uc_recurring_admin_filter_form_
   }
 }
 
-// Confirm a recurring fee charge.
+
+/**
+ * Confirm a recurring fee charge.
+ */
 function uc_recurring_admin_charge_form($form_state, $uid) {
   $fee = uc_recurring_fee_load('user', $uid);
   $context = array(
@@ -139,7 +147,10 @@ function uc_recurring_admin_charge_form_
   $form_state['redirect'] = 'admin/store/orders/recurring/view/fee/'. arg(4);
 }
 
-// Let an admin edit a recurring fee.
+
+/**
+ * Let an admin edit a recurring fee.
+ */
 function uc_recurring_admin_edit_form() {
   drupal_add_css(drupal_get_path('module', 'uc_recurring') .'/uc_recurring.css');
 
@@ -218,7 +229,10 @@ function uc_recurring_admin_edit_form_su
   $form_state['redirect'] = 'admin/store/orders/recurring/view/fee/'. arg(4);
 }
 
-// Confirm a recurring fee deletion.
+
+/**
+ * Confirm a recurring fee deletion.
+ */
 function uc_recurring_admin_delete_form() {
   $form['submit'] = array(
     '#type' => 'submit',
@@ -235,4 +249,3 @@ function uc_recurring_admin_delete_form_
 
   $form_state['redirect'] = 'admin/store/orders/recurring';
 }
-
Index: uc_recurring.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/uc_recurring/uc_recurring.install,v
retrieving revision 1.1.4.3
diff -u -p -r1.1.4.3 uc_recurring.install
--- uc_recurring.install	3 Jun 2009 03:01:39 -0000	1.1.4.3
+++ uc_recurring.install	29 Jun 2009 02:33:49 -0000
@@ -134,6 +134,7 @@ function uc_recurring_schema() {
       'data' => array(
         'description' => t('Serialized array of extra data.'),
         'type' => 'text',
+        'serialize' => TRUE,
       ),
       'created' => array(
         'description' => t('Timestamp for when the fee was first attached to the user.'),
@@ -394,7 +395,7 @@ function uc_recurring_update_6005() {
   db_add_field($ret, 'uc_recurring_users', 'fee_title', array('type' => 'varchar', 'length' => 255, 'not null' => TRUE, 'default' => ''));
   db_change_field($ret, 'uc_recurring_extensions', 'pfid', 'pfid', array('type' => 'int', 'unsigned' => TRUE, 'not null' => FALSE, 'default' => NULL));
   // setup default extensions
-  db_query("INSERT INTO {uc_recurring_extensions} (pfid, rebill_attempt, time_to_extend) VALUES (NULL, 1, 259200), (NULL, 2, 432000), (NULL, 3, 0)"); 
+  db_query("INSERT INTO {uc_recurring_extensions} (pfid, rebill_attempt, time_to_extend) VALUES (NULL, 1, 259200), (NULL, 2, 432000), (NULL, 3, 0)");
 
   return $ret;
 }
Index: uc_recurring.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/uc_recurring/uc_recurring.module,v
retrieving revision 1.1.4.17
diff -u -p -r1.1.4.17 uc_recurring.module
--- uc_recurring.module	22 Jun 2009 01:35:35 -0000	1.1.4.17
+++ uc_recurring.module	29 Jun 2009 02:33:50 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: uc_recurring.module,v 1.1.4.17 2009/06/22 01:35:35 univate Exp $
+// $Id: uc_recurring.module,v 1.1.4.16 2009/06/19 23:28:55 univate Exp $
 
 /**
  * @file
@@ -40,6 +40,7 @@ function uc_recurring_menu() {
     'access callback' => 'uc_recurring_user_access',
     'access arguments' => array(1),
     'type' => MENU_LOCAL_TASK,
+    'file' => 'uc_recurring.pages.inc',
   );
   $items['user/%user/recurring/%/cancel'] = array(
     'title' => 'Cancel the recurring fee?',
@@ -106,9 +107,9 @@ function uc_recurring_menu() {
  * @param $account
  *   The user account being accessed
  * @param $rfid
- *   The id of the recurring fee being access, parsing NULL checks 
- *     if we can access any recurring fees for the account  
- * @return 
+ *   The id of the recurring fee being access, parsing NULL checks
+ *     if we can access any recurring fees for the account
+ * @return
  *   True if user has permission to access account
  */
 function uc_recurring_user_access($account, $rfid = NULL) {
@@ -129,7 +130,7 @@ function uc_recurring_user_access($accou
     if (user_access('view own recurring fees') && $user->uid == $account->uid) {
       return TRUE;
     }
-  } 
+  }
   else {
     // Users can only access forms for their own recurring fees through their own
     // user account.
@@ -151,6 +152,18 @@ function uc_recurring_perm() {
 }
 
 /**
+ * Implements hook_theme()
+ */
+function uc_recurring_theme() {
+  return array(
+    'uc_recurring_user_table' => array(
+      'arguments' => array($uid => NULL),
+      'file' => 'uc_recurring.pages.inc',
+    ),
+  );
+}
+
+/**
  * Implementation of hook_form_alter().
  */
 function uc_recurring_form_alter(&$form, &$form_state, $form_id) {
@@ -160,7 +173,7 @@ function uc_recurring_form_alter(&$form,
     $order->products = uc_cart_get_contents();
 
     // Make no changes if no recurring fees are found.
-    if (uc_recurring_find_fees($order) == array()) {
+    if (uc_recurring_find_applicable_fees_for_order($order) == array()) {
       return;
     }
 
@@ -205,7 +218,7 @@ function uc_recurring_form_alter(&$form,
     // Check to make sure the payment method is good and we're in CC debug mode.
     if ($methods[$order->payment_method] === $order->payment_method && variable_get('uc_credit_debug', FALSE)) {
       // Look for recurring fees on this order.
-      $fees = uc_recurring_find_fees($order);
+      $fees = uc_recurring_find_applicable_fees_for_order($order);
 
       // If we have fees, check to see if they've already been added to the order.
       if (count($fees)) {
@@ -227,7 +240,7 @@ function uc_recurring_form_alter(&$form,
 }
 
 /**
- * TODO: Investigate the use/need for this function - the only way to setup a recurring payment after 
+ * TODO: Investigate the use/need for this function - the only way to setup a recurring payment after
  * inital processing is if you have the clients payment details - which for most gateways you wont.
  *
  * Submit function for the order view update form to process recurring fees.
@@ -235,12 +248,12 @@ function uc_recurring_form_alter(&$form,
 function uc_recurring_order_view_update_form_submit($form, &$form_state) {
   if ($form_state['values']['process_fees']) {
     $order = uc_order_load($form_state['values']['order_id']);
-    $fees = uc_recurring_find_fees($order);
+    $fees = uc_recurring_find_applicable_fees_for_order($order);
 
     if (count($fees)) {
       $pass = TRUE;
       foreach ($fees as $fee) {
-        if (!uc_recurring_process($order, $fee)) {
+        if (!uc_recurring_fee_create($order, $fee)) {
           uc_order_comment_save($order->order_id, 0, t('The recurring fee for product @model failed.', array('@model' => $fee->model)), 'admin', $order->order_status);
           $pass = FALSE;
         }
@@ -253,11 +266,43 @@ function uc_recurring_order_view_update_
 }
 
 /**
+ * Implementation of hook_user().
+ */
+function uc_recurring_user($op, &$edit, &$account, $category = NULL) {
+  global $user;
+  switch ($op) {
+    case 'view':
+      if ($user->uid && (($user->uid == $account->uid && user_access('view own recurring fees') && uc_recurring_user_access($account)) || user_access('administor recurring fees'))) {
+        $account->content['recurring_fees'] = array(
+          '#type' => 'user_profile_category',
+          '#weight' => -3,
+          '#title' => t('Recurring fees'),
+          'table' => array(
+            '#type' => 'user_profile_item',
+            '#value' => l('Click here to view your recurring fees', 'user/'. $user->uid . '/recurring-fees'),
+          ),
+        );
+      }
+      break;
+
+    case 'update':
+      if (!$user->status) {
+        // @TODO: What action should be taken if a user becomes blocked, should all recurring fees for that user be cancelled?
+      }
+      break;
+
+    case 'delete':
+      // @TODO: Cancel all recurring fees for a user when they're deleted
+      break;
+  }
+}
+
+/**
  * Implementation of hook_cron().
  *
  * On the renewal datetime of a recurring fee see if the payment method would like to perfom any addition actions
  *
- * TODO: limit the number of recurring orders to process on each cron job (maybe 
+ * TODO: limit the number of recurring orders to process on each cron job (maybe
  * this can be done based on time, incase a remote server is slow a responding to
  * requests)
  */
@@ -265,16 +310,18 @@ function uc_recurring_cron() {
   $successes = 0;
   $fails = 0;
 
-  $result = db_query("SELECT * FROM {uc_recurring_users} WHERE (remaining_intervals > 0 OR remaining_intervals IS NULL) AND next_charge <= %d", time());
-  while ($fee = db_fetch_array($result)) {
-    $fee['data'] = unserialize($fee['data']);
+  $result = db_query("SELECT rfid FROM {uc_recurring_users} WHERE (remaining_intervals > 0 OR remaining_intervals IS NULL) AND next_charge <= %d", time());
+
+  while ($row = db_fetch_array($result)) {
+    $rfid = $row['rfid'];
+    $fee = uc_recurring_fee_load('user', $rfid);
+
+    // Authorize.net ARB subscriptions are handled by reverse Silent-Post, so skip these.
+    if ($fee->fee_handler == 'authorizenet_arb') { continue; }
 
-    $order_id = uc_recurring_renew($fee);
-    if ($order_id) {
+    if (uc_recurring_renew($fee)) {
       $successes++;
-      // update the status which will invoke any hooks that respond to completed status
-      uc_order_update_status($new_id, 'completed');
-    } 
+    }
     else {
       // payment attempted but failed
       $fails--;
@@ -286,133 +333,191 @@ function uc_recurring_cron() {
   }
 }
 
+
+/**
+ * Invokes uc_recurring_fee hook in a fee's payment handler
+ */
+function uc_recurring_invoke_payment_handler($op, &$fee, $order = NULL) {
+  $func = uc_recurring_get_payment_handler($fee, $order);
+
+  if (function_exists($func)) {
+    return $func($op, $fee, $order);
+  }
+  else {
+    return FALSE;
+  }
+}
+
+function uc_recurring_get_payment_handler($fee, $order = NULL) {
+  static $payment_method;
+
+  if (!empty($order) && $order->payment_method) {
+    $payment_method = $order->payment_method;
+  }
+
+  if (empty($fee->fee_handler)) {
+    $fee->fee_handler = $payment_method;
+  }
+
+  // Handler can me modified by hook_recurring_api
+  uc_recurring_invoke_fee_api('handler', $fee, $order);
+
+  $func = 'uc_recurring_'. $fee->fee_handler .'_fee';
+
+  if (!$func || !function_exists($func)) {
+    return FALSE;
+  }
+  else {
+    return $func;
+  }
+}
+
+
+function uc_recurring_invoke_fee_api($op, &$fee, &$data = NULL) {
+  $return = array();
+  foreach (module_implements('recurring_fee_api') as $name) {
+    $function = $name .'_recurring_fee_api';
+    $result = $function($op, $fee, $a3, $a4);
+    if (isset($result) && is_array($result)) {
+      $return = array_merge($return, $result);
+    }
+    else if (isset($result)) {
+      $return[] = $result;
+    }
+  }
+  return $return;
+}
+
+
 /**
  * Attempts to find a recurring fee with some specific data/id stored by the fee_handler
  */
 function uc_recurring_find_fee($fee_handler, $data) {
-  $fee = db_fetch_array(db_query("SELECT * FROM {uc_recurring_users} WHERE fee_handler = '%s' AND data LIKE '%%s%'", $fee_handler, $data));
-  if (!empty($fee)) {
-    $fee['data'] = unserialize($fee['data']);
-    return $fee;
+  // @TODO - this certainly could be done more elegantly and db-optimizedly
+  $rfid = db_result(db_query("SELECT rfid FROM {uc_recurring_users} WHERE fee_handler = '%s' AND data LIKE '%%s%'", $fee_handler, $data));
+  if ($rfid) {
+    return uc_recurring_fee_load('user', $rfid);
   }
+
   return FALSE;
 }
 
 /**
- * Process a renewal, either from the cron job or manually from a fee_handler 
+ * Process a renewal, either from the cron job or manually from a fee_handler
  *
  * @param $fee
- *   array of the fee object to be renewed  
+ *   fee object to be renewed
  * @param $check_handler
- *   set this FALSE if the recurring payment does not need to check the fee_handler, 
- *   it means the payment method/gateway needs to handle the actually payment recording 
+ *   set this FALSE if the recurring payment does not need to check the fee_handler,
+ *   it means the payment method/gateway needs to handle the actually payment recording
  *   itself, this is useful for gateways like Paypal's IPN.
- * @return 
- *   (int)order_id of the new order or FALSE if unable to renew fee
+ * @return
+ *   (Object) newly created fee or FALSE if unable to renew fee
  */
 function uc_recurring_renew($fee, $check_handler = TRUE) {
-  // attempt to a call uc_recurring_[fee_handler]_renew() function if it exits
-  $renew_func = 'uc_recurring_'. $fee['fee_handler'] .'_renew'; 
-  if ($check_handler == FALSE || function_exists($renew_func)) {
+
+  if ($check_handler == FALSE || uc_recurring_get_payment_handler($fee)) {
     // Load the order.
-    $order = uc_order_load($fee['order_id']);
+    $order = uc_order_load($fee->order_id);
 
-    $fee_amount = $fee['fee_amount'];
+    $fee_amount = $fee->fee_amount;
 
     // create a new order by cloning the currenting order and replacing order id's
     $new_order = uc_order_new($order->uid, 'in_checkout');
-    $old_id = $fee['order_id'];
-    $new_id = $new_order->order_id;
+    $old_order_id = $fee->order_id;
+    $new_order_id = $new_order->order_id;
     $new_order = $order;
-    $new_order->order_id = $new_id;
-    $fee['order_id'] = $new_id;
-    $fee['attempts']++;
+    $new_order->order_id = $new_order_id;
+    $fee->attempts++;
 
-    unset($new_order->products);
     // add recurring fee item to the order
     $product = new stdClass();
-    $product->order_id = $new_id;
-    $product->nid = $fee['data']['nid'];
-    $product->model = $fee['data']['model'];
-    $product->title = $fee['fee_title'] == '' ? 'renewal' : $fee['fee_title'];
+    $product->order_id = $new_order_id;
+    $product->nid = $fee->data['nid'];
+    $product->model = $fee->data['model'];
+    $product->title = $fee->fee_title == '' ? t('renewal') : t('!fee_title renewal', array('!fee_title' => $fee->fee_title));
     $product->qty = 1;
     $product->price = $fee_amount;
-    $product->data = $fee['data']['product']->data;
+    $product->data = $fee->data['product']->data;
 
-    $new_order->products[] = $product;
+    // @TODO: It would be better to not create one-off orders for each fee renewal, and try to batch things together.
+    $new_order->products = array($product);
 
     // lets give other modules a chance to modify the new order
-    module_invoke_all('recurring_renew', $new_order, $fee);
+    uc_recurring_invoke_fee_api('renew', $fee, $new_order);
 
-    // calculate the new fee amount to charge on this order 
-    $fee['fee_amount'] = uc_order_get_total($new_order);
-    uc_order_update_status($new_id, 'processing');
+    // calculate the new fee amount to charge on this order
+    $fee->fee_amount = uc_order_get_total($new_order);
+    uc_order_update_status($new_order_id, 'processing');
 
-    if ($check_handler == FALSE || ($check_handler && $renew_func($new_order, $fee))) {
-      uc_order_update_status($new_id, 'payment_received');
+    if ($check_handler == FALSE || ($check_handler && uc_recurring_invoke_payment_handler('renew', $fee, $new_order))) {
+      uc_order_update_status($new_order_id, 'payment_received');
       // payment was successful save the new order
       uc_order_save($new_order);
-  
-      uc_order_comment_save($new_id, 0, t('!amount recurring fee collected. (ID: <a href="!url">!fee</a>)', array('!url' => url('admin/store/orders/recurring/view/fee/'. $fee['rfid']), '!fee' => $fee['rfid'], '!amount' => uc_currency_format($fee['fee_amount']))));
- 
+
+      uc_order_comment_save($new_order_id, 0, t('!amount recurring fee collected. (ID: <a href="!url">!fee</a>)', array('!url' => url('admin/store/orders/recurring/view/fee/'. $fee->rfid), '!fee' => $fee->rfid, '!amount' => uc_currency_format($fee->fee_amount))));
+
       // also add a comment in the old order history to refer to the recurring fee and order
-      uc_order_comment_save($old_id, 0, t('New recurring fee processed, new order is <a href="!url">!order_id</a>.', array('!url' => url('admin/store/orders/'.$new_id), '!order_id' => $new_id)));
+      uc_order_comment_save($old_order_id, 0, t('New recurring fee processed, new order is <a href="!url">!order_id</a>.', array('!url' => url('admin/store/orders/'.$new_order_id), '!order_id' => $new_order_id)));
 
-      if ($fee['remaining_intervals'] > 0) $fee['remaining_intervals'] -= 1;
+      if ($fee->remaining_intervals > 0) $fee->remaining_intervals -= 1;
 
       // Update the fee in the database.
-      if ($fee['remaining_intervals'] !== NULL && $fee['remaining_intervals'] == 0) {
+      if ($fee->remaining_intervals !== NULL && $fee->remaining_intervals == 0) {
         $next_charge = time();
-  
+
         // TODO: should we provide any hooks/CA here?
         // e.g. allow a module to remind people their subscription has ended
       }
       else {
-        $next_charge = strtotime('+'. $fee['regular_interval']);
-        $user_fee = array(
+        $next_charge = strtotime('+'. $fee->regular_interval);
+        $values = array(
           'rfid' => 0,
           'uid' => $new_order->uid,
-          'fee_handler' => $fee['fee_handler'],
-          'fee_title' => $fee['fee_title'],
+          'fee_handler' => $fee->fee_handler,
+          'fee_title' => $fee->fee_title,
           'next_charge' => $next_charge,
           'fee_amount' => $fee_amount,
-          'regular_interval' => $fee['regular_interval'],
-          'remaining_intervals' => $fee['remaining_intervals'],
-          'charged_intervals' => $fee['charged_intervals']+1,
-          'order_id' => $new_id,
-          'data' => serialize($fee['data']),
-          'pfid' => $fee['pfid'],
+          'regular_interval' => $fee->regular_interval,
+          'remaining_intervals' => $fee->remaining_intervals,
+          'charged_intervals' => $fee->charged_intervals+1,
+          'order_id' => $new_order_id,
+          'data' => $fee->data,
+          'pfid' => $fee->pfid,
           'attempts' => 0,
         );
-        $user_fee['rfid'] = uc_recurring_fee_save('user', $user_fee);
+        $new_fee = uc_recurring_fee_save('user', $values);
 
-        uc_order_comment_save($new_id, 0, t('New recurring fee created for next renewal. (<a href="!url">click here to see when due</a>)', array('!url' => url('admin/store/orders/recurring/view/fee/'. $user_fee['rfid']), '!fee' => $user_fee['rfid'])));
+        uc_order_comment_save($new_order_id, 0, t('New recurring fee created for next renewal. (<a href="!url">click here to see when due</a>)', array('!url' => url('admin/store/orders/recurring/view/fee/'. $new_fee->rfid), '!fee' => $new_fee->rfid)));
       }
-  
+
       // stop the old recurring payment
-      db_query("UPDATE {uc_recurring_users} 
-                SET next_charge = %d, 
-                    remaining_intervals = 0, 
+      db_query("UPDATE {uc_recurring_users}
+                SET next_charge = %d,
+                    remaining_intervals = 0,
                     charged_intervals = charged_intervals + 1,
-                    attempts = %d 
-                WHERE rfid = %d", time(), $fee['attempts'], $fee['rfid']);
-  
+                    attempts = %d
+                WHERE rfid = %d", time(), $fee->attempts, $fee->rfid);
+
+      // update the status which will invoke any hooks that respond to completed status
+      uc_order_update_status($new_order_id, 'completed');
+
       // TODO: should this just be part of conditional actions?
       // Modules can hook into the charge process using hook_recurring_api().
-      module_invoke_all('recurring_api', 'charge', $fee);
-  
-      return $new_id;
+      uc_recurring_invoke_fee_api('charge', $new_fee);
+
+      return $new_fee;
     }
     else {
       uc_order_comment_save($fee['order_id'], 0, t('Error: Recurring fee <a href="!url">!fee</a> for product @model failed.', array('!url' => url('admin/store/orders/recurring/view/fee/'. $fee['rfid']), '!fee' => $fee['rfid'], '@model' => $fee['data']['model'])));
       watchdog('uc_recurring', 'Failed to capture recurring fee of !amount for product @model on order !order_id.', array('!amount' => $fee['fee_amount'], '@model' => $fee['data']['model'], '!order_id' => $fee['order_id']), WATCHDOG_ERROR, l(t('order !order_id', array('!order_id' => $fee['order_id'])), 'admin/store/orders/'. $fee['order_id']));
-  
+
       // TODO: should this just be part of conditional actions?
       // Modules can hook into the charge process using hook_recurring_api().
-      module_invoke_all('recurring_api', 'fail', $fee);
-  
+      uc_recurring_invoke_fee_api('fail', $fee);
+
       // TODO: what can we do to better handle failures?
-      // ie: 
+      // ie:
       //    notify user/admin
       //    extend current order for set time to give user a chance to fix problem
 
@@ -431,44 +536,18 @@ function uc_recurring_renew($fee, $check
       }
 
       $ext = $fee['next_charge'] + $extend_seconds;
-      db_query("UPDATE {uc_recurring_users} 
-                SET attempts = %d, 
-                  next_charge = %d 
+      db_query("UPDATE {uc_recurring_users}
+                SET attempts = %d,
+                  next_charge = %d
                 WHERE rfid = %d", $fee['attempts'], $ext, $fee['rfid']);
     }
   }
   return FALSE;
 }
 
-/**
- * Implementation of hook_user().
- */
-function uc_recurring_user($op, &$edit, &$account, $category = NULL) {
-  global $user;
-  switch ($op) {
-    case 'view':
-      if ($user->uid && (($user->uid == $account->uid && user_access('view own recurring fees') && uc_recurring_user_access($account)) || user_access('administor recurring fees'))) {
-        $account->content['recurring_fees'] = array(
-          '#type' => 'user_profile_category',
-          '#weight' => -3,
-          '#title' => t('Recurring fees'),
-          'table' => array(
-            '#type' => 'user_profile_item',
-            '#value' => l('Click here to view your recurring fees', 'user/'. $user->uid . '/recurring-fees'),
-          ),
-        );
-      }
 
-      break;
-  }
-}
 
-/**
- * Display users recurrings fees
- */
-function uc_recurring_user_fees($user) {
-  return theme('uc_recurring_user_table', $user->uid);
-}
+
 
 /*******************************************************************************
  * Ubercart Hooks
@@ -477,22 +556,26 @@ function uc_recurring_user_fees($user) {
 /**
  * Implementation of hook_order().
  */
-function uc_recurring_order($op, &$arg1, $arg2) {
+function uc_recurring_order($op, &$order) {
   switch ($op) {
-    case 'submit':
+    case 'approved':
       if (variable_get('uc_recurring_checkout_process', TRUE)) {
-        $fees = uc_recurring_find_fees($arg1);
+        $fees = uc_recurring_find_applicable_fees_for_order($order);
         if (count($fees)) {
           $pass = TRUE;
+
           foreach ($fees as $fee) {
-            if (!uc_recurring_process($arg1, $fee)) {
-              uc_order_comment_save($arg1->order_id, 0, t('The recurring fee for product @model failed.', array('@model' => $fee->model)), 'admin', $arg1->order_status);
+
+            $result = uc_recurring_fee_create($order, $fee);
+
+            if (!$result) {
+              uc_order_comment_save($order->order_id, 0, t('The recurring fee for product @model failed.', array('@model' => $fee->model)), 'admin', $order->order_status);
               $pass = FALSE;
             }
           }
           if ($pass == FALSE) {
             $process = variable_get('uc_recurring_checkout_fail', 'fail');
-            if ($process == 'fail' && uc_payment_balance($arg1) < $arg1->order_total) {
+            if ($process == 'fail' && uc_payment_balance($order) < $order->order_total) {
               $process = 'proceed';
             }
             switch ($process) {
@@ -507,8 +590,8 @@ function uc_recurring_order($op, &$arg1,
       break;
 
     case 'update':
-      if (uc_order_status_data($arg1->order_status, 'state') == 'in_checkout') {
-        db_query("UPDATE {uc_recurring_users} SET uid = %d WHERE uid = 0 AND order_id = %d", $arg1->uid, $arg1->order_id);
+      if (uc_order_status_data($order->order_status, 'state') == 'in_checkout') {
+        db_query("UPDATE {uc_recurring_users} SET uid = %d WHERE uid = 0 AND order_id = %d", $order->uid, $order->order_id);
       }
   }
 }
@@ -528,6 +611,7 @@ function uc_recurring_product_feature() 
   return $features;
 }
 
+
 /**
  * Implementation of hook_recurring_fee_ops().
  */
@@ -536,58 +620,29 @@ function uc_recurring_recurring_fee_ops(
 
   switch ($context) {
     case 'fee_admin':
-      if ($fee['remaining_intervals'] > 0 || $fee['remaining_intervals'] === NULL) {
-        $ops[] = l(t('charge'), 'admin/store/orders/recurring/'. $fee['rfid'] .'/charge');
+      if (!empty($fee->remaining_intervals)) {
+        $ops[] = l(t('charge'), 'admin/store/orders/recurring/'. $fee->rfid .'/charge');
       }
-      $ops[] = l(t('edit'), 'admin/store/orders/recurring/'. $fee['rfid'] .'/edit');
-      $ops[] = l(t('delete'), 'admin/store/orders/recurring/'. $fee['rfid'] .'/delete');
+      $ops[] = l(t('edit'), 'admin/store/orders/recurring/'. $fee->rfid .'/edit');
+      $ops[] = l(t('delete'), 'admin/store/orders/recurring/'. $fee->rfid .'/delete');
       break;
 
     case 'user':
-      $ops[] = l(t('cancel'), 'user/'. $fee['uid'] .'/recurring/'. $fee['rfid'] .'/cancel');
+      $ops[] = l(t('cancel'), 'user/'. $fee->uid .'/recurring/'. $fee->rfid .'/cancel');
   }
 
   return $ops;
 }
 
 
-/******************************************************************************
- * Workflow-ng Hooks                                                          *
- ******************************************************************************/
-
-// Tell Workflow about the various order events.
-function uc_recurring_event_info() {
-  $events['fee_expires'] = array(
-    '#label' => t('Recurring payment expires'),
-    '#module' => t('Recurring Payments'),
-    '#arguments' => array(
-      'order' => array('#entity' => 'order', '#label' => t('Order')),
-    ),
-  );
-  $events['fee_charge_successful'] = array(
-    '#label' => t('Payment is charged successfully'),
-    '#module' => t('Recurring Payments'),
-    '#arguments' => array(
-      'order' => array('#entity' => 'order', '#label' => t('Order')),
-    ),
-  );
-  $events['fee_charge_fails'] = array(
-    '#label' => t('Payment charge fails'),
-    '#module' => t('Recurring Payments'),
-    '#arguments' => array(
-      'order' => array('#entity' => 'order', '#label' => t('Order')),
-    ),
-  );
-
-  return $events;
-}
-
 
 /*******************************************************************************
  * Callback Functions
  ******************************************************************************/
 
-// Builds the form to display for adding or editing a recurring fee.
+/**
+ * Builds the form to display for adding or editing a recurring fee.
+ */
 function uc_recurring_feature_form($form_state, $node, $feature) {
   drupal_add_css(drupal_get_path('module', 'uc_recurring') .'/uc_recurring.css');
 
@@ -602,7 +657,7 @@ function uc_recurring_feature_form($form
     '#title' => t('Applicable SKU'),
     '#description' => t('Select the applicable product model/SKU for this fee.'),
     '#options' => $options,
-    '#default_value' => $fee['model'],
+    '#default_value' => $fee->model,
   );
 
   $form['fee'] = array(
@@ -613,7 +668,7 @@ function uc_recurring_feature_form($form
   );
 
   $attributes = array('onclick' => 'if ( $("#edit-fee-same-product").attr("checked") ) { $("#edit-fee-amount").attr("disabled","disabled"); $("#edit-fee-amount").val($("#edit-product-price").val()); } else { $("#edit-fee-amount").removeAttr("disabled"); }');
-  if ($fee['fee_amount'] == -1) {
+  if ($fee->fee_amount == -1) {
     $attributes['checked'] = 'checked';
   }
   $form['fee']['fee_same_product'] = array(
@@ -630,11 +685,11 @@ function uc_recurring_feature_form($form
     '#type' => 'textfield',
     '#title' => t('Recurring fee amount'),
     '#description' => t('Charge this amount each billing period.<br />The product price is still charged at checkout.'),
-    '#default_value' => $fee['fee_amount'] == -1 ? $node->sell_price : $fee['fee_amount'],
+    '#default_value' => $fee->fee_amount == -1 ? $node->sell_price : $fee->fee_amount,
     '#size' => 16,
     '#field_prefix' => variable_get('uc_sign_after_amount', FALSE) ? '' : variable_get('uc_currency_sign', '$'),
     '#field_suffix' => variable_get('uc_sign_after_amount', FALSE) ? variable_get('uc_currency_sign', '$') : '',
-    '#attributes' => ($fee['fee_amount'] == -1 ? array('disabled' => 'disabled') : array()),
+    '#attributes' => ($fee->fee_amount == -1 ? array('disabled' => 'disabled') : array()),
   );
 
   $form['interval'] = array(
@@ -653,7 +708,7 @@ function uc_recurring_feature_form($form
   $form['interval']['initial']['initial_charge_value'] = array(
     '#type' => 'select',
     '#options' => drupal_map_assoc(uc_range(0, 52)),
-    '#default_value' => $fee['initial_charge_value'],
+    '#default_value' => $fee->initial_charge_value,
   );
   $form['interval']['initial']['initial_charge_unit'] = array(
     '#type' => 'select',
@@ -663,7 +718,7 @@ function uc_recurring_feature_form($form
       'months' => t('month(s)'),
       'years' => t('year(s)'),
     ),
-    '#default_value' => $fee['initial_charge_unit'],
+    '#default_value' => $fee->initial_charge_unit,
   );
 
   $form['interval']['regular'] = array(
@@ -676,7 +731,7 @@ function uc_recurring_feature_form($form
   $form['interval']['regular']['regular_interval_value'] = array(
     '#type' => 'select',
     '#options' => drupal_map_assoc(uc_range(1, 52)),
-    '#default_value' => $fee['regular_interval_value'],
+    '#default_value' => $fee->regular_interval_value,
   );
   $form['interval']['regular']['regular_interval_unit'] = array(
     '#type' => 'select',
@@ -686,7 +741,7 @@ function uc_recurring_feature_form($form
       'months' => t('month(s)'),
       'years' => t('year(s)'),
     ),
-    '#default_value' => $fee['regular_interval_unit'],
+    '#default_value' => $fee->regular_interval_unit,
   );
 
   $form['num_interval'] = array(
@@ -697,7 +752,9 @@ function uc_recurring_feature_form($form
   );
 
   $attributes = array('onclick' => 'if ( $("#edit-unlimited-intervals").attr("checked") ) { $("#edit-number-intervals").attr("disabled","disabled"); $("#edit-number-intervals").val(""); } else { $("#edit-number-intervals").removeAttr("disabled"); }');
-  if ($fee['number_intervals'] == NULL) {
+
+  // @TODO: For infinite intervals, it would be better design to use -1 as a designation for this
+  if ($fee->number_intervals == NULL) {
     $attributes['checked'] = 'checked';
   }
   $form['num_interval']['unlimited_intervals'] = array(
@@ -710,8 +767,8 @@ function uc_recurring_feature_form($form
     '#title' => t('Number of billing periods'),
     '#description' => t('Specify how many times the recurring fee will be charged.'),
     '#size' => 16,
-    '#default_value' => $fee['number_intervals'] == NULL ? '' : $fee['number_intervals'],
-    '#attributes' => ($fee['number_intervals'] == NULL ? array('disabled' => 'disabled') : array()),
+    '#default_value' => $fee->number_intervals == NULL ? '' : $fee->number_intervals,
+    '#attributes' => ($fee->number_intervals == NULL ? array('disabled' => 'disabled') : array()),
   );
 
   return uc_product_feature_form($form);
@@ -743,7 +800,7 @@ function uc_recurring_feature_form_submi
   }
 
   // Build the recurring fee's data array.
-  $fee = array(
+  $values = array(
     'pfid' => $pfid,
     'model' => $form_state['values']['model'],
     'fee_amount' => $form_state['values']['fee_amount'],
@@ -758,11 +815,11 @@ function uc_recurring_feature_form_submi
   );
 
   $args = array(
-    '@product' => empty($fee['model']) ? t('this product') : t('product @model', array('@model' => $fee['model'])),
-    '!amount' => $form_state['values']['fee_amount'] == -1 ? 'the same amount as the product selling price' : uc_price($fee['fee_amount'], $context),
-    '!initial' => $fee['initial_charge'],
-    '!regular' => $fee['regular_interval'],
-    '!intervals' => t('!num times', array('!num' => ($fee['number_intervals'] == 'NULL' ? 'Unlimited' : ($fee['number_intervals'] - 1)))),
+    '@product' => empty($values['model']) ? t('this product') : t('product @model', array('@model' => $values['model'])),
+    '!amount' => $form_state['values']['fee_amount'] == -1 ? 'the same amount as the product selling price' : uc_price($values->fee_amount, $context),
+    '!initial' => $values['initial_charge'],
+    '!regular' => $values['regular_interval'],
+    '!intervals' => t('!num times', array('!num' => ($values['number_intervals'] == 'NULL' ? 'Unlimited' : ($values['number_intervals'] - 1)))),
   );
 
   // Build the feature's data array.
@@ -777,13 +834,16 @@ function uc_recurring_feature_form_submi
   $form_state['redirect'] = uc_product_feature_save($data);
 
   if (empty($pfid)) {
-    $fee['pfid'] = db_last_insert_id('uc_product_features', 'pfid');
+    $values['pfid'] = db_last_insert_id('uc_product_features', 'pfid');
   }
 
-  uc_recurring_fee_save('product', $fee);
+  uc_recurring_fee_save('product', $values);
 }
 
-// Adds the settings for the recurring module on the feature settings form.
+
+/**
+ * Adds the settings for the recurring module on the feature settings form.
+ */
 function uc_recurring_settings_form() {
   foreach (_payment_method_list() as $method) {
     if (function_exists('uc_recurring_'. $method['id'] .'_fee')) {
@@ -844,7 +904,7 @@ function uc_recurring_settings_form() {
   while($ext = db_fetch_array($result)) {
     $rows[] = array(
       'attempt' => $ext['rebill_attempt'],
-      'time_to_extent' => $ext['time_to_extend'] == 0 ? t('expire') : t('@num_days days', array('@num_days' => $ext['time_to_extend'] / (24*60*60))), 
+      'time_to_extent' => $ext['time_to_extend'] == 0 ? t('expire') : t('@num_days days', array('@num_days' => $ext['time_to_extend'] / (24*60*60))),
     );
     if ($ext['time_to_extend'] == 0) break;
   }
@@ -858,67 +918,71 @@ function uc_recurring_settings_form() {
   );
   */
   $form['extensions']['default'] = array(
-    '#value' => theme('table', array('Attempt #', 'Time to extend'), $rows, array('style' => "width: auto;")) 
+    '#value' => theme('table', array('Attempt #', 'Time to extend'), $rows, array('style' => "width: auto;"))
   );
 
   return $form;
 }
 
+
 /**
- * Implements hook_theme() 
+ * Creates a recurring fee for an order, using the specified fee handler for processing.
+ *
+ * @param $order
+ *   The order object the fees are attached to.
+ * @param $fee
+ *   The fee object to be processed.
+ * @return
+ *   TRUE or FALSE indicating whether or not the processing was successful.
  */
-function uc_recurring_theme() {
-  return array(
-    'uc_recurring_user_table' => array(
-      'arguments' => array($uid => NULL),
-    ),
-  );
-}
-
+function uc_recurring_fee_create($order, $fee, $check_handler = TRUE) {
+  if ($check_handler && !uc_recurring_get_payment_handler($fee, $order)) {
+    drupal_set_message(t('A handler for processing recurring fees cannot be found.'), 'error');
+    return FALSE;
+  }
 
-// Displays a table for users to administer their recurring fees.
-function theme_uc_recurring_user_table($uid) {
-  $rows = array();
-  $output = '';
+  if ($check_handler === FALSE || $result = uc_recurring_invoke_payment_handler('create', $fee, $order)) {
+    if (is_array($result) && count($result)) {
+      $fee->data = array_merge($result, (array)$fee->data);
+    }
+    $fee->data['nid'] = $fee->nid;
+    $fee->data['model'] = $fee->product->model;
+    $fee->data['product'] = $fee->product;
 
-  // Set up a header array for the table.
-  $header = array(t('Order'), t('Amount'), t('Interval'), t('Next charge'), t('Remaining'), t('Operations'));
+    // successfully processed the setup of recurring fee
+    $values = array(
+      'uid' => $order->uid,
+      'fee_handler' => $fee->fee_handler,
+      'next_charge' => strtotime('+'. $fee->initial_charge),
+      'fee_amount' => $fee->fee_amount,
+      'regular_interval' => $fee->regular_interval,
+      'remaining_intervals' => $fee->number_intervals,
+      'charged_intervals' => 0,
+      'order_id' => $order->order_id,
+      'data' => $fee->data,
+      'created' => time(),
+      'attempts' => 0,
+      'pfid' => $fee->pfid,
+      'fee_title' => $fee->fee_title,
+    );
 
-  $context = array(
-    'revision' => 'themed-original',
-    'location' => 'recurring-user-table',
-  );
+    $rfid = uc_recurring_fee_save('user', $values);
+    $new_fee = uc_recurring_fee_load('user', $rfid);
 
-  // Loop through the fees sorted by the order ID descending.
-  $result = db_query("SELECT * FROM {uc_recurring_users} WHERE uid = %d AND (remaining_intervals > 0 OR remaining_intervals IS NULL) ORDER BY order_id DESC", $uid);
-  while ($fee = db_fetch_array($result)) {
-    $ops = array();
-
-    // Get the $ops from the module implementing the handler.
-    $callback = 'uc_recurring_'. $fee['fee_handler'] .'_fee_ops';
-    if (function_exists($callback)) {
-      $ops = $callback('user', $fee);
-    }
+    // Allow payment handler to react to the creation of the fee
+    $extra = uc_recurring_invoke_payment_handler('created', $new_fee, $order);
 
-    // Add the row to the table for display.
-    $rows[] = array(
-      l($fee['order_id'], 'user/'. $uid .'/order/'. $fee['order_id']),
-      uc_price($fee['fee_amount'], $context),
-      array('data' => check_plain($fee['regular_interval']), 'nowrap' => 'nowrap'),
-      format_date($fee['next_charge'], 'small'),
-      $fee['remaining_intervals'] === NULL ? t('till cancelled') : $fee['remaining_intervals'],
-      array('data' => implode(' ', $ops), 'nowrap' => 'nowrap'),
-    );
-  }
+    $comment_extra = isset($extra['order_comment_extra'])? $extra['order_comment_extra'] : '';
+    $comment = t('Recurring fee <a href="!url">!fee</a> added to order. !extra', array('!url' => url('admin/store/orders/recurring/view/fee/'. $new_fee->rfid), '!fee' => $new_fee->rfid, '!extra' => $comment_extra));
+    uc_order_comment_save($order->order_id, 0, $comment, 'admin', $order->status);
 
-  // Only display the table if fees were found.
-  if (count($rows) > 0) {
-    $output = theme('table', $header, $rows);
+    return $new_fee;
   }
 
-  return $output;
+  return FALSE;
 }
 
+
 /**
  * Saves a recurring fee either for a product or for a user.
  *
@@ -928,44 +992,32 @@ function theme_uc_recurring_user_table($
  * @param $data
  *   An array of data for the fee depending on $type.
  * @return
- *   No return for 'product' $type; the rfid of the saved fee for 'user' $type.
+ *   pfid of saved product, or rfid of saved user
  */
-function uc_recurring_fee_save($type, $data) {
+function uc_recurring_fee_save($type, $fee_values) {
+  $update = array();
+  $fee_values = (array)$fee_values;
+
   switch ($type) {
     case 'product':
-      // First attempt to update an existing row.
-      db_query("UPDATE {uc_recurring_products} SET model = '%s', fee_amount = %f, initial_charge = '%s', regular_interval = '%s', number_intervals = %s WHERE pfid = %d",
-        $data['model'], $data['fee_amount'], $data['initial_charge'], $data['regular_interval'], $data['number_intervals'], $data['pfid']);
-
-      // Otherwise insert this feature as a new row.
-      if (db_affected_rows() == 0) {
-        db_query("INSERT INTO {uc_recurring_products} (pfid, model, fee_amount, initial_charge, regular_interval, number_intervals) VALUES (%d, '%s', %f, '%s', '%s', %s)",
-          $data['pfid'], $data['model'], $data['fee_amount'], $data['initial_charge'], $data['regular_interval'], $data['number_intervals']);
+      if ($fee_values['pfid']) {
+        $update[] = 'pfid';
       }
+      $result = drupal_write_record('uc_recurring_users', $fee_values, $update);
+      return ($result) ? $fee_values['pfid'] : FALSE;
       break;
 
     case 'user':
-      if ($data['remaining_intervals'] === NULL) {
-        $data['remaining_intervals'] = 'NULL';
-      }
-      else {
-        $data['remaining_intervals'] = intval($data['remaining_intervals']);
-      }
-      // First attempt to update an existing row.
-      db_query("UPDATE {uc_recurring_users} SET uid = %d, fee_handler = '%s', fee_title = '%s', next_charge = %d, fee_amount = %f, regular_interval = '%s', remaining_intervals = %s, charged_intervals = %d, order_id = %d, data = '%s', pfid = %d, attempts = %d WHERE rfid = %d",
-        $data['uid'], $data['fee_handler'], $data['fee_title'], $data['next_charge'], $data['fee_amount'], $data['regular_interval'], $data['remaining_intervals'], $data['charged_intervals'], $data['order_id'], $data['data'], $data['pfid'], $data['attempts'], $data['rfid']);
-
-      // Otherwise insert this feature as a new row.
-      if (db_affected_rows() == 0) {
-        db_query("INSERT INTO {uc_recurring_users} (uid, fee_handler, fee_title, next_charge, fee_amount, regular_interval, remaining_intervals, charged_intervals, order_id, data, created, pfid, attempts) VALUES (%d, '%s', '%s', %d, %f, '%s', %s, %d, %d, '%s', %d, %d, %d)",
-          $data['uid'], $data['fee_handler'], $data['fee_title'], $data['next_charge'], $data['fee_amount'], $data['regular_interval'], $data['remaining_intervals'], $data['charged_intervals'], $data['order_id'], $data['data'], time(), $data['pfid'], $data['attempts']);
-        $data['rfid'] = db_last_insert_id('uc_recurring_users', 'rfid');
+      if ($fee_values['rfid']) {
+        $updatep[] = 'rfid';
       }
-
-      return $data['rfid'];
+      $result = drupal_write_record('uc_recurring_users', $fee_values, $update);
+      return ($result) ? $fee_values['rfid'] : FALSE;
+      break;
   }
 }
 
+
 /**
  * Loads a recurring fee either from a product or for a user.
  *
@@ -979,37 +1031,56 @@ function uc_recurring_fee_save($type, $d
  *   An associative array of data for the specified fee.
  */
 function uc_recurring_fee_load($type, $id) {
-  switch ($type) {
-    case 'product':
-      $fee = db_fetch_array(db_query("SELECT * FROM {uc_recurring_products} WHERE pfid = %d", $id));
-      if (!empty($fee)) {
-        list($fee['initial_charge_value'], $fee['initial_charge_unit']) = explode(' ', $fee['initial_charge']);
-        list($fee['regular_interval_value'], $fee['regular_interval_unit']) = explode(' ', $fee['regular_interval']);
-      }
-      break;
-    case 'user':
-      $fee = db_fetch_array(db_query("SELECT * FROM {uc_recurring_users} WHERE rfid = %d", $id));
+  static $fees;
 
-      if ($fee['fee_handler'] == 'uc_recurring') {
-        $fee['data'] = unserialize($fee['data']);
+  if (!isset($fees[$type][$id])) {
+    switch ($type) {
+      case 'product':
+        $result = db_query("SELECT rp.*, pf.nid, n.title as fee_title
+                            FROM {uc_recurring_products} AS rp
+                              LEFT JOIN {uc_product_features} AS pf ON rp.pfid = pf.pfid
+                              LEFT JOIN {node} AS n on n.nid = pf.nid
+                            WHERE rp.pfid = %d", $id);
+        $fee = db_fetch_object($result);
+
+        if (!empty($fee)) {
+          list($fee->initial_charge_value, $fee->initial_charge_unit) = explode(' ', $fee->initial_charge);
+          list($fee->regular_interval_value, $fee->regular_interval_unit) = explode(' ', $fee->regular_interval);
+        }
+        break;
 
-        if ($key = uc_credit_encryption_key()) {
-          $crypt = new uc_encryption_class;
-          $fee['data']['payment_details']['cc_number'] = $crypt->decrypt($key, $fee['data']['payment_details']['cc_number']);
-          if (variable_get('uc_credit_debug', FALSE)) {
-            $fee['data']['payment_details']['cc_cvv'] = $crypt->decrypt($key, $fee['data']['payment_details']['cc_cvv']);
+      case 'user':
+        $fee = db_fetch_object(db_query("SELECT * FROM {uc_recurring_users} WHERE rfid = %d", $id));
+        $fee->data = unserialize($fee->data);
+
+        if ($fee->fee_handler == 'uc_recurring') {
+          if ($key = uc_credit_encryption_key()) {
+            $crypt = new uc_encryption_class;
+            $fee->data['payment_details']['cc_number'] = $crypt->decrypt($key, $fee->data['payment_details']['cc_number']);
+            if (variable_get('uc_credit_debug', FALSE)) {
+              $fee->data['payment_details']['cc_cvv'] = $crypt->decrypt($key, $fee->data['payment_details']['cc_cvv']);
+            }
+            $fee->data['payment_details']['cc_exp_month'] = $crypt->decrypt($key, $fee->data['payment_details']['cc_exp_month']);
+            $fee->data['payment_details']['cc_exp_year'] = $crypt->decrypt($key, $fee->data['payment_details']['cc_exp_year']);
+            uc_store_encryption_errors($crypt, 'uc_recurring');
           }
-          $fee['data']['payment_details']['cc_exp_month'] = $crypt->decrypt($key, $fee['data']['payment_details']['cc_exp_month']);
-          $fee['data']['payment_details']['cc_exp_year'] = $crypt->decrypt($key, $fee['data']['payment_details']['cc_exp_year']);
-          uc_store_encryption_errors($crypt, 'uc_recurring');
         }
-      }
-      break;
+
+        break;
+    }
+
+    $fees[$type][$id] = $fee;
   }
 
-  return $fee;
+  // Doing this here in order that subsequent operations could have fresh hook invocations run
+  if ($type == 'user') {
+    uc_recurring_invoke_fee_api('load', $fees[$type][$id]);
+  }
+
+  return $fees[$type][$id];
 }
 
+
 /**
  * Deletes a recurring fee from a product or user.
  *
@@ -1018,28 +1089,48 @@ function uc_recurring_fee_load($type, $i
  * @param $id
  *   The ID of the recurring fee to be removed from the appropriate table.
  */
-function uc_recurring_fee_delete($feature, $type = 'product') {
+function uc_recurring_fee_delete($fee, $type = 'product') {
   switch ($type) {
     case 'product':
-      db_query("DELETE FROM {uc_recurring_products} WHERE pfid = %d", $feature['pfid']);
+      db_query("DELETE FROM {uc_recurring_products} WHERE pfid = %d", $fee->pfid);
+
+      $result = db_query("SELECT rfid FROM {uc_recurring_users} WHERE pfid = %d", $fee->pfid);
+      while ($row = db_fetch_array($result)) {
+        $fee = uc_recurring_fee_load('user', $row['rfid']);
+        uc_recurring_fee_delete($fee, 'user');
+      }
       break;
+
     case 'user':
-      module_invoke_all('recurring_api', 'delete', $feature['fid']);
-      db_query("DELETE FROM {uc_recurring_users} WHERE rfid = %d", $feature['pfid']);
+      // Allow fee payment handler to react to the deletion
+      uc_recurring_invoke_payment_handler('delete', $fee);
+
+      // Allow other modules to react to the fee deletion
+      uc_recurring_invoke_fee_api('delete', $fee);
+      db_query("DELETE FROM {uc_recurring_users} WHERE rfid = %d", $fee->rfid);
       break;
   }
 }
 
+
 /**
  * Cancels a user's recurring fee by setting remaining intervals to 0.
  *
  * @param $rfid
  *   The recurring fee's ID.
  */
-function uc_recurring_fee_cancel($rfid) {
-  db_query("UPDATE {uc_recurring_users} SET remaining_intervals = 0 WHERE rfid = %d", $rfid);
+function uc_recurring_fee_cancel($fee) {
+  if (is_numeric($fee)) { $fee = uc_recurring_fee_load('user', $fee); }
+
+  // Allow fee payment handler to react to the deletion
+  uc_recurring_invoke_payment_handler('cancel', $fee);
+
+  // Allow other modules to react to the fee deletion
+  uc_recurring_invoke_fee_api('cancel', $fee);
+  db_query("UPDATE {uc_recurring_users} SET remaining_intervals = 0 WHERE rfid = %d", $fee->rfid);
 }
 
+
 /**
  * Returns an array of recurring fees associated with any product on an order.
  *
@@ -1048,7 +1139,7 @@ function uc_recurring_fee_cancel($rfid) 
  * @return
  *   An array of recurring fee objects containing all their data from the DB.
  */
-function uc_recurring_find_fees($order) {
+function uc_recurring_find_applicable_fees_for_order($order) {
   if (!is_array($order->products) || count($order->products) == 0) {
     return array();
   }
@@ -1057,75 +1148,50 @@ function uc_recurring_find_fees($order) 
   $nids = array();
 
   foreach ((array) $order->products as $product) {
-    $nids[] = $product->nid;
+    $nids[] = (int)$product->nid;
     $models[] = check_plain($product->model);
   }
 
   $fees = array();
-  $result = db_query("SELECT rp.*, pf.nid, n.title as fee_title
-                      FROM {uc_recurring_products} AS rp 
-                        LEFT JOIN {uc_product_features} AS pf ON rp.pfid = pf.pfid 
-                        LEFT JOIN {node} AS n on n.nid = pf.nid 
-                      WHERE rp.model IN ('". implode("', '", $models) ."') OR (rp.model = '' AND pf.nid IN ('". implode("', '", $nids) ."'))");
-  while ($fee = db_fetch_object($result)) {
+
+  $sql = db_rewrite_sql(
+          "SELECT rp.pfid
+            FROM {uc_recurring_products} AS rp
+              LEFT JOIN {uc_product_features} AS pf ON rp.pfid = pf.pfid
+              LEFT JOIN {node} AS n on n.nid = pf.nid
+            WHERE pf.fid='recurring' AND pf.nid IN (". db_placeholders($nids, 'int') .")"
+        );
+  $result = db_query($sql, $nids);
+
+  while ($row = db_fetch_array($result)) {
+    $pfid = $row['pfid'];
+    $fee = uc_recurring_fee_load('product', $pfid);
+    $fee->fee_handler = $order->payment_method;
+
     foreach($order->products as $product) {
       if ($fee->nid == $product->nid) {
         $fee->product = $product;
         if ($fee->fee_amount == -1) {
-          $fee->fee_amount = $product->price; 
+          $fee->fee_amount = $product->price;
         }
       }
     }
+
     $fees[] = $fee;
   }
   return $fees;
 }
 
+
 /**
- * Passes the information onto the specified fee handler for processing.
- *
- * @param $order
- *   The order object the fees are attached to.
- * @param $fee
- *   The fee object to be processed.
- * @return
- *   TRUE or FALSE indicating whether or not the processing was successful.
+ * Implementation of hook_authorizenet_arb()
+ * Modifying the data about to be transmitted tu authorizenet.
  */
-function uc_recurring_process($order, $fee) {
-  $handler = 'uc_recurring_'. $order->payment_method .'_fee';
-  if (!function_exists($handler)) {
-    drupal_set_message(t('A handler for processing recurring fees cannot be found.'), 'error');
-    return FALSE;
-  }
-
-  if ($handler($order, $fee) == TRUE) {
-    $fee->data['nid'] = $fee->nid;
-    $fee->data['model'] = $fee->product->model;
-    $fee->data['product'] = $fee->product;
-    // successfully processed the setup of recurring fee
-    $user_fee = array(
-      'rfid' => 0,
-      'uid' => $order->uid,
-      'fee_handler' => $fee->fee_handler,
-      'fee_title' => $fee->fee_title,
-      'next_charge' => strtotime('+'. $fee->initial_charge),
-      'fee_amount' => $fee->fee_amount,
-      'regular_interval' => $fee->regular_interval,
-      'remaining_intervals' => $fee->number_intervals,
-      'charged_intervals' => 0,
-      'order_id' => $order->order_id,
-      'data' => serialize($fee->data),
-      'pfid' => $fee->pfid,
-      'attempts' => 0,
-    );
-  
-    $user_fee['rfid'] = uc_recurring_fee_save('user', $user_fee);
-  
-    uc_order_comment_save($order->order_id, 0, t('Recurring fee <a href="!url">!fee</a> added to order.', array('!url' => url('admin/store/orders/recurring/view/fee/'. $user_fee['rfid']), '!fee' => $user_fee['rfid'])));
-  
-    return TRUE;
+function uc_recurring_authorizenet_arb($op, &$data, $fee) {
+  if ($op == 'create') {
+    // 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));
+    }
   }
-
-  return FALSE;
 }
-
Index: uc_recurring.pages.inc
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/uc_recurring/uc_recurring.pages.inc,v
retrieving revision 1.1.4.1
diff -u -p -r1.1.4.1 uc_recurring.pages.inc
--- uc_recurring.pages.inc	28 May 2009 14:55:46 -0000	1.1.4.1
+++ uc_recurring.pages.inc	29 Jun 2009 02:33:50 -0000
@@ -7,7 +7,17 @@
  *
  */
 
-// Displays the confirm form for cancelling a recurring fee.
+/**
+ * Display users recurrings fees
+ */
+function uc_recurring_user_fees($user) {
+  return theme('uc_recurring_user_table', $user->uid);
+}
+
+
+/**
+ * Displays the confirm form for cancelling a recurring fee.
+ */
 function uc_recurring_user_cancel_form($form_state, $user, $rfid) {
   $form['uid'] = array(
     '#type' => 'value',
@@ -30,3 +40,51 @@ function uc_recurring_user_cancel_form_s
   $form_state['redirect'] = 'user/'. $form_state['values']['uid'];
 }
 
+
+
+/**
+ * Displays a table for users to administer their recurring fees.
+ */
+function theme_uc_recurring_user_table($uid) {
+  $rows = array();
+  $output = '';
+
+  // Set up a header array for the table.
+  $header = array(t('Order'), t('Amount'), t('Interval'), t('Next charge'), t('Remaining'), t('Operations'));
+
+  $context = array(
+    'revision' => 'themed-original',
+    'location' => 'recurring-user-table',
+  );
+
+  // Loop through the fees sorted by the order ID descending.
+  $result = db_query("SELECT rfid FROM {uc_recurring_users} WHERE uid = %d AND (remaining_intervals > 0 OR remaining_intervals IS NULL) ORDER BY order_id DESC", $uid);
+  while ($row = db_fetch_array($result)) {
+    $rfid = $row['rfid'];
+    $fee = uc_recurring_fee_load('user', $rfid);
+    $ops = array();
+
+    // Get the $ops from the module implementing the handler.
+    $callback = 'uc_recurring_'. $fee->fee_handler .'_fee_ops';
+    if (function_exists($callback)) {
+      $ops = $callback('user', $fee);
+    }
+
+    // Add the row to the table for display.
+    $rows[] = array(
+      l($fee->order_id, 'user/'. $uid .'/order/'. $fee->order_id),
+      uc_price($fee->fee_amount, $context),
+      array('data' => check_plain($fee->regular_interval), 'nowrap' => 'nowrap'),
+      format_date($fee->next_charge, 'small'),
+      $fee->remaining_intervals === NULL ? t('till cancelled') : $fee->remaining_intervals,
+      array('data' => implode(' ', $ops), 'nowrap' => 'nowrap'),
+    );
+  }
+
+  // Only display the table if fees were found.
+  if (count($rows) > 0) {
+    $output = theme('table', $header, $rows);
+  }
+
+  return $output;
+}
