On line 430 of uc_recurring.module, the conditional statement checks to see if there going to be retries for the recurring billing, however, there is no execution of uc_recurring_expire when there are no retries set.
e.g.
// check if there will be a retry
$extension = uc_recurring_get_extension($fee->pfid, $fee->attempts);
if ($extension) {
$fee->data['retry_order_id'] = $order->order_id;
uc_recurring_fee_user_save($fee);
}
uc_recurring_renewal_module_invoke('recurring_renewal_failed', $order, $fee);
// @todo - replace with rules
//ca_pull_trigger('uc_recurring_renewal_failed', $order, $fee);
rules_invoke_event('uc_recurring_renewal_failed', $order, $fee);
uc_order_comment_save($fee->order_id, $user->uid, t('New recurring fee failed on order <a href="@store-orders">@order_id</a>.', array('@store-orders' => url('admin/store/orders/' . $order->order_id), '@order_id' => $order->order_id)));
}
return FALSE;
Should be
// check if there will be a retry
$extension = uc_recurring_get_extension($fee->pfid, $fee->attempts);
if ($extension) {
$fee->data['retry_order_id'] = $order->order_id;
uc_recurring_fee_user_save($fee);
}
// if no retries, cancel the recurring order
else {
uc_recurring_expire($fee);
}
uc_recurring_renewal_module_invoke('recurring_renewal_failed', $order, $fee);
// @todo - replace with rules
//ca_pull_trigger('uc_recurring_renewal_failed', $order, $fee);
rules_invoke_event('uc_recurring_renewal_failed', $order, $fee);
uc_order_comment_save($fee->order_id, $user->uid, t('New recurring fee failed on order <a href="@store-orders">@order_id</a>.', array('@store-orders' => url('admin/store/orders/' . $order->order_id), '@order_id' => $order->order_id)));
}
return FALSE;
This should update the recurring order and properly expire it. Couldn't execute this in Rules as there isn't a rules action to update a recurring order entry. We had an issue where the recurring subscription would loop endlessly and create new orders to try and charge the customer while using uc_qbms as a payment gateway. This issue seems to be more related to uc_recurring not having the proper subscription expiration executed when a charge doesn't go through, so I thought best to implement here.
Hope this helps someone else!
Comments
Comment #1
tinker commentedThe expiration is handled on the next CRON run by uc_recurring_cron() when the fee will be found using uc_recurring_get_fees_for_expiration() and expired with uc_recurring_expire().
Comment #2
jordan8037310 commentedI disagree that this works as designed. I was migrating a site from Drupal 6 to Drupal 7 when I encountered this bug. The payment gateway used was uc_qbms. At the time of the upgrade, multiple credit card accounts on file were attempted to be charged upwards of 100 to 1,000 times.
I think this needs to be addressed in some functional manner to check that enough time has passed to attempt a charge again, OR alternatively set that the charge failed at the time that it fails (expired).
Thoughts?
Comment #3
tinker commentedHere's the process:
I have not used uc_qbms. If that module sets $fee->own_handler = 1, then uc_recurring_process_extensions() and uc_recurring_get_fees_for_expiration() do not execute as it is up to that module to handle fee expiration. See paypal implementation for own handler.
BTW never upgrade a site with a live payment processor.
Comment #4
jordan8037310 commentedThanks for the breakdown. Unfortunately the issue happened during migration to production deployment and was not an issue during development and staging. I will look at uc_qbms fee expiration handler when I have some time.
Comment #5
tinker commentedI once executed a 'final' D6 to D7 migration script on a staging server, just before applying on live server, and saw it try to process hundreds of payments using Auth.net CIM. Luckily I had disabled payment gateway and outbound emails on staging server. Tracked it down to a 3 hour time offset between staging and live servers that forced previously completed recurring billings to process again. Could have caused massive cleanup of pending transactions and many worried customers from notification emails! The same script applied to live server without any problems afterwards. Live and learn.