Free Order Payment Method

Contrib type: 
Module
Status: 
Complete and working
Moderation: 
Full approval

Downloads

Compatibility: 
Ubercart 1.x

This module works in conjunction with the payment checkout pane using the order total preview to present a "Free order" payment method to customers when their order total drops to $0.00 or less. By default, this method will be hidden, and server side verification of an order total will be used to prevent customers from gaming the system. So, while I have not tested it with either system in the contrib directory, when a discount or coupon gets added to an order that would make the order free, the module will display and select the free payment method. The others will be hidden, and you should be able to continue on through checkout like normal. Some folks have expressed a desire to remove the payment pane altogether when an order is free, but I don't believe this is the best way to go. If you think it must be done, feel free to tweak uc_free_order.js to hide the entire pane instead of just the other payment methods. Simply upload and enable this module to be up and running. There is a single permission available so you can test the way the payment method displays on your site. It includes a link that lets you add or remove a $2,000 test discount to the order so you can make sure it works properly. Of course, it will stop you if you try to check out, since the discount is only recognized on the checkout form. For further testing, you can simply create a product worth $0. Many thanks to Warner Bros. Records for sponsoring the core improvements that made a module like this possible.

Module support:

Please post support requests, bug reports, and patches to this module's issue tracker on drupal.org.
Related threads: 
storbaek's picture
Offline
Joined: 01/26/2008
Juice: 36
Some issues

Hi,

I've been wanting a module similar to this, so thanks for posting this to Ubercart! I have tested the module and have some issues. It seem to be working pretty well except for a few points:

1) If completing the order, the order status stays on Pending and not Completed with the result that the user cannot download any files. Can it go directly to completed instead of pending, as no financial transactions are done?

2) Remove billing address. It's quite annoying and does not make sense for a 0 dollar purchase.

3) Remove or change "thank you for shopping" after completing an $0 dollar purchase. Again, somewhat strange when it's a free product.

Thanks again for an otherwise great module!

Dan

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15459
Re: Some issues

Hey Dan, thanks for the feedback. In answer to your queries, point 1 is actually just an issue where you would use a custom workflow configuration. You can set it up on checkout to see if the order total is <= $0 then update the order status. Point 2 could make a good additional option for the module, but it won't make sense for everyone. Perhaps better conditional display of checkout panes can work its way into Uber 2.0. And for point 3, I imagine you can add some conditional text to the checkout completion messages to get them to display properly.

Glad you found the module useful!

karc's picture
Offline
Joined: 05/28/2008
Juice: 72
Can I easily add this as an option to a product?

I mean, that I would have two options (buttons) for a ordering at each product.

1. Normal Order + quantity
2. Oder 1 free sample

Is it easy to reconfigurate your module for this purpose?

At the moment i´m thinking about using ubercart for my shop, which will be redesigned soon. And this would be something I would really need (or better the guys, that will do the job for me).

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15459
Re: Can I easily add this as an option to a product?

You could use an attribute to possibly do this that would deduct the price of the product for the free sample option. The free order module will pick up on the $0 order total and make that payment method available.

richygecko's picture
Offline
Joined: 02/22/2008
Juice: 32
WORKFLOW_NG "payment_entered" Event?

WORKFLOW_NG "payment_entered" Event?

Just noticed that orders that were free were not having their status updated with workflow_ng. That is the payment_entered event isn't being fired.

Any Suggestions?

UPDATE:

I think i figured this one out. Please correct me if I am wrong:

in uc_free_order.module i added the following to the end of the file

<?php
/**
* Implementation of hook_order().
*/
function uc_free_order_order($op, &$arg1) {
  switch (
$op) {
    case
'submit':
      if (
module_exists('workflow_ng')) {
       
workflow_ng_invoke_event('payment_entered', uc_order_load($arg1->order_id));
      }
      break;
  }
}
?>

It seems to work fine.

--Nathan

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15459
Commented in this thread.

Commented in this thread.

budda@drupal.org's picture
Offline
Joined: 09/02/2008
Juice: 16
Billing address pane hiding

For item 2 the following change to the module appears to work:

<?php
function uc_free_order_form_alter($form_id, &$form) {
  global
$user;

 

// Alter the checkout form to prepare it for our special JS.
 
if ($form_id == 'uc_cart_checkout_form' && isset($form['panes']['payment'])) {
    if (
user_access('test free order')) {
     
drupal_set_message('Test free order: <span style="cursor: pointer;" onclick="alert(\'Discount added.\'); set_line_item(\'fo_discount\', \'Test Discount\', -2000, 0);">Add discount</span> | <span style="cursor: pointer;" onclick="alert(\'Discount removed.\'); remove_line_item(\'fo_discount\');">Remove discount</span>');
    }

   

drupal_add_js(drupal_get_path('module', 'uc_free_order') .'/uc_free_order.js');

   

$set = FALSE;
    foreach (
array_keys($form['panes']['payment']['payment_method']['#options']) as $key) {
      if (
$key !== 'free_order' && !$set) {
       
drupal_add_js("uc_free_order_ixis_next_method = '". $key ."';", 'inline');
       
$set = TRUE;
      }
    }
   
   
$order = uc_order_load($_SESSION['cart_order']);
    if (
$order->payment_method == 'free_order') {
      if (
$order->order_total == 0) {
        unset(
$form['panes']['billing']);
      }
    }
   
  }
  elseif (
$form_id == 'uc_cart_checkout_review_form') {
   
$order = uc_order_load($_SESSION['cart_order']);

    if (

$order->payment_method == 'free_order') {
      if (
$order->order_total >= .01) {
       
drupal_set_message(t('We cannot process your order without payment.'), 'error');
       
drupal_goto('cart/checkout');
      }
    }
  }
}
?>

Basically if payment method is free, and obviously value = 0 then unset the billing address pane.

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15459
Re: Billing address pane hiding

The problem w/ unsetting it is going to be that if a shipping or tax gets applied to the order where payment becomes required, you're gonna be stuck without a billing info pane. I've used a show/hide solution with JS on a client's site that seemed to work well in the past... it just takes a little bit of JS programming instead of PHP.

budda@drupal.org's picture
Offline
Joined: 09/02/2008
Juice: 16
Re: Re: Billing address pane hiding

If you just show/hide using JavaScript wont the Drupal FAPI still be expecting the required fields to be completed, even if they are not shown? How did you get around that?

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15459
Re: Re: Re: Billing address pane hiding

Ahh, you are correct. I forgot the missing link. If I hid it, I guess I did something similar to what you did, but instead of unsetting I just turned off the #required field for the address fields.

mikeryan's picture
Offline
Joined: 09/12/2008
Juice: 27
D6/Ubercart 2?

Is there a version of uc_free_order upgraded to Drupal 6 and Ubercart 2.x?

Thanks.

budda@drupal.org's picture
Offline
Joined: 09/02/2008
Juice: 16
Fix for broken uc_free_order.js

The JavaScript contained in the tar file didn't work when some items in the basket were of a value >= 0.01
A variable referenced in the uc_free_order.js file was never set uc_free_order_next_method, causing the AJAX call back to fail. How this ever worked for others I do not know?!

Without this variable being populated it caused the payment details pane to be filled with a copy of the checkout page - which re cursed until FireFox stopped it. This was due to the AJAX call get_payment_details('cart/checkout/payment_details/' + uc_free_order_next_method); which never had a payment method on the end so failed.

Here's my amended function with some new JQuery to find the first available payment method and select that.

/**
* Checks the current total and updates the available/selected payment methods
* accordingly.
*/
function free_order_check_total(total) {
  total = parseFloat(total);

  if (total >= .01) {
    // Disable the free order option and select the first available method.
    if (using_free_order == 0) {
      // Show the other payment method radios.
      $("#payment-pane .form-radios input:radio").removeAttr('disabled').parent().show(0);

      // Hide the free order radio.
      $("input:radio[@value=free_order]").attr('disabled', 'disabled').parent().hide(0);

      // Find first available payment option
      var firstmethod = $(':radio[name="panes[payment][payment_method]"]:first');
      uc_free_order_next_method = firstmethod.val();

      // Select the first payment method.
      $("input:radio[@value=" + uc_free_order_next_method + "]").attr('checked', 'checked');

      // Refresh the payment details section.
      get_payment_details('cart/checkout/payment_details/' + uc_free_order_next_method);
    } else {
      using_free_order = 0;
    }
  }
  else {
    // Disable the CC option and select the gift card option.
    if (using_free_order != 1) {
      // Hide the fallback payment method radio.
      $("#payment-pane .form-radios input:radio").attr('disabled', 'disabled').parent().hide(0);
     
      // Show and select the gift card.
      $("input:radio[@value=free_order]").removeAttr('disabled').attr('checked', 'checked').parent().show(0);

      // Refresh the payment details section.
      get_payment_details('cart/checkout/payment_details/free_order');

      using_free_order = 1;
    }
  }
}

I've attached the full file too, as I'm not sure what the process is for supplying patches/updates to code hosted on ubercart.org ?

AttachmentSize
uc_free_order.js_.txt 1.96 KB
budda@drupal.org's picture
Offline
Joined: 09/02/2008
Juice: 16
Re: Re: Re: Re: Billing address pane hiding

Any chance you could dig out where you achieved this Ryan?

The problem I'm having is detecting if the order/cart total is £0 or not from the hook_form_alter() stage. Before the visitor gets to the review form there is no cart order, no ID, so how do you get the cart contents total and determine whether to hide the billing or not?

I'm happy to work on the module and submit back, just need a push in the right direction.

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15459
Re: Re: Re: Re: Re: Billing address pane hiding

You can just use the function uc_cart_get_contents() and loop through the items on the cart to see if they have prices. You have to calculate the total of the cart from there.

budda@drupal.org's picture
Offline
Joined: 09/02/2008
Juice: 16
Re: Re: Re: Re: Re: Re: Billing address pane hiding

Perfect, thanks Ryan. I now have a working free_order module that hides the billing pane when its not necessary!
Is it worth submitting the module back to replace the existing/old one?

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15459
Re: Re: Re: Re: Re: Re: Re: Billing address pane hiding

I need to get this module up on drupal.org. You can wait till then and post a patch there or just attach a patch here and I can migrate it when I migrate this module.

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15459
Module moved to CVS at drupal.org

Alrighty, I just took the time to get this into CVS on drupal.org. Check it out and feel free to use the issue tracker at http://drupal.org/project/uc_free_order.

nextgate's picture
Offline
Joined: 11/26/2008
Juice: 7
Question on: 1) If

Question on:
1) If completing the order, the order status stays on Pending and not Completed with the result that the user cannot download any files. Can it go directly to completed instead of pending, as no financial transactions are done?

Got the same problem and tried to solv it with Workflow-ng.
Rule:
Invoked on event: A payment gets entered for an order

Condition: Payment method: Free order - payment not necessary.
Action: Update the order status : Payment received

I never worked with Workflow-ng, but I think this is correct, the only problem is - it is NOT working Sad ???
What did I do wrong?

Sunsetco's picture
Offline
Joined: 12/04/2008
Juice: 18
Payment Received not fired

nextgate, you are on the right track here. I figured out the same issue before finding your post.

I checked the Workflows set up under Workflow_ng. Some items do not fire until "Payment Received" is triggered on the order. Free Order is not sending back that a payment was received, so some things don't get processed.

In my case, I am using uc_node_access. I am distributing some coupons to certain users for free access to certain nodes. Everything works fine, but they do not get access until I go into the Orders section and enter a payment ($0.00) for that order. As soon as I do, the workflow is fired and they get the access they "purchased" with the coupon.

Can Free Order return a "Payment Received" instead of just "Order Completed"? If so, then it should solve this problem for modules that are waiting for Payments before continuing.

Meanwhile, every order using Free Order needs to have a manual payment of ZERO entered so Ubercart thinks they got paid...

Ryan's picture
Offline
Joined: 08/07/2007
Juice: 15459
Re: Payment Received not fired

You don't have to do it this way... You can automate the process by creating a new Workflow-ng configuration that checks the payment method upon checkout and updates the order to payment received or even completed when it's done. This is the reason we chose Workflow-ng for handling things like this. It's totally configurable by the end user and extensible by contributed modules.

Sunsetco's picture
Offline
Joined: 12/04/2008
Juice: 18
Thank you Ryan!

Thanks for all your hard work and quick response.

I think the solution was too simple! Sometimes we always try to look for the hard way to do stuff when a simple solution is sitting right there...

Thanks again!

eoneillPPH's picture
Offline
Joined: 12/22/2008
Juice: 55
D6 port

bumping: when available for D6? Have used it on a D5 site, and it's great. Now another client, IN A BIG HURRY! and on a D6 installation. Any timeframe in mind???

Thanks, Ryan

rkeppner's picture
Offline
Joined: 04/03/2009
Juice: 21
D6 UC2 version working, but not with file downloads

There were enough patches at http://drupal.org/node/336138 for me to get uc_free_order.module working for Drupal 6.10/Ubercart 6.x-2.0-beta5. However, I can't seem to get file downloads to work. I tried setting up a conditional action to set the order status to payment received, but that didn't seem to make a difference. Any ideas?

vessenes's picture
Offline
Joined: 04/02/2009
Juice: 15
Similary problem

I am experiencing the same problem. Anybody?

rhyanod's picture
Offline
Joined: 04/22/2009
Juice: 4
Download free items

Hello,
I didnt get the free order to working so I tried another way.

I created a conditional action in ubercart for the use of free items/downloads in the shop (Drupal 6 and Ubercart 2).
This set should only registered users allow to download free items from the shop. As work around I am using the other payment method because only then the order gets completed and the order status will be triggered.

This is intended so because I want to show the free items only to registered users. So only they can see them in the shop and buy them for free.

This is how it looks:

Title: Update Order Status for free items to completed
Trigger: Order status gets updated

Conditions connected with "AND" operator
#1 Check order totals: less or euqal 0
#2 Check Payment mehtod: check or money order
#3 Check role of user: registered

Actions:
#1 Update order status. update original order to completed
#2 Renew files on an order: original order

I also added into the payment pack module file the following code:
$methods[] = array(
'id' => 'free',
'name' => t('Free Order'),
'title' => t('Free Order'),
'desc' => t('A free order payment method type.'),
'callback' => 'uc_payment_method_other',
'weight' => 11,
'checkout' => FALSE,
'no_gateway' => TRUE,
);

So you get a new button for your customer to select free order.

torgosPizza's picture
Offline
Bug FinderEarly adopter... addicted to alphas.Getting busy with the Ubercode.
Joined: 08/14/2007
Juice: 4111
Re: D6 UC2 version working, but not with file downloads

#23 and #24: Did you try patching with the CA locking patch? Let me know if you don't know what I'm talking about.

--
Help directly fund development: Donate via PayPal!

rhyanod's picture
Offline
Joined: 04/22/2009
Juice: 4
@ #23 File Download

see my post #25.
I had to add the renew file option as well. so that the paid files will get accessible for the user.

rhyanod

xcf33's picture
Offline
Joined: 04/24/2009
Juice: 2
Easier

Reply to #25

Thanks for the information, but it didn't work for me.

I'm using drupal 6.8 with UC 2.0 Beta-5

I didn't get your way to work but here was my solution, I didn't add the user role check because I wanted anonymous users to be able to checkout product and register at the same time.

Condition action

Trigger: Order status gets updated
Conditions connected with "AND" operator

#1 Check order totals: less or equal to $1

Actions:

#1 Update order status to: original order, payment received
#2 Renew files on an order: original order

dustwolff's picture
Offline
Joined: 07/25/2009
Juice: 2
Re: Easier

Don't know if this has already been posted, tried using the solutions above, but there was a little more than met the eye, such as the cart id not being available on uc_cart_checkout_form and the return of the uc_cart_get_contents being an object rather than a typical array. Hopefully this will be a simple copy/paste solution to removing billing pane if cart total is $0 in 6.x without modifying the js:

In the .module's form alter, right underneath the drupal_add_js

//push contents of cart to object var
$objects = uc_cart_get_contents();

/* loop through object without having to cast $objects or use of get_object_vars,
the following foreach doesn't get the total price of the cart, but we don't need the total value, we just need to check to see if ANY item in the cart has a price of $0.01 or greater */
foreach($objects as $obj) {
$price += $obj->price;
}
$total_price = $price;
if ($price < 0.01){
unset($form['panes']['billing']);
}

majnoona@drupal.org's picture
Offline
Joined: 07/02/2009
Juice: 90
Thanks!

I've been banging my head over the lack of cart contents on the checkout page -- I assumed that the code above would work and that I had a bug somewhere. I appreciate that you posted an update!

I've taken it a bit further, customizing it to be a "registration" page instead of a "purchase page"
hope this helps someone:

<?php
if ($form_id == 'uc_cart_checkout_form'){
       
$objects = uc_cart_get_contents();
        foreach(
$objects as $obj) {
               
$price += $obj->price;
        }
       
$total_price = $price;
        if (
$total_price <= .01) {
               
drupal_add_js(drupal_get_path('module', 'uc_free_order') .'/uc_free_order.js');
               
drupal_set_title('Registration');
                unset (
$form['panes']['comments']);
               
$form['panes']['cart'] = FALSE;
               
$form['panes']['customer']['#title']= 'Registrant information';
               
$form['panes']['customer']['#description']= 'Registration details will be sent to the following address:';
//billing pane
               
$form['panes']['billing']['#title'] = "Confirm contact details";
               
$form['panes']['billing']['#description'] = 'Select the contact information from your profiles or <a
href="/?q=user/'
. $user->uid . '/edit/Registration&destination=cart/checkout">edit</a> the values';

               

$form['panes']['payment']['#collapsed'] = 1;

               

$form['panes']['payment']['#collapsed'] = 1;
               
$form['panes']['payment']['payment_method']['#default_value']='free_order';

        }
}

?>
N8thh's picture
Offline
Joined: 07/01/2009
Juice: 9
We cannot process your order without payment error

Hi all, please help. I am using this module so that certain customers with the correct coupon code can recieve their entire cart for free, however once the code is entered and it shows the total as £0, the free order button comes up but when i continue to review order, i get the error "we cannot process your order without payment".
Any ideas?

Thanks in advance

lestorx's picture
Offline
Joined: 03/15/2010
Juice: 3
Issue with sales tax

First, let me say that this module is exactly what I needed.

Here is my situation:
My client has to charge sales tax to customers in his state.

So, I have a Conditional Action to charge tax on orders from his home state.
He partnered with a third party company to distribute discounts to their targeted customers. (some free)
The CA was still applying tax to them.
I created another CA to disable tax on $0 orders.

So far so good.

Then we were having issues with still having the credit card pane on free orders.

Free Order Payment Method to the rescue. It seemed to answer the problem, but...

If an order for a free item originates from home state it gives the "we cannot process your order without payment" message. It does not give this error on out of state orders.

Well, the coupons were distributed and orders coming in. He does not charge shipping on these products, so I changed them to "Not Shippable" and set "apply tax to shippable items only" but we need to be able to charge tax on a non-free order of those products.

Please help.
Thank you

VictoriaB's picture
Offline
Joined: 03/15/2010
Juice: 20
Worked for me!

dustwolff,

Great work. Your code worked for me. Saved me hours of time!

Thanks again!

VictoriaB's picture
Offline
Joined: 03/15/2010
Juice: 20
Remove Billing Info Pane on "cart/checkout/review"

Now that I can remove the "Billing Info" from the "cart/checkout" page a blank "Billing Info" entry still appears on the "cart/checkout/review" page. Any one know how to remove that if the order is free?

klippklapp's picture
Offline
Joined: 05/17/2010
Juice: 28
Payment Method does not change

Hi everybody,

I´m using D6 with ubercart 6.x-2.2 and i am using the store coupon modul on the checkout page. If i use the store coupon for 100% discount the total order price changes to 0,00 EUR but the payment method does not change to free order and this couses a problem with paypal. I´ve attached a jpg file to show how it looks like right now.

have a good day!

AttachmentSize
ubercart.jpg 75.57 KB
s sachs's picture
Offline
Joined: 12/06/2011
Juice: 12
Realize this post is old, but replying to help others like me

I realize this query is 4 years old, but I'm replying because I just spent some time myself figuring this out and want to save others the trouble.

To edit the form on the cart/checkout/review page, you can use theme_uc_cart_checkout_review() from your template.php to make whatever changes you want.

For example, inD6, the following in template.php would remove the "Billing information" pane from the form:

function myTheme_uc_cart_checkout_review($panes, $form) {
  unset($panes['Billing information']);

  drupal_add_css(drupal_get_path('module', 'uc_cart') . '/uc_cart.css');

  $output = check_markup(variable_get('uc_checkout_review_instructions', uc_get_message('review_instructions')), variable_get('uc_checkout_review_instructions_format', FILTER_FORMAT_DEFAULT), FALSE)
           . '<table class="order-review-table">';

  foreach ($panes as $title => $data) {
    $output .= '<tr class="pane-title-row">';
    $output .= '<td colspan="2">' . $title . '</td>';
    $output .= '</tr>';
    if (is_array($data)) {
      foreach ($data as $row) {
        if (is_array($row)) {
          if (isset($row['border'])) {
            $border = ' class="row-border-' . $row['border'] . '"';
          }
          else {
            $border = '';
          }
          $output .= '<tr valign="top"' . $border . '>';
          $output .= '<td class="title-col">' . $row['title'] . ':</td>';
          $output .= '<td class="data-col">' . $row['data'] . '</td>';
          $output .= '</tr>';
        }
        else {
          $output .= '<tr valign="top"><td colspan="2">' . $row . '</td></tr>';
        }
      }
    }
    else {
      $output .= '<tr valign="top"><td colspan="2">' . $data . '</td></tr>';
    }
  }

  $output .= '<tr class="review-button-row">';
  $output .= '<td colspan="2">' . $form . '</td>';
  $output .= '</tr>';

  $output .= '</table>';

  return $output;
}

Hope this is helpful to someone, it would've saved me a lot of time!