diff --git uc_protx_vsp_direct.install uc_protx_vsp_direct.install
new file mode 100755
index 0000000..07479a4
--- /dev/null
+++ uc_protx_vsp_direct.install
@@ -0,0 +1,198 @@
+<?php
+
+/**
+ * Implementation of hook_schema().
+ */
+function uc_protx_vsp_direct_schema() {
+  $schema = array();
+  
+  $schema['uc_protx_vsp_direct_transactions'] = array(
+    'description' => 'Stores transaction requests information',
+    'fields' => array(
+      'tid' => array(
+        'description' => 'The primary identifier for a transaction.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE),
+      'oid' => array(
+        'description' => 'The current {orders}.oid identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'TxType' => array(
+        'description' => 'Transaction type: "PAYMENT", "DEFERRED" or "AUTHENTICATE".',
+        'type' => 'varchar',
+        'length' => 15,
+        'not null' => TRUE,
+        'default' => ''),
+      'VendorTxCode' => array(
+        'description' => 'This should be your own reference code to the transaction. You should provide a completely unique VendorTxCode for each transaction.',
+        'type' => 'varchar',
+        'length' => 40,
+        'not null' => TRUE,
+        'default' => ''),
+      'VPSTxId' => array(
+        'description' => 'Response. Sage Pay ID to uniquely identify the transaction. Only present if Status is OK.',
+        'type' => 'varchar',
+        'length' => 38,
+        'not null' => TRUE,
+        'default' => ''),
+      'SecurityKey' => array(
+        'description' => '10-digit alphanumeric code used in digitally signing the transaction',
+        'type' => 'char',
+        'length' => 10,
+        'not null' => TRUE,
+        'default' => ''),
+      'TxAuthNo' => array(
+        'description' => 'Unique reference number to the authorisation',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'Status' => array(
+        'description' => 'The transaction status',
+        'type' => 'varchar',
+        'length' => 15,
+        'not null' => TRUE,
+        'default' => ''),
+      'StatusDetail' => array(
+        'description' => 'Human-readable text providing extra detail for the Status message.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''),
+      'AVSCV2' => array(
+        'description' => 'Response from AVS and CV2 checks.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''),
+      'AddressResult' => array(
+        'description' => 'The specific result of the checks on the cardholder\'s address from the AVS/CV2 checks.',
+        'type' => 'varchar',
+        'length' => 20,
+        'not null' => TRUE,
+        'default' => ''),
+      'PostCodeResult' => array(
+        'description' => 'The specific result of the checks on the cardholder\'s Post Code from the AVS/CV2 checks. Not present if the Status is 3DAUTH, AUTHENTICATED or REGISTERED, or PPREDIRECT',
+        'type' => 'varchar',
+        'length' => 20,
+        'not null' => TRUE,
+        'default' => ''),
+      'CV2Result' => array(
+        'description' => 'The specific result of the checks on the cardholder\'s CV2 code from the AVS/CV2 checks. Not present if the Status is 3DAUTH, AUTHENTICATED or REGISTERED, or PPREDIRECT',
+        'type' => 'varchar',
+        'length' => 20,
+        'not null' => TRUE,
+        'default' => ''),
+      'raw_response' => array(
+        'description' => 'Raw response data',
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'medium'),
+    ),
+    'unique keys' => array(
+      'oid'     => array('oid'),
+      'VendorTxCode' => array('VendorTxCode'),
+    ),
+    'primary key' => array('tid'),
+  );
+
+  $schema['uc_protx_vsp_direct_refunds'] = array(
+    'description' => 'Stores refunds information',
+    'fields' => array(
+      'rid' => array(
+        'description' => 'The primary identifier for a refund.',
+        'type' => 'serial',
+        'unsigned' => TRUE,
+        'not null' => TRUE),
+      'tid' => array(
+        'description' => 'The current {uc_protx_requests}.tid identifier.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'uid' => array(
+        'description' => 'The id of the user who requested the refund',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'VendorTxCode' => array(
+        'description' => 'This should be your own reference code for the refund. You should provide a completely unique VendorTxCode for each transaction.',
+        'type' => 'varchar',
+        'length' => 40,
+        'not null' => TRUE,
+        'default' => ''),
+      'amount' => array(
+        'description' => 'Amount to Refund. You can make multiple refunds against a single transaction but the total value of all refunds CANNOT exceed the amount of the original transaction.',
+        'type' => 'float',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0.0),
+      'Description' => array(
+        'description' => 'Free text description of reason for Refund.',
+        'type' => 'varchar',
+        'length' => 100,
+        'not null' => TRUE,
+        'default' => ''),
+      'VPSTxId' => array(
+        'description' => 'Response. Sage Pay ID to uniquely identify the transaction. Only present if Status is OK.',
+        'type' => 'varchar',
+        'length' => 38,
+        'not null' => TRUE,
+        'default' => ''),
+      'TxAuthNo' => array(
+        'description' => 'The Sage Pay authorisation code (also called VPSAuthCode) for the transaction. Only present if Status is OK.',
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0),
+      'Status' => array(
+        'description' => 'The transaction status',
+        'type' => 'varchar',
+        'length' => 15,
+        'not null' => TRUE,
+        'default' => ''),
+      'StatusDetail' => array(
+        'description' => 'Human-readable text providing extra detail for the Status message.',
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => ''),
+      'refunded_at' => array(
+        'description' => t('Time when the refund was requested.'),
+        'type' => 'int',
+        'not null' => FALSE,
+        'default' => 0,
+      ),
+      'raw_response' => array(
+        'description' => 'Raw response data',
+        'type' => 'text',
+        'not null' => TRUE,
+        'size' => 'medium'),
+    ),
+    'unique keys' => array(
+      'rid'     => array('rid'),
+      'VendorTxCode' => array('VendorTxCode'),
+    ),
+    'primary key' => array('rid'),
+  );
+
+  return $schema;
+}
+
+/**
+ * Implementation of hook_install
+ */
+function uc_protx_vsp_direct_install() {
+  drupal_install_schema('uc_protx_vsp_direct');
+}
+
+/**
+ * Implementation of hook_uninstall
+ */
+function uc_protx_vsp_direct_uninstall() {
+  drupal_uninstall_schema('uc_protx_vsp_direct');
+}
diff --git uc_protx_vsp_direct.module uc_protx_vsp_direct.module
index 05a63f5..5ef3290 100755
--- uc_protx_vsp_direct.module
+++ uc_protx_vsp_direct.module
@@ -102,6 +102,15 @@ function uc_protx_vsp_direct_menu() {
     'access arguments' => array('authorize credit cards'),
     'type' => MENU_CALLBACK
   );
+  $items['admin/store/orders/%uc_order/refunds'] = array(
+    'title' => 'Refunds',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('uc_protx_vsp_direct_refund_form', 3),
+    'access arguments' => array('refund orders'),
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 3,
+    'file' => 'uc_protx_vsp_direct_refunds.inc',
+  );
 
   return $items;
 }
@@ -477,6 +486,16 @@ function uc_protx_vsp_direct_charge($order_id, $amount, $data) {
   }
   $post = substr($post, 0, -1);
 
+  // inject the oid of the order
+  $transaction['oid'] = $order_id;
+  // inject the status
+  $transaction['StatusDetail'] = t('Sending request...');
+  
+  // log request
+  if(!$transaction_inserted = uc_protx_vsp_direct_insert_transaction($transaction)) {
+    watchdog('error', 'Could not insert transaction', array(), WATCHDOG_WARNING);
+  }
+  
   list($response, $http_response_code, $curl_error) = uc_protx_vsp_direct_curl($url, $post);
 
   if ($curl_error!='') {
@@ -499,6 +518,11 @@ function uc_protx_vsp_direct_charge($order_id, $amount, $data) {
   );
   uc_order_comment_save($order_id, $user->uid, $comment);
 
+  // update transaction with the response information
+  if($transaction_inserted) {
+    uc_protx_vsp_direct_update_transaction($response, $transaction['tid']);
+  }
+
   // ----------------------------------------
   // Now make sense of the response.
 
@@ -697,6 +721,11 @@ function uc_protx_vsp_direct_url($method, $server) {
       1 => 'https://test.sagepay.com/gateway/service/direct3dcallback.vsp',
       2 => 'https://live.sagepay.com/gateway/service/direct3dcallback.vsp',
     ),
+    'refund' => array(
+      0 => 'https://test.sagepay.com/Simulator/VSPServerGateway.asp?Service=VendorRefundTx',
+      1 => 'https://test.sagepay.com/gateway/service/refund.vsp',
+      2 => 'https://live.sagepay.com/gateway/service/refund.vsp',  
+    ),
   );
 
   return $servers[$method][$server];
@@ -911,6 +940,25 @@ function uc_protx_vsp_direct_auth_form_submit($form, &$form_state) {
   return 'admin/store/orders/'. $order_id .'/uc_protx_vsp_direct_auth';
 }
 
+function uc_protx_vsp_direct_auth_load_transaction($oid) {
+  return db_fetch_array(db_query("SELECT * FROM {uc_protx_vsp_direct_transactions} WHERE oid = %d", $oid));
+}
+
+function uc_protx_vsp_direct_insert_transaction(&$transaction) {
+  return drupal_write_record('uc_protx_vsp_direct_transactions', $transaction);
+}
+
+function uc_protx_vsp_direct_update_transaction($response, $tid) {
+  $response['raw_response'] = serialize($response);
+  $response['tid'] = $tid;
+  drupal_write_record('uc_protx_vsp_direct_transactions', $response, 'tid');
+}
+
+function uc_protx_vsp_direct_auth_successful_transaction($transaction) {
+  return isset($transaction['Status']) && $transaction['Status'] == 'OK';
+}
+
+
 /**
  * Outputs the cards used in the configuration fieldset
  * @return <string>
diff --git uc_protx_vsp_direct_refunds.inc uc_protx_vsp_direct_refunds.inc
new file mode 100755
index 0000000..e06f010
--- /dev/null
+++ uc_protx_vsp_direct_refunds.inc
@@ -0,0 +1,184 @@
+<?php
+
+function uc_protx_vsp_direct_refund_form($form_state, $order) {
+  $form['order_id'] = array(
+    '#type' => 'hidden', 
+    '#value' => $order->order_id,
+  );
+  
+  $form['price'] = array(
+    '#value' => t('Order total: <strong>%price %currency</strong>', array('%price' => $order->order_total, '%currency' => variable_get('uc_currency_sign', '£'))),
+    '#prefix' => '<div>',
+    '#suffix' => '</div>',
+  );
+  
+  $balance_amount = $order->order_total - uc_protx_vsp_direct_refund_compute_refunds($order->order_id);
+  
+  $form['balance'] = array(
+    '#value' => t('Current balance: <strong>%balance %currency</strong>', array('%balance' => $balance_amount, '%currency' => variable_get('uc_currency_sign', '£'))),
+    '#prefix' => '<div>',
+    '#suffix' => '</div>',
+  );
+  
+  $form['amount'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Amount to refund (%currency)', array('%currency' => variable_get('uc_currency_sign', '£'))),
+    '#default_value' => $balance_amount,
+    '#required' => TRUE,
+    '#size' => 20,
+  );
+  
+  $form['description'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Description of reason for refund (max 100 characters)'),
+    '#required' => TRUE,
+  );
+  
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Refund'),
+  );
+  
+  $form['#validate'] = array('uc_protx_vsp_direct_refund_form_validate');
+  $form['#submit'] = array('uc_protx_vsp_direct_refund_form_submit');
+  
+ return $form;
+}
+
+
+function uc_protx_vsp_direct_refund_form_validate($form, &$form_state) {
+  
+  $vendor = variable_get('uc_protx_vsp_direct_vendor', '');
+  if ($vendor == '' || strlen($vendor) > 15) {
+    form_set_error('', t('Invalid Vendor Login Name.  Authorization Request could not be sent.'));
+  }
+  
+  $order = uc_order_load($form_state['values']['order_id']);
+
+  $balance = $order->order_total - uc_protx_vsp_direct_refund_compute_refunds($form_state['values']['order_id']);
+  $amount = (float) $form_state['values']['amount'];
+  
+  if(!$order) {
+    form_set_error('', t('Could not load order: #%order_id', array('%order_id' => $form_state['values']['order_id'])));
+  }
+  elseif(strlen($form_state['values']['description'] > 100)) {
+    form_set_error('description', t('The description should be less than 100 characters.'));
+  }
+  elseif(!is_numeric($amount)) {
+    form_set_error('amount', t('The refund amount is invalid.'));
+  }
+  elseif($amount < 1 || $amount > 100000) {
+    form_set_error('amount', t('The refund amount should be a value bigger than 1.00 and smaller than 100,000.00.'));
+  }
+  elseif ($amount > $balance) {
+    form_set_error('amount', t('The refund amount should be less or equal than the balance of the order.'));
+  }
+}
+
+function uc_protx_vsp_direct_refund_form_submit($form, &$form_state) {
+  global $user;
+  
+  $oid = $form_state['values']['order_id'];
+  
+  $order_transaction = uc_protx_vsp_direct_auth_load_transaction($oid);
+  
+  $amount = sprintf('%01.2f', $form_state['values']['amount']);
+  
+  $transaction = array(
+    'VPSProtocol' => UC_PROTX_VSP_DIRECT_PROTOCOL_VERSION,
+    'TxType' => 'REFUND',
+    'Vendor' => variable_get('uc_protx_vsp_direct_vendor', ''),
+    'VendorTxCode' => md5(  time() . $user->uid . $oid . rand()  ), // This must be unique to the vendor.
+    'Amount' => $amount,
+    'Currency' => variable_get('uc_currency_code', 'GBP'), // This is a UK-based payment gateway, hence GBP default.
+    'Description' => $form_state['values']['description'],
+    'RelatedVPSTxId' => $order_transaction['VPSTxId'],
+    'RelatedVendorTxCode' => $order_transaction['VendorTxCode'],
+    'RelatedSecurityKey' => $order_transaction['SecurityKey'],
+    'RelatedTxAuthNo' => $order_transaction['TxAuthNo'],
+  );
+
+  // Set HTTPS URL
+  $url = uc_protx_vsp_direct_url('refund', variable_get('uc_protx_vsp_direct_server', 2));
+  
+  // Put all this data into an HTTPS POST request
+  $post = '';
+  foreach ($transaction as $name => $value) {
+    //$post .= urlencode(iconv('UTF-8', 'ISO-8859-1', $name)) . '=' . urlencode(iconv('UTF-8', 'ISO-8859-1', $value)) . '&';
+    $post .= urlencode($name) .'='. urlencode($value) .'&';
+  }
+  $post = substr($post, 0, -1);
+
+  // record the uid of the user that requests the refund
+  $transaction['uid'] = $user->uid;
+  // record the uid of the user that requests the refund
+  $transaction['amount'] = $amount;
+  // inject the oid of the order
+  $transaction['tid'] = $order_transaction['tid'];
+  // inject the status
+  $transaction['StatusDetail'] = t('Sending request...');
+  // record the time for the refound request
+  $transaction['refunded_at'] = time();
+
+  // log request
+  if(!$transaction_inserted = uc_protx_vsp_direct_insert_refund($transaction)) {
+    watchdog('error', 'Could not insert refund', array(), WATCHDOG_WARNING);
+  }
+
+  list($response, $http_response_code, $curl_error) = uc_protx_vsp_direct_curl($url, $post);
+  
+  switch($response['Status']) {
+    case 'OK':
+      drupal_set_message(t('Successfuly refunded %amount %currency', array('%amount' => $amount, '%currency' => variable_get('uc_currency_sign', '£'))));
+      uc_order_log_changes($oid, array(t('Successfuly refunded %amount %currency', array('%amount' => $amount, '%currency' => variable_get('uc_currency_sign', '£')))));
+      //watchdog('uc_protx_vsp_direct', 'Successfuly refunded %amount %currency', array('%amount' => $amount, '%currency' => variable_get('uc_currency_sign', '£')), WATCHDOG_NOTICE, $order_link);
+      break;
+    case 'NOTAUTHED':
+    case 'MALFORMED':
+    case 'INVALID':
+      drupal_set_message(t('The request was not successful. This is the StatusDetail received from gateway: %status_detail', array('%status_detail' => $response['StatusDetail'])), 'warning');
+      // set the amount to 0
+      $response['amount'] = 0;
+      break;
+    case 'ERROR':
+      drupal_set_message(t('I got an ERROR status repsonse. This is the StatusDetail received from gateway: %status_detail', array('%status_detail' => $response['StatusDetail'])), 'error');
+      // We don't actually know if the refund took place or not
+      $order_link = l($oid, 'admin/store/orders/' . $oid . '/refund');
+      watchdog('uc_protx_vsp_direct', "Received an ERROR status response while trying to refund order %order. StatusDetail: %status_detail", array('%order' => $order_link, '%status_detail' => $response['StatusDetail']), WATCHDOG_ERROR, $order_link);
+      break;
+
+    default:
+      // This should never happen.
+      $order_link = l($oid, 'admin/store/orders/' . $oid . '/refund');
+      watchdog('uc_protx_vsp_direct', 'SagePay responded with a status code that indicates acceptance of the request, but which is not implemented by this module: @code @StatusDetail', array('@code' => $response['Status'], '@StatusDetail' => $response['StatusDetail']), WATCHDOG_ERROR, $order_link);
+      break;
+  }
+  
+  // update transaction with the response information
+  if($transaction_inserted) {
+    uc_protx_vsp_direct_update_refund($response, $transaction['rid']);
+  }
+}
+
+function uc_protx_vsp_direct_insert_refund(&$transaction) {
+  return drupal_write_record('uc_protx_vsp_direct_refunds', $transaction);
+}
+
+function uc_protx_vsp_direct_update_refund($response, $rid) {
+  $response['raw_response'] = serialize($response);
+  $response['rid'] = $rid;
+  drupal_write_record('uc_protx_vsp_direct_refunds', $response, 'rid');
+}
+
+function uc_protx_vsp_direct_refund_compute_refunds($oid) {
+  $transaction = uc_protx_vsp_direct_auth_load_transaction($oid);
+  
+  $total_refunds_amount = 0;
+  
+  $query = db_query("SELECT amount FROM {uc_protx_vsp_direct_refunds} WHERE tid = %d", $transaction['tid']);
+  while($refund = db_fetch_array($query)) {
+    $total_refunds_amount += current($refund);
+  }
+  
+  return $total_refunds_amount;
+}
