Currently there are CA triggers for when a recurring fee expires, is successful, or fails when renewing. It would be great if there was a trigger fired when the fee is canceled, either by the user or the admin. If the recurring product grants users special privileges (like published nodes, roles, etc), it would be useful to cancel those privileges if the user is no longer paying for them.

This could either be a new trigger ("uc_recurring_renewal_canceled"), or it could use the "uc_recurring_renewal_expired" trigger. If it's the latter, a call to uc_recurring_set_intervals() could be added to uc_recurring_cancel(), since it fires the "uc_recurring_renewal_expired" if the remain_intervals is 0.

Let me know what you think the best option would be and I'll create a patch.

Comments

univate’s picture

I removed the CA trigger occurring in uc_recurring_set_intervals() because it didn't make sense to happen there, it was firing the expired trigger when a payment renewal occured and the member would still have one full subscription cycle remaining.

Having a trigger on cancellation events could have its uses, although I wouldn't use it to remove privileges as the user most likely still has some time remaining on their subscription and then you would be forced to also refund them something if you also cancelled it. So I would also want to be careful about how that trigger was labeled so its clear that the event is a "cancel request" and not the actual subscription expiring.

What we really need is a way to trigger when the subscription actually expires, that is:
* $fee->remaining_intervals == 0
* $fee->next_charge < time()

But when that happens we need a way to mark the fee or order as expired, because we don't want to trigger the event multiple times.

One idea could be to add a new status in the 'uc_order_statuses' table that allows us to set the original order as expired? This could have the same state as 'completed' - actually that wont work since we could have more then one recurring fee on an order.

So I guess we need to add another field to uc_recurring_users table that has the state of the recurring fee?

duellj’s picture

That makes sense that a "cancellation" trigger could get confusing, because yes, you wouldn't want to prematurely cancel privileges if the user has time left on their subscription.

So a trigger that gets fired when the subscription actually expires would be immensely helpful, as long as it gets fired when the users cancels a recurring subscription (the terminology is a bit misleading in this case though, since the subscription isn't really "expired", but it works).

A "state" field added to the uc_recurring_users makes sense, probably could be expanded to other things in the future if needed. We probably need to add additional code to hook_cron() to handle expired fees, something like this maybe:

function uc_recurring_cron() {
  ....

  $fees = uc_recurring_get_fees_for_expiration();
  if (!empty($fees)) {
    foreach($fees as $fee) {
      uc_recurring_expire($fee);
    }
    watchdog('uc_recurring', '@count recurring fees expired successfully.', array('@count' => count($fees)));
  }
}

function uc_recurring_get_fees_for_expiration() {
  $fees= array();
  $result = db_query("SELECT * FROM {uc_recurring_users} WHERE remaining_intervals = 0 AND next_charge <= %d AND own_handler = 0 AND status != 'expired' ORDER BY order_id DESC", time());
  while ($fee = db_fetch_object($result)) {
    $fee->data = unserialize($fee->data);
    $fees[$fee->rfid] = $fee;
  }
  return $fees;
}

function uc_recurring_expire($fee) {
  $fee->status = 'expired';
  uc_recurring_fee_save($fee);
}

Let me know what you think and I can write a patch to implement this.

univate’s picture

That looks good to me and I think we should do this.

I can think of a number of uses for that status field as well:

* active
* canceled (still active, but wont renew on next charge)
* expired
* suspended

What we should then also add is a CA trigger in the uc_recurring_expire() function so you can add emails or other events on expiration.

duellj’s picture

Status: Active » Needs review
StatusFileSize
new3.37 KB

Attached is a patch for the code in #2, with database updates and the call to the expire CA trigger. The additional statuses sound useful, should probably create a new issue to handle them.

univate’s picture

That looks great, although its missing the new field in the schema.

diff --git uc_recurring.install uc_recurring.install
index cd81cec..934e113 100644
--- uc_recurring.install
+++ uc_recurring.install
@@ -179,6 +179,13 @@ function uc_recurring_schema() {
         'not null' => FALSE,
         'default' => 0,
       ),
+      'status' => array(
+        'description' => 'The status of the recurring fee, e.g. "active" or "expired"',
+        'type' => 'varchar',
+        'length' => 32,
+        'not null' => TRUE,
+        'default' => 'active',
+      ),
     ),
     'primary key' => array('rfid'),
   );

I also wonder whether there are any thoughts on the status field being a varchar? I have typically been of the view that a field for storing things like 'status' that only has a specific set of values is usually best defined in code rather then text in the database. And instead using an INT as the type in the database.

So:

define('UC_RECURRING_FEE_STATUS_ACTIVE', 0);
define('UC_RECURRING_FEE_STATUS_EXPIRED', 1);

I believe this is more efficient for both storing this data as well as retrieving rows filtered on that column.

duellj’s picture

StatusFileSize
new3.32 KB

I like the idea of constants; it's much cleaner and more efficient. Rerolled patch to include the changes.

univate’s picture

Status: Needs review » Fixed

committed.

Status: Fixed » Closed (fixed)

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

vitis’s picture

Title: Add CA trigger for Recurring Fee Expired » Add CA trigger for Recurring Fee Cancel

How can the administrator tell which users have canceled their subscription?

univate’s picture

Title: Add CA trigger for Recurring Fee Cancel » Add CA trigger for Recurring Fee Expired

@vitis replying to closed issues is a good way to be ignored. This and the other post you have made are on issues that where closed back in october, they are not going to show up in my list of issues that need to be addressed - its only because I get emailed about new posts that I actually saw these posts.

This specific issue was really mis-titled as it was really about expired recurring fees which has been implemented.

Please create new issues for the new features or bugs you are reporting.

vitis’s picture

ok