diff -urpN uc_coupon-1.x-dev-20110215/theme/uc_coupon-certificate.tpl.php uc_coupon-1.x-dev--store-credit/theme/uc_coupon-certificate.tpl.php
--- uc_coupon-1.x-dev-20110215/theme/uc_coupon-certificate.tpl.php	2010-12-21 19:22:09.000000000 -0500
+++ uc_coupon-1.x-dev--store-credit/theme/uc_coupon-certificate.tpl.php	2011-02-17 02:57:38.000000000 -0500
@@ -1,14 +1,16 @@
-<?php 
+<?php
 // $Id: uc_coupon-certificate.tpl.php,v 1.4 2010/12/22 00:22:09 longwave Exp $
 
 /**
  * Basic template for a printed coupon certificate.
- * 
+ *
  * Coupon data is available in the following variables:
- * 
+ *
  * $coupon - the coupon object
  * $code - the specific coupon code to be printed
+ * $value - the discount amount
  * $display_name - the name of the coupon (with "purchased by..." stripped)
+ * $credit - TRUE for store credit coupons
  * $include - an array of the items for which this coupon is valid, if any
  * $exclude - an array of the items for which this coupon is not valid, if any
  * $valid_from - the formatted date the coupon is valid from, if set
@@ -21,7 +23,7 @@
  * $coupon->owner - the account object of the purchaser
  *
  * Also includes global tokens, and thus the global Ubercart store information.
- * 
+ *
  * Templates for specific coupons may be created after the following patterns:
  *   uc_coupon-certificate-[cid].tpl.php
  *     (where [cid] is the id of the coupon for which the template should be applied)
@@ -43,11 +45,21 @@
   <h2 id="title"><?php print $display_name; ?></h2>
   <h1 class="uc-coupon-code"><?php print $code; ?></h1>
 
-  <p>This certificate entitles you to a discount of <?php print $value ?> at <?php print $store_name ?>.
-    <?php if ($store_url): ?>
-      Please visit us at <?php print $store_url; ?> and enter the code <strong><?php print $code; ?></strong> at checkout to obtain your discount.
-    <?php endif; ?>
-  </p>
+  <?php if (!$credit): ?>
+    <p>This certificate entitles you to a discount of <?php print $value ?> at <?php print $store_name ?>.
+      <?php if ($store_url): ?>
+        Please visit us at <?php print $store_url; ?> and enter the code <strong><?php print $code; ?></strong> at checkout to obtain your discount.
+      <?php endif; ?>
+    </p>
+  <?php endif; ?>
+
+  <?php if ($credit): ?>
+    <p>This certificate entitles you to a store credit amount of <?php print $value ?> at <?php print $store_name ?>.
+      <?php if ($store_url): ?>
+        Please log in or register at <?php print $store_url; ?> and enter the code <strong><?php print $code; ?></strong> to redeem your store credit.
+      <?php endif; ?>
+    </p>
+  <?php endif; ?>
 
   <?php if ($valid_until): ?>
     <p>Valid <?php if ($not_yet_valid) print "from " . $valid_from; ?> until <?php print $valid_until; ?>.</p>
@@ -60,7 +72,7 @@
     <?php if ($include): ?>
       <dt>May be used for any of the following:</dt>
       <dd><?php print implode(", ", $include); ?>.</dd>
-    <?php endif; ?> 
+    <?php endif; ?>
 
     <?php if ($exclude): ?>
       <dt>Not valid for any of the following:</dt>
diff -urpN uc_coupon-1.x-dev-20110215/uc_coupon.admin.inc uc_coupon-1.x-dev--store-credit/uc_coupon.admin.inc
--- uc_coupon-1.x-dev-20110215/uc_coupon.admin.inc	2011-02-15 10:58:48.000000000 -0500
+++ uc_coupon-1.x-dev--store-credit/uc_coupon.admin.inc	2011-02-17 02:57:38.000000000 -0500
@@ -6,7 +6,7 @@
  */
 function uc_coupon_settings_form() {
   global $base_url;
- 
+
   $form['uc_coupon_show_in_cart'] = array(
     '#type' => 'checkbox',
     '#title' => t('Show coupons as an item in the cart.'),
@@ -160,9 +160,12 @@ function uc_coupon_view($coupon) {
     case 'expensive':
       $applies = format_plural($coupon->data['apply_count'], 'Most expensive product', '@count most expensive products');
       break;
+    case 'credit':
+      $applies = t('Store credit');
+      break;
   }
   $rows[] = array(t('Applied to'), $applies);
-  
+
   if ($coupon->data['apply_to'] != 'subtotal') {
     $restrict = array();
     if (isset($coupon->data['product_types'])) {
@@ -299,6 +302,12 @@ function uc_coupon_add_form(&$form_state
     $form['#uc_coupon'] = $value;
     $form['#uc_coupon_used'] = $used;
     $use_validity = $value->valid_until;
+    // If we're dealing with a redeemed credit coupon which holds a user's store credit balance...
+    if ($value->type == 'credit' && $value->status == 0 && $value->value > 0) {
+      $used = TRUE;
+      $creditcoupon = TRUE;
+      $form['#uc_coupon_credit'] = $value->code;
+    }
   }
   else {
     $use_validity = FALSE;
@@ -351,6 +360,7 @@ function uc_coupon_add_form(&$form_state
     '#default_value' => $value->name,
     '#required' => TRUE,
     '#weight' => -15,
+    '#disabled' => $creditcoupon,
   );
 
   $form['code'] = array(
@@ -411,21 +421,36 @@ function uc_coupon_add_form(&$form_state
     '#title' => t('Coupon is active.'),
     '#description' => t('Only active coupons can be used at checkout.'),
     '#default_value' => $value->status,
+    '#disabled' => $creditcoupon,
   );
 
   $form['discount'] = array(
     '#type' => 'textfield',
     '#title' => t('Discount'),
-    '#default_value' => uc_coupon_format_discount($value, FALSE),
+    '#default_value' => $value->value,
     '#size' => 6,
-    '#description' => t('Three discount types are possible:') . theme('item_list', array(
-      t('Fixed price discount; enter "15" for $15.00 off.'),
-      t('Percentage discount; enter "15%" for 15% off.'),
-      t('Set product price; enter "=15" to make all matching products $15.00 after discount.'),
-    )),
-    '#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', '$') : '',
+    '#description' => t('Enter a plain number and select the discount type below.'),
+    '#required' => TRUE,
+  );
+
+  $form['type'] = array(
+    '#type' => 'radios',
+    '#title' => t('Discount type'),
+    '#default_value' => $value->type,
+    '#options' => array(
+      'price' => 'Fixed price discount. For example: $15.00 off',
+      'percentage' => 'Percentage discount. For example: 15% off',
+      'set_price' => 'Set product price. For example: make all matching products $15.00',
+      'credit' => 'Store credit. For example: $15.00 gift certificate',
+    ),
     '#required' => TRUE,
+    '#disabled' => $creditcoupon,
+  );
+
+  $form['credit_options'] = array(
+    '#prefix' => '<div id="credit-options">',
+    '#value' => '<i>For store credit coupons, only the "Valid dates" option applies below. All other options are ignored.</i>',
+    '#suffix' => '</div>',
   );
 
   $form['apply_to'] = array(
@@ -448,6 +473,7 @@ function uc_coupon_add_form(&$form_state
   );
 
   $form['use_validity'] = array(
+    '#prefix' => '<div class="form-item"><label>Valid dates:</label></div>',
     '#type' => 'checkbox',
     '#title' => t('Restrict coupon by date.'),
     '#default_value' => $use_validity,
@@ -753,11 +779,12 @@ function uc_coupon_add_form_validate($fo
     form_set_error('code', t('Coupon code already used by %name.', array('%name' => $name)));
   }
 
-  if (!preg_match('/^(=(?!.*%))?\d+(\.\d+)?%?$/', $form_state['values']['discount'])) {
-    form_set_error('discount', t('Invalid discount.'));
+  $form_state['values']['discount'] = abs($form_state['values']['discount']);
+  if ($form_state['values']['discount'] == 0) {
+    form_set_error('discount', t('Invalid discount value.'));
   }
 
-  if (substr($form_state['values']['discount'], 0, 1) == '=' && $form_state['values']['apply_to'] == 'subtotal') {
+  if ($form_state['values']['type'] == 'set_price' && $form_state['values']['apply_to'] == 'subtotal') {
     form_set_error('apply_to', t('Set prices cannot be applied to the order subtotal.'));
   }
 
@@ -801,6 +828,9 @@ function uc_coupon_add_form_submit($form
     $form_state['values']['code'] = $form['#uc_coupon']->code;
     $form_state['values']['bulk_number'] = $form['#uc_coupon']->data['bulk_number'];
   }
+  if ($form['#uc_coupon_credit']) {
+    $form_state['values']['code'] = $form['#uc_coupon_credit'];
+  }
 
   // Ensure all fields of original coupon object propagate to presave hook.
   $coupon = (isset($form['#uc_coupon'])) ? $form['#uc_coupon'] : new stdClass;
@@ -822,14 +852,8 @@ function uc_coupon_add_form_submit($form
     $coupon->valid_from = $coupon->valid_until = 0;
   }
   $coupon->status = $form_state['values']['status'];
-  $coupon->type = 'price';
-  if (substr($form_state['values']['discount'], -1) == '%') {
-    $coupon->type = 'percentage';
-  }
-  if (substr($form_state['values']['discount'], 0, 1) == '=') {
-    $coupon->type = 'set_price';
-  }
-  $coupon->value = str_replace(array('%', '='), '', $form_state['values']['discount']);
+  $coupon->type = $form_state['values']['type'];
+  $coupon->value = $form_state['values']['discount'];
   $coupon->minimum_order = $form_state['values']['minimum_order'] ? $form_state['values']['minimum_order'] : 0;
   $coupon->data['minimum_qty'] = $form_state['values']['minimum_qty'];
   $coupon->max_uses = $form_state['values']['max_uses'] ? $form_state['values']['max_uses'] : 0;
@@ -838,6 +862,9 @@ function uc_coupon_add_form_submit($form
   if ($form_state['values']['apply_to'] == 'cheapest' || $form_state['values']['apply_to'] == 'expensive') {
     $coupon->data['apply_count'] = $form_state['values']['apply_count'];
   }
+  if ($form_state['values']['type'] == 'credit') {
+    $coupon->data['apply_to'] = 'credit';
+  }
 
   // Map restrictions back to their IDs.
   array_walk($form_state['values']['products'], '_uc_coupon_map_restriction', 'nid');
diff -urpN uc_coupon-1.x-dev-20110215/uc_coupon.admin.js uc_coupon-1.x-dev--store-credit/uc_coupon.admin.js
--- uc_coupon-1.x-dev-20110215/uc_coupon.admin.js	2010-12-21 19:57:41.000000000 -0500
+++ uc_coupon-1.x-dev--store-credit/uc_coupon.admin.js	2011-02-17 02:57:38.000000000 -0500
@@ -24,4 +24,14 @@ Drupal.behaviors.ucCouponAdmin = functio
   }).is(':not(:checked)')) {
     $('#edit-valid-from-wrapper, #edit-valid-until-wrapper').hide();
   }
+
+  $('#credit-options').hide();
+  $('input[name=type]', context).click(function() {
+    if (this.value == 'credit') {
+      $('#credit-options').show();
+    }
+    else {
+      $('#credit-options').hide();
+    }
+  }).filter(':checked').click();
 }
diff -urpN uc_coupon-1.x-dev-20110215/uc_coupon.install uc_coupon-1.x-dev--store-credit/uc_coupon.install
--- uc_coupon-1.x-dev-20110215/uc_coupon.install	2010-12-17 19:07:02.000000000 -0500
+++ uc_coupon-1.x-dev--store-credit/uc_coupon.install	2011-02-17 02:57:38.000000000 -0500
@@ -91,11 +91,17 @@ function uc_coupon_schema() {
         'not null' => TRUE,
         'default' => 0,
       ),
+      'user' => array(
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
     ),
     'primary key' => array('cid'),
   );
 
-  $schema['uc_coupons_orders'] = array( 
+  $schema['uc_coupons_orders'] = array(
     'description' => t('Ubercart Coupons used on Orders'),
     'fields' => array(
       'cuid' => array(
@@ -271,3 +277,20 @@ function uc_coupon_update_6006() {
 
   return $ret;
 }
+
+// Add user column for store credit coupons
+function uc_coupon_update_6007() {
+  $ret = array();
+
+  if (!db_column_exists('uc_coupons', 'user')) {
+    $field = array(
+      'type' => 'int',
+      'unsigned' => TRUE,
+      'not null' => TRUE,
+      'default' => 0,
+    );
+    db_add_field($ret, 'uc_coupons', 'user', $field);
+  }
+
+  return $ret;
+}
diff -urpN uc_coupon-1.x-dev-20110215/uc_coupon.js uc_coupon-1.x-dev--store-credit/uc_coupon.js
--- uc_coupon-1.x-dev-20110215/uc_coupon.js	2010-12-16 18:49:09.000000000 -0500
+++ uc_coupon-1.x-dev--store-credit/uc_coupon.js	2011-02-17 02:57:38.000000000 -0500
@@ -20,7 +20,7 @@ function getCoupon() {
 
       if (coupon.valid) {
         if (window.set_line_item) {
-          set_line_item('coupon', coupon.title, -coupon.amount, 0);
+          set_line_item('coupon', coupon.title, -coupon.amount, 1);
         }
       }
       else {
@@ -28,7 +28,46 @@ function getCoupon() {
           remove_line_item('coupon');
         }
       }
-  
+
+      if (window.getTax) {
+        getTax();
+      }
+      else if (window.render_line_items) {
+        render_line_items();
+      }
+    }
+  });
+}
+
+function getCredit() {
+  $('#credit-message').remove();
+  $('#credit-throbber').addClass('ubercart-throbber').html('&nbsp;&nbsp;&nbsp;&nbsp;');
+
+  var amount = $('#edit-panes-credit-amount');
+
+  $.ajax({
+    type: "POST",
+    url: Drupal.settings.ucURL.applyCredit,
+    data: {
+      amount: amount.val(),
+      order: serializeOrder()
+    },
+    dataType: "json",
+    success: function(credit) {
+      $('#credit-throbber').removeClass('ubercart-throbber');
+      amount.parent().parent().append('<div id="credit-message">' + credit.message + '</div>');
+
+      if (credit.valid) {
+        if (window.set_line_item) {
+          set_line_item('credit', credit.title, -credit.amount, 1);
+        }
+      }
+      else {
+        if (window.remove_line_item) {
+          remove_line_item('credit');
+        }
+      }
+
       if (window.getTax) {
         getTax();
       }
diff -urpN uc_coupon-1.x-dev-20110215/uc_coupon.module uc_coupon-1.x-dev--store-credit/uc_coupon.module
--- uc_coupon-1.x-dev-20110215/uc_coupon.module	2011-02-15 10:58:48.000000000 -0500
+++ uc_coupon-1.x-dev--store-credit/uc_coupon.module	2011-02-17 03:03:40.000000000 -0500
@@ -3,6 +3,8 @@
 
 require_once 'uc_coupon.ca.inc';
 
+include_once 'uc_coupon.user.inc';
+
 /**
  * @file
  * Provides discount coupons for Ubercart.
@@ -178,6 +180,23 @@ function uc_coupon_menu() {
     'type' => MENU_CALLBACK,
   );
 
+  $items['cart/checkout/credit'] = array(
+    'title' => 'Apply credit',
+    'page callback' => 'uc_coupon_credit_checkout_apply',
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['redeem'] = array(
+    'title' => 'Redeem gift certificate',
+    'description' => 'Redeem a gift certificate code.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('uc_coupon_redeem_credit_form'),
+    'access callback' => 'user_is_logged_in',
+    'file' => 'uc_coupon.user.inc',
+    'type' =>  MENU_SUGGESTED_ITEM,
+  );
+
   return $items;
 }
 
@@ -249,14 +268,14 @@ function theme_uc_checkout_pane_coupon($
 
 /**
  * Save a coupon object.
- * 
+ *
  * If the 'cid' field is set, then this will update an existing coupon.
  * Otherwise, a new bulk seed will be generated, the coupon will be
  * inserted into the database, and $coupon->cid will be set.
- * 
+ *
  * @param $coupon
  *   The coupon to save.
- * 
+ *
  * @param $edit
  *   An optional array of extra data that other modules may need to save.
  */
@@ -277,7 +296,7 @@ function uc_coupon_save(&$coupon, $edit 
   }
 
   // Notify other modules that a coupon has been saved.
-  module_invoke_all('uc_coupon_save' ,$coupon);
+  module_invoke_all('uc_coupon_save', $coupon);
 }
 
 /**
@@ -363,6 +382,7 @@ function uc_coupon_count_usage($cid, $ui
 function uc_coupon_format_discount($coupon, $currency = TRUE) {
   switch ($coupon->type) {
     case 'price':
+    case 'credit':
       return $currency ? uc_currency_format($coupon->value) : $coupon->value;
 
     case 'percentage':
@@ -468,6 +488,11 @@ function uc_coupon_validate($code, $orde
     return $coupon;
   }
 
+  if ($coupon->type == 'credit') {
+    $coupon->message = t('Store credit codes can be redeemed !link.', array('!link' => l('here', 'redeem')));
+    return $coupon;
+  }
+
   // Assume the coupon is valid, unless a validation hook fails.
   $coupon->code = $code;
   $coupon->valid = TRUE;
@@ -509,8 +534,75 @@ function uc_coupon_validate($code, $orde
 }
 
 /**
+ * Validate the store credit applied to an order and calculate the credit discount.
+ *
+ * @param $amount
+ *  The credit amount entered at the checkout screen.
+ *
+ * @return
+ *  A coupon object with details about the validation.
+ */
+function uc_coupon_validate_credit($amount) {
+  global $user;
+
+  $result->valid = FALSE;
+
+  if ($amount < 0) {
+    $result->message = 'Discount amount cannot be less than zero.';
+    return $result;
+  }
+
+  if (!is_numeric($amount)) {
+    $result->message = 'Unable to parse your entry. Be sure to enter a number only, without a currency symbol or other characters.';
+    return $result;
+  }
+
+  if ($amount == 0) {
+    $result->message = 'Discount amount set to zero.';
+    return $result;
+  }
+
+  $coupon = db_fetch_object(db_query("SELECT * FROM {uc_coupons} WHERE type = 'credit' AND user = %d AND valid_from < %d AND (valid_until = 0 OR valid_until > %d)", $user->uid, time(), time()));
+  if (!$coupon) {
+    $result->message = 'You have no store credit available.';
+    return $result;
+  }
+
+  $result->message = '';
+
+  $balance = $coupon->value;
+
+  if ($amount > $balance) {
+    $amount = $balance;
+    $result->message .= t('You have entered an amount greater than your store credit balance. ');
+  }
+
+  $cart_total = 0;
+  foreach (uc_cart_get_contents() as $item) {
+    if ($item->type != 'credit') {
+      $cart_total += $item->price * $item->qty;
+    }
+  }
+  if ($amount > $cart_total) {
+    $amount = $cart_total;
+    $result->message .= t('You have entered an amount greater than the order total. ');
+  }
+
+  $result->message .= t('A discount of !amount has been applied to your order.', array('!amount' => uc_currency_format($amount)));
+
+  $result->valid = TRUE;
+  $result->amount = $amount;
+  $result->type = 'credit';
+  $result->title = 'Store credit';
+  $result->cid = $coupon->cid;
+  $result->code = $coupon->code;
+
+  return $result;
+}
+
+/**
  * Implementation of hook_uc_coupon_validate().
- * 
+ *
  * @param $coupon
  *   The coupon object to validate, with special fields set as follows:
  *   - $coupon->code: The specific code to be applied (even for bulk coupons).
@@ -541,7 +633,7 @@ function uc_coupon_uc_coupon_validate(&$
   if ($account && isset($coupon->data['max_uses_per_user']) && $coupon->usage['user'] >= $coupon->data['max_uses_per_user']) {
     return t('This coupon has reached the maximum redemption limit.');
   }
-  
+
   // Check minimum order total.
   if ($order && $coupon->minimum_order > 0) {
     $total = 0;
@@ -773,17 +865,72 @@ function uc_coupon_block($op = 'list', $
       $blocks[0] = array(
         'info' => t('Coupon discount form'),
       );
+      $blocks[1] = array(
+        'info' => t('Store credit discount form'),
+      );
+      $blocks[2] = array(
+        'info' => t('User store credit'),
+      );
+      $blocks[3] = array(
+        'info' => t('Redeem store credit'),
+      );
       return $blocks;
 
-    case 'view':
-      if ($delta == 0) {
-        $block = array(
-          'subject' => t('Coupon discount'),
-          'content' => drupal_get_form('uc_coupon_block_form'),
+    case 'configure':
+      if ($delta == 2) {
+        $form['uc_coupon_user_credit_block'] = array(
+          '#type' => 'textarea',
+          '#title' => t('Content for the user store credit block'),
+          '#default_value' => variable_get('uc_coupon_block', t('You have <strong>!value</strong> in store credit, which can be applied to your order at checkout.')),
+          '#description' => t('Variable <em>!value</em> displays the total amount of credit available.'),
         );
-        return $block;
+      }
+      return $form;
+
+    case 'save':
+      if ($delta == 2) {
+        variable_set('uc_coupon_user_credit_block', $edit['uc_coupon_user_credit_block']);
       }
       break;
+
+    case 'view':
+      switch ($delta) {
+        case 0:
+          $block = array(
+            'subject' => t('Coupon discount'),
+            'content' => drupal_get_form('uc_coupon_block_form'),
+          );
+          return $block;
+
+        case 1:
+          global $user;
+          if ($user->uid > 0) {
+            $balance = uc_coupon_get_total_credit($user->uid);
+            if ($balance) {
+              $block['subject'] = t('Store credit discount');
+              $block['content'] = drupal_get_form('uc_coupon_credit_block_form');
+            }
+          }
+          return $block;
+
+        case 2:
+          global $user;
+          if ($user->uid > 0) {
+            $balance = uc_coupon_get_total_credit($user->uid);
+            if ($balance) {
+              $block['subject'] = t('Store credit');
+              $block['content'] = t(variable_get('uc_coupon_user_credit_block', t('You have <strong>!value</strong> in store credit, which can be applied to your order at checkout.')), array('!value' => uc_currency_format($balance)));
+            }
+          }
+          return $block;
+
+        case 3:
+          $block = array(
+            'subject' => t('Redeem'),
+            'content' => drupal_get_form('uc_coupon_redeem_credit_form', 15),
+          );
+          return $block;
+      }
   }
 }
 
@@ -799,6 +946,14 @@ function uc_coupon_cart_pane($items) {
     'weight' => 1,
     'enabled' => TRUE,
   );
+  $panes[] = array(
+    'id' => 'credit',
+    'body' => drupal_get_form('uc_coupon_credit_block_form', 25),
+    'title' => t('Store credit'),
+    'desc' => t('Allows shoppers to use store credit for order discounts.'),
+    'weight' => 1,
+    'enabled' => TRUE,
+  );
   return $panes;
 }
 
@@ -844,10 +999,63 @@ function uc_coupon_block_form_submit($fo
 }
 
 /**
+ * Store credit block form, also available as a cart pane.
+ */
+function uc_coupon_credit_block_form($form_state, $size = 15) {
+  global $user;
+
+  // Only show this form for logged in users.
+  if ($user->uid == 0) {
+    return;
+  }
+
+  $balance = uc_coupon_get_total_credit($user->uid);
+
+  // Only show this form if the user has a positive balance.
+  if ($balance == 0) {
+    return;
+  }
+
+  $form['balance'] = array('#value' => t('You have <strong>!value</strong> in store credit.', array('!value' => uc_currency_format($balance))));
+  $form['amount'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Amount'),
+    '#default_value' => $_SESSION['uc_coupon_credit'],
+    '#description' => variable_get('uc_coupon_credit_pane_description', 'Enter the amount you wish to apply to your order.'),
+    '#size' => $size,
+  );
+  $form['apply'] = array(
+    '#type' => 'submit',
+    '#value' => t('Apply to order'),
+  );
+  return $form;
+}
+
+function uc_coupon_credit_block_form_submit($form, &$form_state) {
+  if ($form_state['values']['amount']) {
+    $amount = $form_state['values']['amount'];
+    $credit = uc_coupon_validate_credit($amount);
+    if (!$credit->valid) {
+      unset($_SESSION['uc_coupon_credit']);
+      drupal_set_message($credit->message, 'error');
+    }
+    else {
+      $_SESSION['uc_coupon_credit'] = $credit->amount;
+      drupal_set_message($credit->message);
+    }
+  }
+  else {
+    unset($_SESSION['uc_coupon_credit']);
+  }
+}
+
+/**
  * Implementation of hook_order().
  *
- * Ensure any cart pane coupon is added to the order if the checkout page is skipped
- * (e.g. Paypal Express Checkout, Google Checkout)
+ * - Ensure any cart pane coupon or credit is added to the order if the checkout page is skipped
+ *   (e.g. Paypal Express Checkout, Google Checkout).
+ * - Deduct credit from user's balance when the order is submitted.
+ * - Restore credit to user's balance if the order is cancelled.
  */
 function uc_coupon_order($op, &$arg1, $arg2) {
   if ($op == 'save' && isset($_SESSION['uc_coupon'])) {
@@ -856,6 +1064,57 @@ function uc_coupon_order($op, &$arg1, $a
       _uc_coupon_apply_to_order($arg1->order_id, $coupon);
     }
   }
+
+  if ($op == 'save' && isset($_SESSION['uc_coupon_credit'])) {
+    $credit = uc_coupon_validate_credit($_SESSION['uc_coupon_credit']);
+    if ($credit->valid) {
+      // Adjust the credit amount if greater than the order total minus any coupon discount.
+      $coupondiscount = db_result(db_query("SELECT amount FROM {uc_order_line_items} WHERE order_id = %d AND type = 'coupon'", $arg1->order_id));
+      $total = $arg1->order_total + $coupondiscount;
+      if ($credit->amount > $total) {
+        $credit->amount = $total;
+      }
+      _uc_coupon_apply_to_order($arg1->order_id, $credit);
+    }
+  }
+
+  // Deduct any credit used from the user's store credit balance.
+  if ($op == 'submit') {
+    $creditdiscount = abs(db_result(db_query("SELECT amount FROM {uc_order_line_items} WHERE order_id = %d AND type = 'credit'", $arg1->order_id)));
+    if ($creditdiscount) {
+      $uid = $arg1->uid;
+      $coupon = db_fetch_object(db_query("SELECT * FROM {uc_coupons} WHERE type = 'credit' AND user = %d AND valid_from < %d AND (valid_until = 0 OR valid_until > %d)", $uid, time(), time()));
+      $coupon->value -= $creditdiscount;
+      if ($coupon->value == 0) {
+        $uid = 0;
+        $coupon->name = 'REDEEMED';
+      }
+      db_query("UPDATE {uc_coupons} SET user = %d, value = %f, name = '%s' WHERE cid = %d", $uid, $coupon->value, $coupon->name, $coupon->cid);
+    }
+  }
+
+  // If the order is cancelled, restore any credit that was applied.
+  if ($op == 'update' && $arg2 == 'canceled') {
+    $creditdiscount = abs(db_result(db_query("SELECT amount FROM {uc_order_line_items} WHERE order_id = %d AND type = 'credit'", $arg1->order_id)));
+    if ($creditdiscount) {
+      $uid = $arg1->uid;
+      $coupon = db_fetch_object(db_query("SELECT * FROM {uc_coupons} WHERE type = 'credit' AND user = %d AND valid_from < %d AND (valid_until = 0 OR valid_until > %d)", $uid, time(), time()));
+      if ($coupon) {
+        $coupon->value += $creditdiscount;
+        db_query("UPDATE {uc_coupons} SET value = %f WHERE cid = %d", $coupon->value, $coupon->cid);
+      }
+      else { // The credit coupon was used up and zeroed out, so create a new one.
+        $coupon->name = 'Store credit for user '. $uid;
+        $coupon->code = strtoupper(user_password(12));
+        $coupon->value = $creditdiscount;
+        $coupon->type = 'credit';
+        $coupon->status = 0;
+        $coupon->valid_from = $coupon->valid_until = 0;
+        $coupon->user = $uid;
+        uc_coupon_save($coupon);
+      }
+    }
+  }
 }
 
 /**
@@ -873,6 +1132,14 @@ function uc_coupon_checkout_pane() {
     'weight' => 5,
     'process' => TRUE,
   );
+  $panes[] = array(
+    'id' => 'credit',
+    'callback' => 'uc_checkout_pane_coupon_credit',
+    'title' => t('Store credit'),
+    'desc' => t('Allows shoppers to use store credit during checkout for order discounts.'),
+    'weight' => 5,
+    'process' => TRUE,
+  );
   return $panes;
 }
 
@@ -990,17 +1257,146 @@ function uc_coupon_checkout_apply() {
 }
 
 /**
+ * Checkout Pane callback function.
+ *
+ * Used to display a form in the checkout process so that customers
+ * can enter a store credit discount amount.
+ */
+function uc_checkout_pane_coupon_credit($op, &$arg1, $arg2) {
+  switch ($op) {
+    case 'view':
+      global $user;
+
+      // Only show this pane for logged in users.
+      if ($user->uid == 0) {
+        return;
+      }
+      $balance = uc_coupon_get_total_credit($user->uid);
+
+      // Only show this pane if the user has a positive balance.
+      if ($balance == 0) {
+        return;
+      }
+
+      drupal_add_js(drupal_get_path('module', 'uc_coupon') .'/uc_coupon.js');
+      drupal_add_js(array(
+        'ucURL' => array(
+          'applyCredit' => url('cart/checkout/credit'),
+        ),
+      ), 'setting');
+
+      if (isset($_SESSION['uc_coupon_credit'])) {
+        $amount = $_SESSION['uc_coupon_credit'];
+      }
+      else if (isset($arg1->data['credit'])) {
+        $amount = $_SESSION['uc_coupon_credit'] = $arg1->data['credit'];
+      }
+      else {
+        $amount = '';
+      }
+
+      $credit = uc_coupon_validate_credit($amount);
+      if ($credit->valid) {
+        if (variable_get('uc_coupon_show_in_cart', TRUE)) {
+          // Modify stored subtotal to ignore credit cart item.
+          drupal_add_js('$(function() {
+            if (window.set_line_item) {
+              li_values["subtotal"] += ' . $credit->amount . ';
+            }
+          });', 'inline');
+        }
+
+        drupal_add_js('$(document).ready(function() {
+          if (window.set_line_item) {
+            set_line_item("credit", "'. $credit->title .'", '. -$credit->amount .', ' . _line_item_data('coupon', 'weight') . ');
+          }
+        });', 'inline');
+      }
+
+      $description = t('You have <strong>!value</strong> in store credit.', array('!value' => uc_currency_format($balance))) .'<br />';
+
+      $contents['amount'] = array(
+        '#type' => 'textfield',
+        '#title' => t('Amount'),
+        '#default_value' => $amount,
+        '#description' => variable_get('uc_coupon_credit_pane_description', 'Enter the amount you wish to apply to your order.'),
+        '#size' => 25,
+      );
+      $contents['apply'] = array(
+        '#type' => 'button',
+        '#value' => t('Apply to order'),
+        '#attributes' => array('onclick' => "getCredit(); return false;"),
+        '#suffix' => '<span id="credit-throbber"></span>',
+      );
+      return array('description' => $description, 'contents' => $contents);
+
+    case 'process':
+      if ($arg2['amount']) {
+        $arg1->data['credit'] = $arg2['amount'];
+        $credit = uc_coupon_validate_credit($arg1->data['credit']);
+        if (!$credit->valid) {
+          drupal_set_message($credit->message, 'error');
+          unset($_SESSION['uc_coupon_credit']);
+          unset($arg1->data['credit']);
+          return FALSE;
+        }
+        // Adjust the credit amount considering the order total combined with any coupon discounts.
+        $coupondiscount = db_result(db_query("SELECT amount FROM {uc_order_line_items} WHERE order_id = %d AND type = 'coupon'", $arg1->order_id));
+        $total = $arg1->order_total + $coupondiscount;
+        if ($credit->amount > $total) {
+          $credit->amount = $total;
+          drupal_set_message(t('Discount amount adjusted to match order total.'));
+        }
+        $_SESSION['uc_coupon_credit'] = $credit->amount;
+        _uc_coupon_apply_to_order($arg1->order_id, $credit);
+      }
+      return TRUE;
+
+    case 'settings':
+      $form['uc_coupon_credit_collapse_pane'] = array(
+        '#type' => 'checkbox',
+        '#title' => t('Collapse checkout pane by default.'),
+        '#default_value' => variable_get('uc_coupon_credit_collapse_pane', FALSE),
+      );
+      $form['uc_coupon_credit_pane_description'] = array(
+        '#type' => 'textarea',
+        '#title' => t('Checkout pane message'),
+        '#default_value' => variable_get('uc_coupon_credit_pane_description', 'Enter the amount you wish to apply to your order.'),
+      );
+      return $form;
+  }
+}
+
+/**
+ * Checkout pane AJAX callback.
+ **/
+function uc_coupon_credit_checkout_apply() {
+  $credit = uc_coupon_validate_credit($_POST['amount']);
+  if ($credit->valid) {
+    $_SESSION['uc_coupon_credit'] = $credit->amount;
+  }
+  else {
+    unset($_SESSION['uc_coupon_credit']);
+  }
+
+  drupal_set_header("Content-Type: text/javascript; charset=utf-8");
+  print drupal_to_js($credit);
+  exit();
+}
+
+/**
  * Create or update the coupon line item in an order.
  */
 function _uc_coupon_apply_to_order($order_id, $coupon) {
-  $lid = db_result(db_query("SELECT line_item_id FROM {uc_order_line_items} WHERE order_id = %d AND type = 'coupon'", $order_id));
+  ($coupon->type == 'credit') ? $type = 'credit' : $type = 'coupon';
+  $lid = db_result(db_query("SELECT line_item_id FROM {uc_order_line_items} WHERE order_id = %d AND type = '%s'", $order_id, $type));
   if ($lid) {
     db_query("UPDATE {uc_coupons_orders} SET cid = %d, code = '%s', value = %f WHERE oid = %d", $coupon->cid, $coupon->code, $coupon->amount, $order_id);
     uc_order_update_line_item($lid, $coupon->title, -$coupon->amount);
   }
   else {
     db_query("INSERT INTO {uc_coupons_orders} (cid, oid, code, value) VALUES (%d, %d, '%s', %f)", $coupon->cid, $order_id, $coupon->code, $coupon->amount);
-    uc_order_line_item_add($order_id, 'coupon', $coupon->title, -$coupon->amount);
+    uc_order_line_item_add($order_id, $type, $coupon->title, -$coupon->amount);
   }
 }
 
@@ -1012,7 +1408,16 @@ function uc_coupon_line_item() {
     'id' => 'coupon',
     'title' => t('Coupon discount'),
     'tax_adjustment' => 'uc_coupon_tax_adjustment',
-    'weight' => 0,
+    'weight' => 1,
+    'default' => FALSE,
+    'stored' => TRUE,
+    'add_list' => TRUE,
+    'calculated' => TRUE,
+  );
+  $items[] = array(
+    'id' => 'credit',
+    'title' => t('Store credit'),
+    'weight' => 1,
     'default' => FALSE,
     'stored' => TRUE,
     'add_list' => TRUE,
@@ -1096,7 +1501,7 @@ function uc_coupon_token_list($type = 'a
  */
 function uc_coupon_token_values($type, $object = NULL) {
   $values = array();
- 
+
   switch ($type) {
     case 'order':
       $values['order-coupon-code'] = isset($object->data['coupon']) ? $object->data['coupon'] : '';
@@ -1125,17 +1530,34 @@ function uc_coupon_token_values($type, $
  * This will be converted to a real line item during checkout.
  */
 function uc_coupon_uc_cart_alter(&$items, $ignore = NULL) {
-  if (variable_get('uc_coupon_show_in_cart', TRUE) && isset($_SESSION['uc_coupon'])) {
-    $coupon = uc_coupon_validate($_SESSION['uc_coupon']);
-    if ($coupon->valid) {
-      $items[] = (object) array(
-        'module' => 'uc_coupon',
-        'coupon' => $coupon,
-        'title' => $coupon->title,
-        'nid' => 0,
-        'qty' => 1,
-        'price' => uc_coupon_price(-$coupon->amount, 'altered'),
-      );
+  if (variable_get('uc_coupon_show_in_cart', TRUE)) {
+    if (isset($_SESSION['uc_coupon'])) {
+      $coupon = uc_coupon_validate($_SESSION['uc_coupon']);
+      if ($coupon->valid) {
+        $items[] = (object) array(
+          'module' => 'uc_coupon',
+          'coupon' => $coupon,
+          'title' => $coupon->title,
+          'nid' => 0,
+          'qty' => 1,
+          'price' => uc_coupon_price(-$coupon->amount, 'altered'),
+        );
+      }
+    }
+    if (isset($_SESSION['uc_coupon_credit'])) {
+      $credit = uc_coupon_validate_credit($_SESSION['uc_coupon_credit']);
+      if ($credit->valid) {
+        $_SESSION['uc_coupon_credit'] = $credit->amount;
+        $items[] = (object) array(
+          'module' => 'uc_coupon',
+          'coupon' => $credit,
+          'title' => $credit->title,
+          'nid' => 0,
+          'qty' => 1,
+          'price' => -$credit->amount,
+          'type' => 'credit',
+        );
+      }
     }
   }
 }
@@ -1155,11 +1577,11 @@ function uc_coupon_cart_display($item) {
 /**
  * Implementation of hook_form_FORM_ID_alter() for uc_cart_checkout_form().
  *
- * Remove the coupon cart item, as it will be handled as a line item during checkout.
- * Collapse coupon checkout pane, if configured to do so.
+ * Remove the coupon and/or credit cart items, as these will be handled as line items during checkout.
+ * Collapse coupon and/or credit checkout pane, if configured to do so.
  */
 function uc_coupon_form_uc_cart_checkout_form_alter(&$form, $form_state) {
-  if (variable_get('uc_coupon_show_in_cart', TRUE) && isset($_SESSION['uc_coupon'])) {
+  if (variable_get('uc_coupon_show_in_cart', TRUE) && (isset($_SESSION['uc_coupon']) || isset($_SESSION['uc_coupon_credit']))) {
     $items = unserialize($form['cart_contents']['#value']);
     foreach ($items as $key => $item) {
       if ($item->module == 'uc_coupon') {
@@ -1172,6 +1594,9 @@ function uc_coupon_form_uc_cart_checkout
   if (variable_get('uc_coupon_collapse_pane', FALSE) && isset($form['panes']['coupon'])) {
     $form['panes']['coupon']['#collapsed'] = TRUE;
   }
+  if (variable_get('uc_coupon_credit_collapse_pane', FALSE) && isset($form['panes']['credit'])) {
+    $form['panes']['credit']['#collapsed'] = TRUE;
+  }
 }
 
 /**
@@ -1181,6 +1606,7 @@ function uc_coupon_form_uc_cart_checkout
  */
 function uc_coupon_uc_checkout_complete($order, $account) {
   unset($_SESSION['uc_coupon']);
+  unset($_SESSION['uc_coupon_credit']);
 }
 
 /**
@@ -1198,6 +1624,8 @@ function template_preprocess_uc_coupon_c
     $variables['display_name'] = substr($variables['display_name'], 0, $n-1);
   }
 
+  ($coupon->type == 'credit') ? $variables['credit'] = TRUE : $variables['credit'] = FALSE;
+
   if ($coupon->valid_until) {
     $variables['not_yet_valid'] = $coupon->valid_from > time();
     $variables['valid_from'] = format_date($coupon->valid_from, 'custom', variable_get('uc_date_format_default', 'm/d/Y'), 0);
@@ -1219,15 +1647,15 @@ function template_preprocess_uc_coupon_c
       $variables['include'][] = node_get_types('name', $type);
     }
   }
-  
+
   if (isset($coupon->data['products'])) {
     $key = isset($coupon->data['negate_products']) ? 'exclude' : 'include';
     foreach ($coupon->data['products'] as $nid) {
       $node = node_load($nid);
-      $variables[$key][] = $node->title; 
+      $variables[$key][] = $node->title;
     }
   }
-  
+
   if (isset($coupon->data['skus'])) {
     foreach($coupon->data['skus'] as $sku) {
       $variables['include'][] = t('SKU') . ' '. $sku;
@@ -1241,7 +1669,7 @@ function template_preprocess_uc_coupon_c
       $variables[$key][] = $term->name;
     }
   }
-  
+
   // Merge in global tokens.
   $tokens = token_get_values();
   foreach ($tokens->tokens as $key => $token) {
diff -urpN uc_coupon-1.x-dev-20110215/uc_coupon.user.inc uc_coupon-1.x-dev--store-credit/uc_coupon.user.inc
--- uc_coupon-1.x-dev-20110215/uc_coupon.user.inc	1969-12-31 19:00:00.000000000 -0500
+++ uc_coupon-1.x-dev--store-credit/uc_coupon.user.inc	2011-02-17 02:57:38.000000000 -0500
@@ -0,0 +1,89 @@
+<?php
+
+/**
+ * Implementation of hook_user().
+ */
+function uc_coupon_user($op, $edit, &$account, $category = NULL) {
+  global $user;
+  switch ($op) {
+    case 'view':
+      $balance = uc_coupon_get_total_credit($account->uid);
+      if ( ($account->uid == $user->uid || user_access('manage store coupons')) && ($balance) ) {
+        $account->content['store_credit'] = array(
+          '#type' => 'user_profile_category',
+          '#weight' => -4,
+          '#title' => t('Store Credit'),
+        );
+        $account->content['store_credit']['total'] = array(
+          '#type' => 'user_profile_item',
+          '#value' => t('You have <strong>!value</strong> in store credit, which can be applied to your order at checkout.', array('!value' => uc_currency_format($balance))),
+          '#attributes' => array('class' => 'store-credit-message'),
+        );
+      }
+      break;
+  }
+}
+
+/**
+ * Form for redeeming a store credit code, available on a special page and as a block.
+ */
+function uc_coupon_redeem_credit_form($form_state, $size = 25) {
+  $form['coupon_code'] = array(
+     '#type' => 'textfield',
+     '#title' => t('Gift certificate code'),
+     '#description' => t('If you have a store credit or gift certificate code, you can redeem it here. Its value will be added to your store credit balance, which can be applied to your order at checkout.'),
+     '#size' => $size,
+  );
+  $form['op'] = array('#type' => 'submit',
+     '#value' => t('Submit'),
+     '#weight' => 20,
+  );
+  return $form;
+}
+
+function uc_coupon_redeem_credit_form_submit($form, &$form_state) {
+  global $user;
+
+  if ($form_state['values']['coupon_code']) {
+    $code = trim(strtoupper($form_state['values']['coupon_code']));
+    $coupon = uc_coupon_find($code);
+
+    if (!$coupon) {
+      drupal_set_message(t('That code is invalid, has already been redeemed, or has expired.'), 'warning');
+      return;
+    }
+
+    if ($coupon->type !== 'credit') {
+      drupal_set_message(t('This form is for redeeming store credit codes only. Other types of discount coupons can be redeemed at checkout.'), 'warning');
+      return;
+    }
+
+    $creditcoupon = db_fetch_object(db_query("SELECT * FROM {uc_coupons} WHERE type = 'credit' AND user = %d AND valid_from < %d AND (valid_until = 0 OR valid_until > %d)", $user->uid, time(), time()));
+    $amount = $coupon->value;
+    if ($creditcoupon) {
+      $coupon->value += $creditcoupon->value;
+      db_query("UPDATE {uc_coupons} SET user = 0, value = 0, name = 'REDEEMED' WHERE cid = %d", $creditcoupon->cid);
+    }
+
+    $coupon->name = 'Store credit for user '. $user->uid;
+    db_query("UPDATE {uc_coupons} SET user = %d, value = %f, name = '%s', status = 0 WHERE cid = %d", $user->uid, $coupon->value, $coupon->name, $coupon->cid);
+    drupal_set_message('Code successfully redeemed! '. uc_currency_format($amount) .' in store credit has been added to your account.');
+  }
+
+  return;
+}
+
+/**
+ * Helper function to retrieve a user's total store credit.
+ * All of a user's balance is stored on a single 'credit' coupon.
+ */
+function uc_coupon_get_total_credit($uid) {
+  $total = 0;
+  if ($uid > 0) {
+    $coupon = db_fetch_object(db_query("SELECT * FROM {uc_coupons} WHERE type = 'credit' AND user = %d AND valid_from < %d AND (valid_until = 0 OR valid_until > %d)", $uid, time(), time()));
+    if ($coupon) {
+      $total = $coupon->value;
+    }
+  }
+  return $total;
+}
