diff --git modules/uc_recurring.uc_paypal.inc modules/uc_recurring.uc_paypal.inc
deleted file mode 100644
index 969fcb1..0000000
--- modules/uc_recurring.uc_paypal.inc
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-// $Id: uc_recurring.uc_paypal.inc,v 1.1.2.2 2009/10/10 06:29:55 univate Exp $
-
-/**
- * @file
- * Uc recurring implementation for the Paypal module.
- */
-
-/**
- * Implementation of hook_recurring_info().
- */
-function uc_recurring_uc_paypal_recurring_info() {
-/*
-  $items['paypal_wpp'] = array(
-    'name' => t('PayPal website payments pro'),
-    'payment method' => 'paypal_wpp',
-    'fee handler' => 'paypal_wpp',
-    'module' => 'UC recurring',
-    'renew' => 'uc_recurring_paypal_wpp_renew',
-    'process' => 'uc_recurring_paypal_wpp_process',
-  );
-*/
-  return $items;
-}
-
-/**
- * PayPal website payments pro process.
- */
-function uc_recurring_paypal_wpp_process($order, $fee) {
-  return TRUE;
-}
-
-/**
- * PayPal website payments pro renew.
- */
-function uc_recurring_paypal_wpp_renew($order, $fee) {
-  return TRUE;
-}
-
-
diff --git modules/uc_recurring_hosted/uc_recurring_hosted api.php modules/uc_recurring_hosted/uc_recurring_hosted api.php
new file mode 100644
index 0000000..6f47f20
--- /dev/null
+++ modules/uc_recurring_hosted/uc_recurring_hosted api.php	
@@ -0,0 +1,14 @@
+<?php
+// $Id: uc_recurring_hosted.module,v 1.1.2.6 2009/11/17 13:32:09 univate Exp $
+
+function hook_uc_recurring_hosted_subscription_alter(&$object) {
+
+}
+
+function hook_uc_recurring_hosted_subscription_save($object) {
+
+}
+
+function hook_uc_recurring_hosted_subscription_delete($id, $type = 'rfid') {
+
+}
\ No newline at end of file
diff --git modules/uc_recurring_hosted/uc_recurring_hosted.module modules/uc_recurring_hosted/uc_recurring_hosted.module
index 56dd0d9..f15b554 100644
--- modules/uc_recurring_hosted/uc_recurring_hosted.module
+++ modules/uc_recurring_hosted/uc_recurring_hosted.module
@@ -7,6 +7,8 @@
  * Authorize.net ARB and Paypal WPS
  */
 
+define('UC_PAYPAL_RECURRING_API', '60.0');
+
 /******************************************************************************
  * DRUPAL HOOKS
  *****************************************************************************/
@@ -15,23 +17,42 @@
  * Implementation of hook_menu().
  */
 function uc_recurring_hosted_menu() {
-  $items['uc_recurring_hosted/paypal/ipn/%'] = array( 
-    'title' => 'PayPal IPN',
-    'page callback' => 'uc_recurring_hosted_paypal_ipn',
-    'access callback' => 'uc_paypal_ipn_access',
-    'type' => MENU_CALLBACK,
-  );
+  $items = array();
+  if (module_exists('uc_paypal')) {
+    $items['uc_recurring_hosted/paypal/ipn/%'] = array(
+      'title' => 'PayPal IPN',
+      'page callback' => 'uc_recurring_hosted_paypal_ipn',
+      'access callback' => 'uc_paypal_ipn_access',
+      'type' => MENU_CALLBACK,
+    );
 
-  // this menu item is a mock url for emulating the paypal IPN
-  $items['uc_recurring_hosted/paypal/cgi-bin/webscr'] = array(
-    'title' => 'PayPal Website',
-    'page callback' => '_uc_recurring_hosted_paypal_mock_web_page',
-    'access callback' => TRUE,
-    'type' => MENU_CALLBACK,
-  );
+    // A mock url for emulating the paypal IPN.
+    $items['uc_recurring_hosted/paypal/cgi-bin/webscr'] = array(
+      'title' => 'PayPal Website',
+      'page callback' => '_uc_recurring_hosted_paypal_mock_web_page',
+      'access callback' => TRUE,
+      'type' => MENU_CALLBACK,
+    );
+  }
   return $items;
 }
 
+/**
+ * Implementation of hook_payment_gateway_alter().
+ *
+ * Register own payment gateway instead of the PayPal pro.
+ * @see uc_paypal_payment_gateway().
+ */
+function uc_recurring_hosted_payment_gateway_alter(&$gateways) {
+ foreach ($gateways as &$gateway) {
+   if ($gateway['id'] == 'paypal_wpp') {
+     // Change the callback.
+     $gateway['credit'] = 'uc_recurring_hosted_uc_paypal_wpp_charge';
+     break;
+   }
+ }
+}
+
 /******************************************************************************
  * UBERCART HOOKS (including uc_recurring)
  *****************************************************************************/
@@ -40,9 +61,7 @@ function uc_recurring_hosted_menu() {
  * Implementation of hook_recurring_info().
  */
 function uc_recurring_hosted_recurring_info() {
-  // *************************************
   // AUTHORIZE.NET ARB
-  // ************************************
   $items['authorizenet_arb'] = array(
     'name' => t('Authorize.net (ARB)'),
     'payment method' => 'credit',
@@ -65,9 +84,7 @@ function uc_recurring_hosted_recurring_info() {
     $items['authorizenet'] = $items['authorizenet_arb'];
   }
 
-  // *************************************
-  // PAYPAL WPS
-  // ************************************
+  // Paypal website payments standard.
   if (module_exists('uc_paypal')) {
     $items['paypal_wps'] = array(
       'name' => t('Paypal website payments standard'),
@@ -78,22 +95,103 @@ function uc_recurring_hosted_recurring_info() {
       'own handler' => TRUE,
       'menu' => array(
         'cancel' => array(
-          'title' => 'Cancel', 
+          'title' => 'Cancel',
           'page arguments' => array('uc_recurring_hosted_cancel_form'),
         ),
       ),
     );
+
+    // PayPal website payments pro.
+    $items['paypal_wpp'] = array(
+      'name' => t('PayPal website payments pro'),
+      'payment method' => 'paypal_wpp',
+      'fee handler' => 'paypal_wpp',
+      'module' => 'uc_recurring',
+      'renew callback' => 'uc_recurring_paypal_wpp_renew',
+      'process callback' => 'uc_recurring_paypal_wpp_process',
+      'cancel callback' => 'uc_recurring_paypal_wpp_cancel',
+      'ops' => 'uc_recurring_paypal_ops',
+      'menu' => array(
+        'charge' => UC_RECURRING_MENU_DISABLED,
+        'edit' => UC_RECURRING_MENU_DISABLED,
+        'cancel' => UC_RECURRING_MENU_DEFAULT,
+      ),
+    );
   }
 
   return $items;
 }
 
+/**
+ * Get a hosted subscription ID and recurring fee ID.
+ *
+ * @param $id
+ *   The ID.
+ * @param $type
+ *   The type of the get request. can be "rfid" for recurring fee ID, or
+ *   "subscription_id" for the subscription ID.
+ * @return
+ *   Object with the subscription ID and recurring fee ID, or an empty array if
+ *   none found.
+ */
+function uc_recurring_hosted_subscription_load($id, $type = 'rfid') {
+  $return = array();
+
+  if ($type ==' rfid') {
+    $return = db_fetch_object(db_query("SELECT * FROM {uc_recurring_hosted} WHERE rfid = %d", $id));
+  }
+  elseif ($type == 'subscription_id') {
+    $return = db_fetch_object(db_query("SELECT * FROM {uc_recurring_hosted} WHERE subscription_id = '%s'", $id));
+  }
+
+  drupal_alter('uc_recurring_hosted_subscription', $object);
+
+  return $return;
+}
+
+/**
+ * Save a new subscription ID
+ */
+function uc_recurring_hosted_subscription_save($rfid, $subscription_id) {
+  // Delete an existing record.
+  uc_recurring_hosted_subscription_delete($rfid);
+
+  $object = new stdClass;
+  $object->rfid = $rfid;
+  $object->subscription_id = $subscription_id;
+
+  module_invoke_all('uc_recurring_hosted_subscription_save', $object);
+  drupal_write_record('uc_recurring_hosted', $object);
+}
+
+/**
+ * Delete a record by subscription ID or recurring fee ID.
+ *
+ * @param $id
+ *   The ID.
+ * @param $type
+ *   The type of the get request. can be "rfid" for recuring fee ID, or
+ *   "subscription" for the subscription ID.
+ */
+function uc_recurring_hosted_subscription_delete($id, $type = 'rfid') {
+  module_invoke_all('uc_recurring_hosted_subscription_delete', $id, $type);
+
+  if ($type ==' rfid') {
+    $return = db_query("DELETE FROM {uc_recurring_hosted} WHERE rfid = %d", $id);
+  }
+  else {
+    $return = db_query("DELETE FROM {uc_recurring_hosted} WHERE subscription_id = '%s'", $id);
+  }
+}
+
+
+
 /******************************************************************************
  * AUTHORIZE.NET ARB
  *****************************************************************************/
 
 /**
- * Set up the recurring fee using the ARB api.
+ * Set up the recurring fee using the ARB API.
  *
  * @param $order
  *   The order object.
@@ -200,7 +298,7 @@ function uc_recurring_hosted_authorizenet_arb_process($order, &$fee) {
       ),
     );
   }
- 
+
   // Build the XML string.
   $xml = _uc_authorizenet_xml_api_wrapper('ARBCreateSubscriptionRequest', _uc_authorizenet_array_to_xml($data));
 
@@ -220,13 +318,10 @@ function uc_recurring_hosted_authorizenet_arb_process($order, &$fee) {
     return FALSE;
   }
   uc_order_comment_save($order->order_id, 0, t('Authorize.Net: ARB subscription created - @id', array('@id' => $data['subscriptionId'])));
-  
-  // Save the new credit reference to the db.
-  $record = array(
-    'rfid' => $fee->rfid, 
-    'subscription_id' => $data['subscriptionId'],
-  );
-  drupal_write_record('uc_recurring_hosted', $record);
+
+  // Save the new credit reference to the database.
+  uc_recurring_hosted_subscription_save($fee->rfid, $data['subscriptionId']);
+
   $order->data = uc_credit_log_reference($order->order_id, $data['subscriptionId'], $order->payment_details['cc_number']);
 
   return TRUE;
@@ -239,7 +334,7 @@ function uc_recurring_hosted_authorizenet_arb_process($order, &$fee) {
  *   The reneal order object.
  * @param $fee
  *   The recurring fee object.
- * 
+ *
  * @return
  *   Always returns TRUE.
  */
@@ -251,10 +346,10 @@ function uc_recurring_hosted_authorizenet_arb_renew($order, $fee) {
 
 /**
  * Implementation of hook_uc_auth_arb_payment().
- * Called during an ARB silent post. 
+ * Called during an ARB silent post.
  */
 function uc_recurring_hosted_uc_auth_arb_payment($post) {
-  $fee = db_fetch_object(db_query("SELECT rfid FROM {uc_recurring_hosted} WHERE subscription_id = '%s'", $post['x_subscription_id']));
+  $fee = uc_recurring_hosted_subscription_load($post['x_subscription_id'], 'subscription_id');
 
   if (!empty($fee)) {
     $fee = uc_recurring_fee_user_load($fee->rfid);
@@ -279,7 +374,7 @@ function uc_recurring_hosted_uc_auth_arb_payment($post) {
  */
 function uc_recurring_hosted_authorizenet_arb_cancel($order, &$fee) {
   $server = variable_get('uc_authnet_arb_mode', 'disabled');
-  
+
   $order = uc_order_load($order->order_id);
   $subscription_id = end(array_keys($order->data['cc_txns']['references']));
 
@@ -349,7 +444,7 @@ function uc_recurring_hosted_authorizenet_arb_update_form_submit(&$form, &$form_
   $order = uc_order_load($fee->order_id);
   $order->payment_details = $form_state['values']['cc_data'];
   $subscription_id = end(array_keys($order->data['cc_txns']['references']));
-    
+
   if ($order->payment_details['cc_exp_month'] < 10) {
     $order->payment_details['cc_exp_month'] = '0'. $order->payment_details['cc_exp_month'];
   }
@@ -363,7 +458,7 @@ function uc_recurring_hosted_authorizenet_arb_update_form_submit(&$form, &$form_
       ),
     ),
   );
-  
+
   if (uc_authorizenet_arb_update($subscription_id, $updates, $order->order_id)) {
     drupal_set_message(t('Account updated.'));
     $form_state['redirect'] = 'user/'. $form_state['values']['uid'];
@@ -397,20 +492,20 @@ function uc_recurring_hosted_paypal_wps_process($order, &$fee) {
  * Normally in a payment gateway we would just need to define the callback 'process':
  * e.g. function uc_recurring_hosted_wps_process($order, $fee)
  *
- * But paypal_wps is implemented by altering the checkout review form to 
+ * But paypal_wps is implemented by altering the checkout review form to
  * change the form so it is submitted directly to paypal, we are using this
  * same trick to alter the paypal form, its a messy hack but works for now.
  */
 function uc_recurring_hosted_form_uc_paypal_wps_form_alter(&$form, $form_state) {
   $order = $form['#parameters'][2];
- 
+
   // if recurring fees exist in the order
   $recurring_fees = uc_recurring_get_recurring_products_in_order($order);
 
   if (count($recurring_fees) > 0) {
     // TODO: what do we do if someone tries to order more then one subscription??
     // we will just process the first for now
-    $recurring_fee = $recurring_fees[0]; 
+    $recurring_fee = $recurring_fees[0];
     if (count($recurring_fees) > 1) {
       drupal_set_message(t('Sorry recurring payments can only be setup for one product at a time when paying via paypal, only the first recurring payment will be setup when you click on Submit order.'), 'warning');
     }
@@ -418,19 +513,19 @@ function uc_recurring_hosted_form_uc_paypal_wps_form_alter(&$form, $form_state)
     // IPN control notify URL
     $data['notify_url'] = url('uc_recurring_hosted/paypal/ipn/'. $order->order_id, array('absolute' => TRUE));
 
-    $data['cmd'] = '_xclick-subscriptions';  
+    $data['cmd'] = '_xclick-subscriptions';
     $data['item_name'] = t('Order @order_id at !store', array('@order_id' => $order->order_id, '!store' => variable_get('uc_store_name', url('<front>', array('absolute' => TRUE)))));
     // first payment
     list($p, $t) = explode(' ', $recurring_fee['recurring product']->initial_charge);
     $data['a1'] = sprintf("%0.2f", $order->order_total);
-    $data['p1'] = $p;  
-    $data['t1'] = strtoupper($t[0]); 
+    $data['p1'] = $p;
+    $data['t1'] = strtoupper($t[0]);
     // recurring payments
     list($p, $t) = explode(' ', $recurring_fee['recurring product']->regular_interval);
     $data['a3'] = sprintf("%0.2f", $product_fee->fee_amount == 0 ? $recurring_fee['product']->price : $recurring_fee['recurring product']->fee_amount);
-    $data['p3'] = $p;  
+    $data['p3'] = $p;
     $data['t3'] = strtoupper($t[0]);
-    
+
     $data['src'] = 1;
     if ($recurring_fee['recurring product']->number_intervals > 0) {
       $data['srt'] = $recurring_fee['recurring product']->number_intervals;
@@ -482,7 +577,7 @@ function uc_recurring_hosted_paypal_ipn($order_id) {
   $payer_email = check_plain($_POST['payer_email']);
 
   $req = '';
- 
+
   if ($order == FALSE) {
     watchdog('uc_recurring_hosted', 'IPN attempted for non-existent order.', array(), WATCHDOG_ERROR);
     return;
@@ -535,7 +630,7 @@ function uc_recurring_hosted_paypal_ipn($order_id) {
       case 'subscr_failed':
         // Calculate when the next retry will be and then extend.
 
-        // 
+        //
         return;
       case 'subscr_eot':
       case 'subscr_cancel':
@@ -637,3 +732,419 @@ function _uc_recurring_hosted_paypal_mock_web_page() {
   echo 'VERIFIED';
   exit();
 }
+
+/**
+ * Paypal pro charge handler.
+ */
+function uc_recurring_hosted_uc_paypal_wpp_charge($order_id, $amount, $data) {
+  global $user;
+  $order = uc_order_load($order_id);
+  if (uc_recurring_get_recurring_products_in_order($order)) {
+    uc_recurring_process_order($order, array('amount' => $amount, 'data' => $data));
+
+    // We have the result in the fee object, so let's retreive it.
+    // TODO: We should assume that an order can be either recurring or either
+    // single pay.
+    if ($fee  = current(uc_recurring_get_fees($order))) {
+      if (!empty($fee->data['npv result']['subscription_id'])) {
+        // Add the subscription Id.
+        uc_recurring_hosted_subscription_save($fee->rfid, $fee->data['npv result']['subscription_id']);
+      }
+      return $fee->data['npv result'];
+    }
+    else {
+      // There's no fee object, which means the recurring payment failed,
+      // so return failure to uc_payment_process().
+      return array(
+        'success' => FALSE,
+        'message' => t('PayPal pro recurring payment failed'),
+        'uid' => $user->uid,
+      );
+    }
+
+  }
+  else {
+    // There are no recurring products, so use the default callback.
+    return uc_paypal_wpp_charge($order_id, $amount, $data);
+  }
+}
+
+
+
+/**
+ * PayPal website payments pro process.
+ */
+function uc_recurring_paypal_wpp_process($order, &$fee) {
+  global $user;
+  // 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));
+  }
+
+  $cc_type = '';
+  if (isset($order->payment_details['cc_type'])) {
+    switch (strtolower($order->payment_details['cc_type'])) {
+      case 'amex':
+      case 'american express':
+        $cc_type = 'Amex';
+        break;
+
+      case 'visa':
+        $cc_type = 'Visa';
+        break;
+
+      case 'mastercard':
+      case 'master card':
+        $cc_type = 'MasterCard';
+        break;
+
+      case 'discover':
+        $cc_type = 'Discover';
+        break;
+    }
+  }
+  if (empty($cc_type)) {
+    $cc_type = _uc_paypal_card_type($order->payment_details['cc_number']);
+    if ($cc_type === FALSE) {
+      drupal_set_message(t('The credit card type did not pass validation.'), 'error');
+      watchdog('uc_recurring', 'Could not figure out credit card type @type', array('@type' => $order->payment_details['cc_type']), WATCHDOG_ERROR);
+      return FALSE;
+    }
+  }
+  // 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.
+    'VERSION' => UC_PAYPAL_RECURRING_API,
+    'METHOD' => 'CreateRecurringPaymentsProfile',
+    'DESC' => 'Order '. $order->order_id .' at '. check_plain(variable_get('uc_store_name', 'Ubercart')),
+    'PROFILESTARTDATE' => $start_date,
+    'CREDITCARDTYPE' => $cc_type,
+    '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_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;
+  }
+
+  if (variable_get('uc_credit_cvv_enabled', TRUE)) {
+    $nvp_request['CVV2'] = $order->payment_details['cc_cvv'];
+  }
+
+  // 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'));
+  $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/'. $order_id)
+      );
+    // Fall through.
+    case 'Success':
+      $message = t('<b>@type</b><br /><b>Success: </b>@amount @currency', array('@type' => $types[$data['txn_type']], '@amount' => uc_price($nvp_request['AMT'], $context, array('sign' => FALSE)), '@currency' => $nvp_response['CURRENCYCODE']));
+      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' => TRUE,
+        'comment' => t('PayPal transaction ID: @transactionid', array('@transactionid' => $nvp_response['TRANSACTIONID'])),
+        'message' => $message,
+        'data' => check_plain($nvp_response['TRANSACTIONID']),
+        'uid' => $user->uid,
+        // We place the subscription ID, and it will be saved in
+        // uc_recurring_hosted_uc_paypal_wpp_charge(), as we still don't have
+        // the recurring fee ID.
+        'subscription_id' => $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($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());
+
+      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);
+      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($order_id, $user->uid, $message, 'admin');
+
+  // Don't log this as a payment money wasn't actually captured.
+  if (in_array($data['txn_type'], array(UC_CREDIT_AUTH_ONLY))) {
+    $result['log_payment'] = FALSE;
+  }
+
+  if(!empty($result['success'])) {
+    // 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 website payments pro renew.
+ *
+ * Renew is triggered by Paypal, the only thing we do here, is capture the
+ * payment and update the fee object upon success or failure.
+ *
+ * @TODO: This can't be done now, as uc_paypal doesn't pass the payment
+ * notification.
+ *
+ * @see uc_paypal_ipn().
+ */
+function uc_recurring_paypal_wpp_renew($order, $fee) {
+  return TRUE;
+}
+
+/**
+ * PayPal website payments pro 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_wpp_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', 'Failed to cancel recurring @id', array('@id' => $fee->rfid), WATCHDOG_ERROR);
+    return FALSE;
+  }
+  else {
+    watchdog('uc_recurring', 'Success to cancel recurring @id', array('@id' => $fee->rfid), WATCHDOG_INFO);
+  }
+
+  uc_order_comment_save($fee->order_id, $user->uid, t('PayPal recurring fee @id with subscription @subscription canceled.', array('@id' => $fee->rfid, '@subscription' => $subscription->subscription_id)), 'admin');
+
+  // Let other modules act on the canceled fee.
+  module_invoke_all('uc_recurring_cancel', $fee);
+
+  return TRUE;
+}
+
+/**
+ * Updates a PayPal subscription; for simplicity's sake, Credit Card
+ * information except for expiration date cannot be updated at this time.
+ *
+ * @param $order
+ *   The order object.
+ * @param $fee
+ *   The recurring fee object.
+ * @param $updates
+ *   An array of data to update using key/ value pairs from the NVP API for
+ *   PayPal.
+ *
+ * @return
+ *   TRUE or FALSE indicating the success of the update.
+ */
+function uc_paypal_recurring_update($order, $fee, $updates = array()) {
+  global $user;
+
+  watchdog('uc_paypal_recurring_update', print_r($fee, 1), null, WATCHDOG_DEBUG);
+
+  // Build an NVP request.
+  $nvp_request = array(
+    'METHOD' => 'UpdateRecurringPaymentsProfile',
+    'PROFILEID' => $fee->data['payapl'],
+  ) + $updates;
+
+  // 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', 'Failed to update recurring profile @id', array('@id' => $fee->data['payapl']), WATCHDOG_ERROR);
+    return FALSE;
+  }
+
+  uc_order_comment_save($order->order_id, $user->uid, t('PayPal recurring fee @id updated.', array('@id' => $fee->data['payapl'])), 'admin');
+  return TRUE;
+}
+
+/**
+ * Helper function; Get a recurring payments profile from PayPal.
+ *
+ * @param $rfid
+ *   The recurring fee ID.
+ *
+ * @return
+ *   FALSE on failure, otherwise, the NVP response from PayPal.
+ */
+function uc_recurring_paypal_get_profile($rfid) {
+  $fee = uc_recurring_fee_user_load($rfid);
+
+  // Build an NVP request.
+  $nvp_request = array(
+    'METHOD' => 'GetRecurringPaymentsProfileDetails',
+    'PROFILEID' => $fee->data['paypal'],
+  );
+
+  // 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') {
+    return FALSE;
+  }
+
+  return $nvp_response;
+}
\ No newline at end of file
diff --git uc_recurring.module uc_recurring.module
index d134948..dab081d 100644
--- uc_recurring.module
+++ uc_recurring.module
@@ -453,10 +453,12 @@ function uc_recurring_uc_message() {
  *
  * @param $order
  *   The order object the fees are attached to.
+ * @param $data
+ *   Optional; Data that should be added to the fee object.
  * @return
  *   FALSE on failure or array with new recurring fee IDs.
  */
-function uc_recurring_process_order($order) {
+function uc_recurring_process_order($order, $data = array()) {
   global $user;
   // Get all the products that should have a recurring fee created for them.
   if (($products = uc_recurring_get_recurring_products_in_order($order))) {
@@ -491,6 +493,7 @@ function uc_recurring_process_order($order) {
       $fee->fee_title = t('Renewal of product @title', array('@title' => $product['product']->title));
 
       $fee->next_charge = strtotime('+'. $product_fee->initial_charge);
+      $fee->initial_charge = $product_fee->initial_charge;
       $fee->regular_interval = $product_fee->regular_interval;
       $fee->remaining_intervals = $product_fee->number_intervals;
       $fee->charged_intervals = 0;
@@ -498,7 +501,7 @@ function uc_recurring_process_order($order) {
         'model' => $product_fee->model,
         'nid' => $product_fee->nid,
         'recurring orders' => array(),
-      );
+      ) + $data;
       $fee->attempts = 0;
       $fee->pfid = $product_fee->pfid;
       $fee->order_product_id = $order_product_id;
