diff --git a/payment/uc_payment/uc_payment.module b/payment/uc_payment/uc_payment.module
index 2527560..adddc12 100644
--- a/payment/uc_payment/uc_payment.module
+++ b/payment/uc_payment/uc_payment.module
@@ -235,6 +235,50 @@ function uc_payment_uc_order_state() {
 }
 
 /**
+ * Implements hook_uc_payment_method().
+ */
+function uc_payment_uc_payment_method() {
+  $methods['free_order'] = array(
+    'name' => t('Free order'),
+    'title' => t('No payment required'),
+    'desc' => t('Allow customers with !zero order totals to checkout without paying.', array('!zero' => uc_currency_format(0))),
+    'callback' => 'uc_payment_method_free_order',
+    'checkout' => TRUE,
+    'no_gateway' => TRUE,
+    'weight' => 0,
+  );
+
+  return $methods;
+}
+
+/**
+ * Payment method callback for free orders.
+ *
+ * @see uc_payment_uc_payment_method()
+ */
+function uc_payment_method_free_order($op, &$order) {
+  switch ($op) {
+    case 'cart-details':
+      return array(
+        '#markup' => t('Continue with checkout to complete your order.'),
+      );
+
+    case 'order-valid':
+      return $order->order_total < 0.01;
+
+    case 'order-submit':
+      if ($order->order_total >= 0.01) {
+        return array(array(
+          'pass' => FALSE,
+          'message' => t('We cannot process your order without payment.'),
+        ));
+      }
+
+      uc_payment_enter($order->order_id, 'free_order', 0, 0, NULL, t('Checkout completed for a free order.'));
+  }
+}
+
+/**
  * Returns a formatted list of line items for an order total preview.
  *
  * @param $return
diff --git a/payment/uc_payment/uc_payment_checkout_pane.inc b/payment/uc_payment/uc_payment_checkout_pane.inc
index 35a280d..d123a19 100644
--- a/payment/uc_payment/uc_payment_checkout_pane.inc
+++ b/payment/uc_payment/uc_payment_checkout_pane.inc
@@ -31,6 +31,13 @@ function uc_checkout_pane_payment($op, &$order, $form = NULL, &$form_state = NUL
 
       $options = array();
       foreach (_uc_payment_method_list() as $id => $method) {
+        // If the 'order-valid' callback returns FALSE, skip this method.
+        $func = $method['callback'];
+        if (function_exists($func) && $func('order-valid', $order) === FALSE) {
+          continue;
+        }
+
+        // If Rules returns FALSE, skip this method.
         $set = rules_config_load('uc_payment_method_' . $method['id']);
         if ($set && !$set->execute($order)) {
           continue;
