diff --git payment/uc_paypal/uc_paypal.install payment/uc_paypal/uc_paypal.install
index be50342..cc518a5 100644
--- payment/uc_paypal/uc_paypal.install
+++ payment/uc_paypal/uc_paypal.install
@@ -131,3 +131,9 @@ function uc_paypal_uninstall() {
     ->execute();
 }
 
+/**
+ * Change table name from uc_paypal_ipn to uc_payment_paypal_ipn.
+ */
+function uc_paypal_update_7000() {
+  db_rename_table('uc_paypal_ipn', 'uc_payment_paypal_ipn');
+}
diff --git payment/uc_paypal/uc_paypal.pages.inc payment/uc_paypal/uc_paypal.pages.inc
index 3496be6..ed796dc 100644
--- payment/uc_paypal/uc_paypal.pages.inc
+++ payment/uc_paypal/uc_paypal.pages.inc
@@ -6,15 +6,124 @@
  * Paypal administration menu items.
  */
 
-// Process Instant Payment Notifiations from PayPal.
+/**
+ * Handle IPN message from Paypal.
+ */
 function uc_paypal_ipn($order_id = 0) {
-  watchdog('uc_paypal', 'Receiving IPN at URL for order @order_id. <pre>@debug</pre>',
-    array('@order_id' => $order_id, '@debug' => variable_get('uc_paypal_wps_debug_ipn', FALSE) ? print_r($_POST, TRUE) : ''));
+  watchdog('uc_paypal', 'Receiving IPN at URL for order @order_id. @debug',
+    array('@order_id' => $order_id, '@debug' => variable_get('uc_paypal_wps_debug_ipn', FALSE) ? '<pre>'. print_r($_POST, TRUE) .'</pre>' : ''));
 
-  if (!isset($_POST['invoice'])) {
-    watchdog('uc_paypal', 'IPN attempted with invalid order ID.', array(), WATCHDOG_ERROR);
+  // Assign posted variables to local variables
+  $ipn = (object)$_POST;
+  $ipn->received = time();
+
+  $order = _uc_paypal_ipn_get_order($ipn);
+  if ($order == FALSE) {
+    watchdog('uc_paypal', 'IPN attempted for non-existent order.', array(), WATCHDOG_ERROR);
     return;
   }
+  $ipn->order_id = $order->order_id;
+
+  // Log the IPN against the actual order if debug enabled.
+  if (variable_get('uc_paypal_wps_debug_ipn', FALSE)) {
+    uc_order_log_changes($order->order_id, array('<pre>'. print_r($ipn, TRUE) .'</pre>'));
+  }
+
+  if (_uc_paypal_validate_ipn_message($ipn)) {
+    // Allow other modules to process the IPN transaction.
+    module_invoke_all('uc_paypal_ipn_transaction', $ipn->txn_type, $ipn, $order);
+    uc_paypal_ipn_save($ipn);
+  }
+}
+
+/**
+ * Implentation of hook_uc_paypal_ipn_transaction().
+ */
+function uc_paypal_uc_paypal_ipn_transaction($txn_type, $ipn, $order) {
+  switch ($txn_type) {
+    case 'adjustment':       // A dispute has been resolved and closed
+    case 'cart':             // Payment received for multiple items; source is Express Checkout or the PayPal Shopping Cart.
+    case 'express_checkout': // Payment received for a single item; source is Express Checkout
+    case 'web_accept':       // Payment received; source is a Buy Now, Donation, or Auction Smart Logos button
+      uc_paypal_process_ipn_payment($ipn) {
+      if ($ipn->payment_status == 'Completed') {
+        uc_cart_complete_sale($order);
+      }
+      break;
+    default:
+      // Let other modules handle other IPN transactions.
+  }
+}
+
+/**
+ * Handles payment information from IPN message.
+ */
+function uc_paypal_process_ipn_payment($ipn) {
+  global $user;
+  $context = array('revision' => 'formatted-original', 'type' => 'amount');
+  $options = array('sign' => FALSE);
+
+  switch ($ipn->payment_status) {
+    case 'Canceled_Reversal':
+      uc_order_comment_save($ipn->order_id, 0, t('PayPal has cancelled the reversal and returned !amount !currency to your account.', array('!amount' => uc_price($ipn->mc_gross, $context, $options), '!currency' => $ipn->mc_currency)), 'admin');
+      break;
+
+    case 'Completed':
+      $comment = t('PayPal transaction ID: @txn_id', array('@txn_id' => $ipn->txn_id));
+      uc_payment_enter($ipn->order_id, 'paypal_wps', $ipn->mc_gross, $user->uid, NULL, $comment);
+      uc_order_comment_save($ipn->order_id, 0, t('Payment of @amount @currency submitted through PayPal.', array('@amount' => uc_price($ipn->mc_gross, $context, $options), '@currency' => $ipn->mc_currency)), 'order', 'payment_received');
+      uc_order_comment_save($ipn->order_id, 0, t('PayPal IPN reported a payment of @amount @currency.', array('@amount' => uc_price($ipn->mc_gross, $context, $options), '@currency' => $ipn->mc_currency)));
+      break;
+
+    case 'Denied':
+      uc_order_comment_save($ipn->order_id, 0, t("You have denied the customer's payment."), 'admin');
+      break;
+
+    case 'Expired':
+      uc_order_comment_save($ipn->order_id, 0, t('The authorization has failed and cannot be captured.'), 'admin');
+      break;
+
+    case 'Failed':
+      uc_order_comment_save($ipn->order_id, 0, t("The customer's attempted payment from a bank account failed."), 'admin');
+      break;
+
+    case 'Pending':
+      uc_order_update_status($ipn->order_id, 'paypal_pending');
+      uc_order_comment_save($ipn->order_id, 0, t('Payment is pending at PayPal: @reason', array('@reason' => _uc_paypal_pending_message(check_plain($ipn->pending_reason)))), 'admin');
+      break;
+
+    // You, the merchant, refunded the payment.
+    case 'Refunded':
+      $comment = t('PayPal transaction ID: @txn_id', array('@txn_id' => $ipn->txn_id));
+      uc_payment_enter($ipn->order_id, 'paypal_wps', $ipn->mc_gross, $ipn->uid, NULL, $comment);
+      break;
+
+    case 'Reversed':
+      watchdog('uc_paypal', 'PayPal has reversed a payment!', array(), WATCHDOG_ERROR);
+      uc_order_comment_save($ipn->order_id, 0, t('Payment has been reversed by PayPal: @reason', array('@reason' => _uc_paypal_reversal_message($ipn->reason_code))), 'admin');
+      break;
+
+    case 'Processed':
+      uc_order_comment_save($ipn->order_id, 0, t('A payment has been accepted.'), 'admin');
+      break;
+
+    case 'Voided':
+      uc_order_comment_save($ipn->order_id, 0, t('The authorization has been voided.'), 'admin');
+      break;
+  }
+}
+
+// Helper functions for paypal IPN.
+
+/**
+ * @return
+ *   Ubercart Order object.
+ */
+function _uc_paypal_ipn_get_order($ipn);
+  if (!isset($ipn->invoice)) {
+    watchdog('uc_paypal', 'IPN attempted with invalid order ID.', array(), WATCHDOG_ERROR);
+    return FALSE;
+  }
 
   if (($len = strpos($_POST['invoice'], '-')) > 0) {
     $order_id = intval(substr($_POST['invoice'], 0, $len));
@@ -23,25 +132,19 @@ function uc_paypal_ipn($order_id = 0) {
     $order_id = intval($_POST['invoice']);
   }
 
-  $order = uc_order_load($order_id);
+  return uc_order_load($order_id);
+}
 
-  if ($order == FALSE) {
-    watchdog('uc_paypal', 'IPN attempted for non-existent order.', array(), WATCHDOG_ERROR);
-    return;
+/**
+ * Validate Paypal IPN.
+ */
+function _uc_paypal_validate_ipn_message($ipn) {
+  if (_uc_paypal_duplicate_ipn($ipn)) {
+    return FALSE;
   }
 
-  // Assign posted variables to local variables
-  $payment_status = check_plain($_POST['payment_status']);
-  $payment_amount = check_plain($_POST['mc_gross']);
-  $payment_currency = check_plain($_POST['mc_currency']);
-  $payment_fee = check_plain($_POST['mc_fee']);
-  $receiver_email = check_plain($_POST['receiver_email']);
-  $txn_id = check_plain($_POST['txn_id']);
-  $txn_type = check_plain($_POST['txn_type']);
-  $payer_email = check_plain($_POST['payer_email']);
-
+  // @todo: any reason we can't use http_build_query() here?
   $req = '';
-
   foreach ($_POST as $key => $value) {
     $value = urlencode(stripslashes($value));
     $req .= $key . '=' . $value . '&';
@@ -64,95 +167,41 @@ function uc_paypal_ipn($order_id = 0) {
   // TODO: Change this to property_exists when we have a PHP requirement >= 5.1.
   if (array_key_exists('error', $response)) {
     watchdog('uc_paypal', 'IPN failed with HTTP error @error, code @code.', array('@error' => $response->error, '@code' => $response->code), WATCHDOG_ERROR);
-    return;
+    return FALSE;
   }
 
   if (strcmp($response->data, 'VERIFIED') == 0) {
     watchdog('uc_paypal', 'IPN transaction verified.');
+    return TRUE;
+  }
+  elseif (strcmp($response->data, 'INVALID') == 0) {
+    watchdog('uc_paypal', 'IPN transaction failed verification.', array(), WATCHDOG_ERROR);
+    uc_order_comment_save($ipn->order_id, 0, t('An IPN transaction failed verification for this order.'), 'admin');
+  }
+  return FALSE;
+}
 
-    $duplicate = db_query("SELECT COUNT(*) FROM {uc_payment_paypal_ipn} WHERE txn_id = :id AND status <> :status", array(':id' => $txn_id, ':status' => 'Pending'))->fetchField();
+/**
+ * Check if we have already recieved a IPN with the same details.
+ */
+function _uc_paypal_duplicate_ipn($ipn) {
+  $duplicate = db_query("SELECT COUNT(*) FROM {uc_payment_paypal_ipn} WHERE txn_id = :id AND status <> :status", array(':id' => $txn_id, ':status' => 'Pending'))->fetchField();
     if ($duplicate > 0) {
       if ($order->payment_method != 'credit') {
         watchdog('uc_paypal', 'IPN transaction ID has been processed before.', array(), WATCHDOG_NOTICE);
       }
       return;
     }
-
-    db_insert('uc_payment_paypal_ipn')
-      ->fields(array(
-        'order_id' => $order_id,
-        'txn_id' => $txn_id,
-        'txn_type' => $txn_type,
-        'mc_gross' => $payment_amount,
-        'status' => $payment_status,
-        'receiver_email' => $receiver_email,
-        'payer_email' => $payer_email,
-        'received' => REQUEST_TIME,
-      ))
-      ->execute();
-
-    $context = array(
-      'revision' => 'formatted-original',
-      'type' => 'amount',
-    );
-    $options = array(
-      'sign' => FALSE,
-    );
-
-    switch ($payment_status) {
-      case 'Canceled_Reversal':
-        uc_order_comment_save($order_id, 0, t('PayPal has cancelled the reversal and returned !amount !currency to your account.', array('!amount' => uc_price($payment_amount, $context, $options), '!currency' => $payment_currency)), 'admin');
-        break;
-
-      case 'Completed':
-        $comment = t('PayPal transaction ID: @txn_id', array('@txn_id' => $txn_id));
-        uc_payment_enter($order_id, 'paypal_wps', $payment_amount, $order->uid, NULL, $comment);
-        uc_cart_complete_sale($order);
-        uc_order_comment_save($order_id, 0, t('Payment of @amount @currency submitted through PayPal.', array('@amount' => uc_price($payment_amount, $context, $options), '@currency' => $payment_currency)), 'order', 'payment_received');
-        uc_order_comment_save($order_id, 0, t('PayPal IPN reported a payment of @amount @currency.', array('@amount' => uc_price($payment_amount, $context, $options), '@currency' => $payment_currency)));
-        break;
-
-      case 'Denied':
-        uc_order_comment_save($order_id, 0, t("You have denied the customer's payment."), 'admin');
-        break;
-
-      case 'Expired':
-        uc_order_comment_save($order_id, 0, t('The authorization has failed and cannot be captured.'), 'admin');
-        break;
-
-      case 'Failed':
-        uc_order_comment_save($order_id, 0, t("The customer's attempted payment from a bank account failed."), 'admin');
-        break;
-
-      case 'Pending':
-        uc_order_update_status($order_id, 'paypal_pending');
-        uc_order_comment_save($order_id, 0, t('Payment is pending at PayPal: @reason', array('@reason' => _uc_paypal_pending_message(check_plain($_POST['pending_reason'])))), 'admin');
-        break;
-
-      // You, the merchant, refunded the payment.
-      case 'Refunded':
-        $comment = t('PayPal transaction ID: @txn_id', array('@txn_id' => $txn_id));
-        uc_payment_enter($order_id, 'paypal_wps', $payment_amount, $order->uid, NULL, $comment);
-        break;
-
-      case 'Reversed':
-        watchdog('uc_paypal', 'PayPal has reversed a payment!', array(), WATCHDOG_ERROR);
-        uc_order_comment_save($order_id, 0, t('Payment has been reversed by PayPal: @reason', array('@reason' => _uc_paypal_reversal_message(check_plain($_POST['reason_code'])))), 'admin');
-        break;
-
-      case 'Processed':
-        uc_order_comment_save($order_id, 0, t('A payment has been accepted.'), 'admin');
-        break;
-
-      case 'Voided':
-        uc_order_comment_save($order_id, 0, t('The authorization has been voided.'), 'admin');
-        break;
-    }
-  }
-  elseif (strcmp($response->data, 'INVALID') == 0) {
-    watchdog('uc_paypal', 'IPN transaction failed verification.', array(), WATCHDOG_ERROR);
-    uc_order_comment_save($order_id, 0, t('An IPN transaction failed verification for this order.'), 'admin');
+    return TRUE;
   }
+  return FALSE;
+}
+
+/**
+ * Paypal IPN save function.
+ */
+function uc_paypal_ipn_save($ipn) {
+  drupal_write_record('uc_payment_paypal_ipn', $ipn);
 }
 
 // Handles the review page for Express Checkout Mark Flow.
