I have posted a long winded opinion (http://www.ubercart.org/forum/ideas_and_suggestions/10133/uc_recurring_t...) on what I currently dislike about ubercart's implementation of recurring payments. At the core of it I think that renewing a subscription product whether it is done automatically via recurring fees or manually by placing a new order should be treated as a new order in the system.

Making the renewal of recurring fees a new order would allow the system to automatically handle things like extending a uc_role and sending out invoices rather then having to re-implement these actions for both a new purchase and the renewals (since they are exactly the same).

I have attached a patch that does just this and modifies the test_gateway module as an example of how this would work. These changes do not effect any payment gateways that don't do recurring payment but for gateways that do they may require a small change (e.g. uc_authorisenet will probably just need the 1 line from [1] below).

The general process is as follows:

  1. if a gateway supports recurring fees it will provide an extra callback in the hook_payment_gateway() for
    'recurring_fee' (this is the function that is called when a fee is setup, ie: on checkout if the person selects that payment gateway). This change allows for multiple gateway options when setting up recurring fees (e.g. customers can get the option of both credit card and paypal subscriptions if you want to give them this - at the moment you can only have one gateway for recurring payments)

    To update a recurring gateway like uc_authorizenet to work with the changes I am proposing it would just need the one extra line like shown in the test_gateway (although it would need extra change to incorporate the recurring fees being actual new orders).

    function test_gateway_payment_gateway() {
      $gateways[] = array(
        'id' => 'test_gateway',
        'title' => t('Test Gateway'),
        'description' => t('Process credit card payments through the Test Gateway.'),
        'credit' => 'test_gateway_charge',
        'recurring_fee' => 'test_gateway_recurring_fee',
      );
    
      return $gateways;
    }
    
  2. if ubercart is responsible for handling the actual renewal of recurring fees then the gateway should implement the hook_recurring_renew() which is called by the cron function in uc_recurring_cron - this is shown in the test_gateway which implements this in this patch
  3. On the other hand if the gateway is responsible for handling the recurring fee then the gateway should not implement hook_recurring_renew instead they should handle the renewal process themselves e.g. Paypal IPN

I have run into some problem with trying to implement these changes

There is a few of other issue/features I would like to see:

  • If an order is unable to be renewed, there should be an option to extend the order for a few days (and notify the user of the failure) - paypal does this with their subscriptions, if the first payment fails it should attempt again a few days later - ubercart should respond to this and extend things like roles (or have the infrastructure in place so uc_roles can respond to this type of an event).
  • there needs to be the ability for unlimited renewals (ie: renew until canceled) -its messy having to enter 99999 to go showing in a users account
  • it would also be good if uc_recurring provided some basic function for setting up the new order for a successful renewal or the ability to update a current order with a payment failure, that way payment gateways like paypal/authorize.net don't need to re-implement this and ubercart can be sure the each gateways manage recurring fees the same way.

The patch attached works (although it probably needs to be cleaned up a bit). It should allow you to test out a recurring payment using the test_gateway and when it renews (via cron) there should be another order/invoice in the users account).

Comments

univate’s picture

Also if you apply the patch linked above #369742: CA predicates maybe don't need to be prevented from being evaluated more than once. you can test out the ability to automatically handle the renewal of roles if they are enabled on a product when a recurring fee is processed.

mrmeech’s picture

Just commenting to show my extreme support for this! :)

rszrama’s picture

I sent you a PM on UC.org about moving forward with the recurring module... feel free to use my contact form to e-mail me if you'd like!

aaronbauman’s picture

This looks like a great patch - I've been looking for a way to set up recurring payments + payment by purchase order, and this seems to be the ticket.

However, just to clarify: this patch will effectively break any existing recurring payments, unless the test gateway is enabled, correct?

torgospizza’s picture

@aaronbauman: from what I gather, it will break recurring payments, but it has nothing to do with the test gateway. That is just for show. What will need to happen is whatever gateway you are using for recurring payments, its module needs to be updated to work with this code.

This looks like a big deal, and +1 to the "unlimited recurring payments" being invoked in a way better than 99999x.. that seems a strange way to do it. Or at least, how it's unlimited should be transparent to the user, so long as it works.

aaronbauman’s picture

OK, so another question / concern: it looks to me like this patch looks for a payment *gateway* in order to process recurring fees:

function uc_recurring_process($order, $fee) {
  $gateways = _payment_gateway_list($order->payment_method, TRUE);

My particular use case is a client who wants automated monthly invoicing for recurring purchase order payments.
However since e.g. COD, Check, and Purchase Order do not use a gateway, they won't be able to implement a recurring fee for these payment methods.

Am i reading this wrong?

Should i implement a shell hook_payment_gateway?
When a payment method doesn't implement a gateway, doesn't it make more sense to give the payment method a chance to handle recurring fees?

Am I barking up the right tree here, or did I miss something?

mrmeech’s picture

Hello --

I've never used patch files before (i'm just starting to get into version control systems and such), but i've been doing my homework and have taught myself the basics. So with that said, i applied the uc_recurring.patch and for the "payment/uc_recurring/uc_recurring.module" file i get this error:

Hunk #1 FAILED at 250.
Hunk #2 FAILED at 414.
Hunk #3 succeeded at 538 (offset -70 lines).
Hunk #4 succeeded at 996 (offset 145 lines).
Hunk #5 FAILED at 1015.

I see the patch is for beta 5, and that's what i'm on. Before attempting this, i haven't modified ANYTHING from my UC2-beta-5 Installation.
Any thoughts on why this file would have failures?

The patch for "payment/uc_credit/test_gateway.module" went smoothly. I assume i was supposed to copy that from the payment/uc_payment folder before running the patch on it.

Thanks in advance :)

univate’s picture

Version: 6.x-2.0-beta5 » 6.x-2.0-rc1
StatusFileSize
new18.31 KB

At the moment this patch is meant to just be a working proof-of-concept.

What torgosPizza said is correct, it is a re-work of the underlying process of how recurring payments are handled - so it will break existing recurring payment gateways, the creditcard test_gateway has been patched to show how it should work as an example.

When you test it out - processing an order for a recurring product with the test_gateway in this patch doesn't really do anything different to what was there before. But when it comes time to renew (you can just edit the database next_charge field to bring forward a renewal) you will see a new invoice in the users account and you will also see any roles attached to that product also get renewed automatically (without uc_recurring even being aware of the uc_roles module exists). This is what you get by treating a renewal as a new order and this is the same for any other module(s) that want to do something on a product being ordered/renewed.

uc_recurring shouldn't have any payment specific code in it and shouldn't care what payment method/gateway want to be able to process recurring payments - when a payment is due it just ask the original gateway if it wants to do anything at that point - some gateways will other will handle things themselve - eg. Paypal IPN

@aaronbauman Calling the payment method to handle the recurring fee is definitely a possibility (actually the more I think about the better it sounds - thanks for the feedback) - at the moment I just want to get something out that worked, the code definitely needs a lot more work - but I want to get an idea if what I was proposing had some support and worth me spending time on - which there looks like it might be.

Not sure if there was a problem with that original patch but I have re-rolled the patch against RC1 (and then tested it, so this patch should definitely work if you want to try again with the latest release)

aaronbauman’s picture

thanks for the info, and thanks for the great patch.
automating invoicing for recurring payments is definitely a feature many of my clients are already interested in.

anyway, i've hacked an implementation of your uc_recurring api into the the uc_po (Purchase Order) module, and it seems to be working ok at the moment.

mrmeech’s picture

Univate -

First of all, sorry to ask this, but, I don't think the patch is 100% working -- stuff is getting patched, but i'm still having issues getting the patch to execute smoothly without spitting back issues. Chances are i'm doing something wrong.. so: Can you do me a HUGE favor and simply attach the two files, uc_recurring.module and test_gateway.module with the patches already applied? I'd like to run a diff on what you send against my "patched" version.

Thanks!

******************
***** EDIT ********
******************
I applied the patch in Eclipse and it worked like a charm. As it turns out, I was coming across problems specifically when trying to apply the patch via Terminal (command line on Mac).

torgospizza’s picture

Interested to see where this is at, and if the patch makes it into core. Please keep us posted!

mrthumpz’s picture

subscribe - definitely a +1 for this functionality!

univate’s picture

Project: UC Recurring Payments and Subscriptions » Ubercart
Version: 6.x-2.x-dev » 6.x-2.0-rc1
StatusFileSize
new21.79 KB

Here is my second round of changes... focusing mostly on cleaning up the api's (I still consider the patch is a work-in-process, but it is fully functional and can be tested)

I realized something as I was going through the code, there are two types of functions that can be used to get modules to interact with each other:

  1. hooks
  2. callbacks

The difference is minor but significant, when calling a callback you are only interested in the results from one module while a hook is used to get results from any module that wants to do something at that point. Drupal itself doesn't really use callbacks much (if at all), this is really more used in a module like ubercart - example: even though you may have multiple gateways setup and running on the one site you are only interested in calling one of these when you process a payment not all of them. In the case of the recurring module it also just needs to find the one module to call at the right time to handle recurring functions.

In Drupal the convention for calling a hook is:

[hook]_[function]()   e.g. uc_order_menu()

While currently in ubercart the convention for using callbacks is either they are predefined in an array (e.g you query all the payment gateways modules to find out what is available and then find the specific callback from this array you need and call it). Or the other method of calling a callback in ubercart is with a syntax like:

[module]_[callback]_[function]()   e.g. [uc_order_pane]_$id_[show_edit]()

So with that in mind I was thinking if you had function(s) in your payment method/gateway module starting with uc_recurring it makes it obvious that those functions are specific callback solely for the purpose of handling recurring fees

So what I did was rename all the relevant functions so they were in this format (which means there is no need to pre-define the available callbacks before hand, we can just see if one is available at the time we need it and if so use it):

So what I setup was the following callback that any payment method can define to support setting up a recurring payments:

uc_recurring_[payment_method]_fee

A callback that allows a module to setup the recurring fee e.g. uc_recurring_credit_fee() and it just expects you to setup the recurring fee with the gateway, set the handler for future renewals and return TRUE/FALSE, the uc_recurring module takes over and actually adds the user fee information to the DB (this is something that Auth.net is doing at the moment and really shouldn't, it is the role of uc_recurring to setup the recurring fees in a standard way for any payment method)

The follow two callbacks are for fee_handlers to define (if they want):

uc_recurring_[fee_handler]_renew

A callback called on cron jobs to allow a gatway to jump in a do something at the time of renewal e.g. uc_recurring_test_gateway_renew()

uc_recurring_[fee_handler]_fee_ops

A callback that defines what operations are available to the admin and normal members, things like edit/update/cancel e.g. uc_recurring_test_gateway_fee_ops()

That basically is the guts of what is required to setup a recurring gateway.

As before the test_gateway has been patched to work with this new api as a working example, but I have also released a gateway (http://drupal.org/project/uc_securepayau) I am developing in conjunction with this for my own needs which also supports the above api's (as another example)

For my own needs I want to also get paypal subscriptions working with this same interface, although one of the problems with paypal is that ubercart first attempts to charge the payment and then sets up the recurring payment, with paypals subscription process you really need this all done in one step, so either recurring payment need to be setup in the payment charge step or the charge step skipped and everything processed in the recurring stage, anyone have any thoughts on what might be a better approach to standardize on for gateways like paypal where you need to do it all in one step?

rszrama’s picture

Project: Ubercart » UC Recurring Payments and Subscriptions
Version: 6.x-2.0-rc1 » 6.x-1.0
rszrama’s picture

Version: 6.x-1.0 » 6.x-2.x-dev

Couldn't select the 2.x-dev version when I moved the project. : P

aaronbauman’s picture

#13 fails against 6.x-2.0-rc2

univate’s picture

Yes, now that uc_recurring has split from ubercart (core) I will need to split this patch into separate patches for ubercart and uc_recurring, although since I am free to commit these change directly into the uc_recurring 2.x-dev branch, I am hoping to get out a dev version shortly with these changes included so its easier for others to start doing some testing.

grendzy’s picture

Project: Ubercart » UC Recurring Payments and Subscriptions
Version: 6.x-2.0-rc1 » 6.x-2.x-dev
StatusFileSize
new57.74 KB

Related to this:

-- uc_recurring doesn't update the order balance, so the orders end up with a negative balance. (see attached)

-- Recurring payments don't show up under reports (/admin/store/reports), only the initial payment is included in the total.

Sborsody’s picture

Grendzy, those sound like new feature requests.

I'm not entirely clued into what this original post was about as I just installed and started playing with Ubercart today... But I want to say that my client is selling software with a monthly licensing fee. In addition to the license, this recurring payment entitles them (depending upon the product) to what amounts to a subscription to a feature (contact the CEO, new downloads related to product) on the website (hopefully via a role). I just want to make sure that it is clear that there are instances when a physical product (or even downloadable product) and a website subscription can go hand-in-hand. Role assignment needs to be continuous as long as the recurring payment is being made but not necessarily dealing with additional shipments of original purchase.

univate’s picture

@grendzy this is exact what this feature is trying to solve. If recurring fees have there own order they would be treated like any other purchase appearing in a separate invoice and in a separate payment record for reports.

By a recurring fee just being another order other modules like uc_role, uc_shipping, uc_whatever can also act on every payment as they currently do when you place a new order.

@Sborsody you do bring up an issue I haven't really thought about. ie: when you want one module (e.g shipping) to only happen on initial order but not on subsequent orders/renewals, while you still want other modules to act on both the initial order and renewals/

What I have currently proposed in this issue should still work, because at least in your case you are selling two different products, ones the physical product with shipping and the second product is the subscription. So I think the way to handle this would be to setup two different product and if you want to sell them together create a product kit with both products combined.

univate’s picture

StatusFileSize
new16.84 KB
new4.87 KB

Recreated the patch(es), this is exactly the same as the previous patch just split into two, one for uc_recurring and the other for ubercart.

The ubercart patch is only required to patch uc_credit and test_gateway to get the test_gateway working with this or other credit card processing gateway.

Sborsody’s picture

Ah! A product kit, hrm? Thanks. I'll check that out.

univate’s picture

StatusFileSize
new16.97 KB
new6.57 KB

Thought I would show how easy it is to add a new payment method that supports this recurring api by creating a patch for uc_payment_pack to support recurring billing for check/cod/other payment methods as aaronbauman was wanting to do - all this does for those manual payment method is create a new invoice in the system and users account on each recurring billing date.

This change is part of the ubercart patch (along with the credit card test gateway patch), I have also made one small change to the uc_recurring patch, which just checks that a payment method actually supports the recurring option before allowing you to enable it on the settings page and use it for recurring billing.

univate’s picture

I have committed the above patch for this module into the 2.x-dev branch.

Hopefully to try and make it a bit easier to get some testing and comments back.

To use it with the credit card test gateways or the one of the payment pack methods you will need to patch your ubercart module with the previous comment patch for ubercart.

Jackinloadup’s picture

StatusFileSize
new24.14 KB

I applied patch to ubercart (6.x-2.0-rc2) then modified the uc_authorizenet module like so:

payment/uc_authorizenet/uc_authorizenet.module

function uc_authorizenet_payment_gateway() {
  $gateways[] = array(
    'id' => 'authorizenet',
    'title' => t('Authorize.net'),
    'description' => t('Process credit card payments using the AIM service of Authorize.net.'),
    'settings' => 'uc_authorizenet_settings_form',
    'credit' => 'uc_authorizenet_charge',
    'credit_txn_types' => array(UC_CREDIT_AUTH_ONLY, UC_CREDIT_PRIOR_AUTH_CAPTURE, UC_CREDIT_AUTH_CAPTURE, UC_CREDIT_REFERENCE_SET, UC_CREDIT_REFERENCE_TXN),
     // ADDED THE FOLLOWING LINE
    'recurring_fee' => 'uc_authorizenet_recurring_fee',
  );

  return $gateways;
}

Now Im getting this message from an order: The recurring fee for product failed.
I attached an image of this.

I noticed that in watchdog each order had two responses for each test I did. The first was an auth_capture which looked normal. The second looked much more scarce: http://pastebin.com/m3ba124a4 Im not exactly sure what the second message is or if it relates.

In addition to that I noticed that on previous successful recurring orders would show watchdog reports from uc_cim, the module im using to manage the recurring payments.

Lastly Im currently using yesterdays (5/5/09) uc_recurring csv due to this issue: #450212: Option for recurring fee to be the same as product price.

Any idea what could be causing this issue and where I can start looking to fix it?

Thank you in advance.

mrthumpz’s picture

The reason it did not work is because each payment gateway has to be integrated with uc_recurring. The patch only did the test gateway.

Luckily for you, I just worked on a uc_cim integration today, and I have gotten it to work.

Here is the related comment with more info:
http://www.ubercart.org/contrib/2537#comment-38214

univate’s picture

yes you will need to do a little bit more work now then I originally posted (see comment #13 above), to get it working, especially if you want new orders to appear on your site for each renewal.

I have just re-worked some of the renewal section in uc_recurring to try and help support gateways like authorizenet and paypal (which I hope to work on at some point), this just mainly provides a new function:

uc_recurring_renew($fee);

This allows other modules to process a renewal in the same way that uc_recurring does in its cron job on the expire date of a fee.

jacerider’s picture

Not sure why, but I had to change line 322 from:

if ($fee['data']['nid'] == $product->nid) {

to:

if ($fee['data']['model'] == $product->model) {

in order for the correct product to show up in the order. NID is missing in the $fee['data'] array. Taking a look at the database it is being saved as NULL in the serialized data field in uc_recurring_users.

Now, correct me if I am wrong, but wouldn't it be better to check for model instead of nid? A single nid can have multiple purchasable products due to the attributes module. A check against model would assure the correct product gets returned (assuming that all model strings are unique).

Other than that -- everything seems to be working perfectly. I did something similar for Ubercart on D5 but don't have the time to rework things for D6. Thanks for the effort.

One thing that my D5 module did was also group orders with multiple recurring products into a single new order. As you have it set now, each recurring product gets its own unique order. Due to shipping, we may want to come up with a way to combine multiple products into the newly created order. Just a thought.

univate’s picture

The only reason we need to trace a recurring fee back to a product is so that when we create a new order we can put something in the actual order "product name" section on the invoice. I'm thinking to support more flexibly recurring fee functionality for example combining recurring fees we should add a 'name'/'title' field in the database so that we can setup the recurring fee name at the time of the original order and that will appear on every recurring order.

univate’s picture

I have tracked down that problem in #28 and fixed it. We can't actually use model as this will be blank if you specify that a recurring fee should apply to all SKU's for a particular product.

I am now storing the original product information with the recurring fee so we can refer back to it at anytime in the recurring rebilling process.

lindsayo’s picture

This is all getting a bit muddled to me.

I want to try using Authorize.net CIM and Ubercart for recurring payments. I am in D6, UC2. I like the new order for each new recurring bill as univate has developed with this patch, but I don't know which is the most current patch. And on the uc_cim page http://www.ubercart.org/contrib/2537 is the most current.

So which modules and which patches do I need to apply where?

I'm sure I'm not the only one feeling overwhelmed by the random bits and pieces, trying to figure out what is the latest version of which patch/module and how to proceed.

I hope someone who has a grip on all of this will help with a bit of a step-by-step on what to install and what to patch.

Maybe we need some sort of a recurring payments workspace to track all of this, so a person could just go there to see what the current methodology is for this?

TIA!

-L

lindsayo’s picture

Okay, I'm finally getting this sorted out. I have installed:

uc_recurring 6.x-1.0 (no patching)
uc_cim (newest patch, #30)
UC 6.x-2.0-rc3

I set up a developer's testing gateway at Authorize.net.
Configured uc_cim according to its README.txt. Great instructions, thanks.
So far so good. The first transaction went through, now awaiting results of an expiring cycle to see what happens.

Thanks to everyone who has worked so hard to make all of this recurring billing stuff work.

univate’s picture

Status: Needs work » Fixed

I think I might close this issue, it started before uc_recurring was a separate project and all the changes to uc_recurring have been committed to the 2.x-dev branch.

I have opened separate issue for tracking the changes that need to happen to ubercart if you are using uc_credit or uc_payment_pack with uc_recurring, so that we can track these changes and when ready submit to the core ubercart project.

See #483494: changes to ubercart to work with uc_recurring

reubenavery’s picture

Cool. might want to update the link on the project front page to go to that issue instead of this one

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

dadderley’s picture

I am looking for clarification.

I am trying to implement a monthly subscription scenario on my site.
I have the subscription (products) set up so that I have a 1 year subscription and a 1 month subscription.
The 1 month subscription has a recurring payment option.
On the initial purchase I give the user a role that expires at the end of a certain time period.
When they renew their subscription the expiration of the role gets extended.

When the recurring fee gets processed, will my roles get extended as they would if the user purchased another subscription?
Is a recurring fee the equivalent to a new order?

I am busy testing right now trying to figure it out.
The recurring fee seems to be working well with paypal in a live setting.
Now my real challenge is to get the roles extended on a payment received.

Am I on the right track here?