'), $node->model => $node->model);
if (module_exists('uc_attribute')) {
$result = db_query("SELECT model FROM {uc_product_adjustments} WHERE nid = %d", $node->nid);
while ($row = db_fetch_object($result)) {
if (!in_array($row->model, $options)) {
$options[$row->model] = $row->model;
}
}
}
$form['model'] = array(
'#type' => 'select',
'#title' => t('Applicable Model/SKU'),
'#description' => t('Select the applicable product model/SKU for this fee.'),
'#options' => $options,
'#default_value' => $fee['model'],
);
$form['fee_amount'] = array(
'#type' => 'textfield',
'#title' => t('Recurring fee amount'),
'#description' => t('Charge this amount each billing period.
The product price is still charged at checkout.'),
'#default_value' => $fee['fee_amount'],
'#size' => 16,
'#field_prefix' => variable_get('uc_sign_after_amount', FALSE) ? '' : variable_get('uc_currency_sign', '$'),
'#field_suffix' => variable_get('uc_sign_after_amount', FALSE) ? variable_get('uc_currency_sign', '$') : '',
);
$form['initial'] = array(
'#type' => 'fieldset',
'#title' => t('Initial charge'),
'#collapsible' => FALSE,
'#description' => t('Specify the time to wait to start charging the recurring fee after checkout. Remember the product price will be charged at the time of checkout.'),
'#attributes' => array('class' => 'interval-fieldset'),
);
$form['initial']['initial_charge_value'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(uc_range(0, 52)),
'#default_value' => $fee['initial_charge_value'],
);
$form['initial']['initial_charge_unit'] = array(
'#type' => 'select',
'#options' => array(
'days' => t('day(s)'),
'weeks' => t('week(s)'),
'months' => t('month(s)'),
'years' => t('year(s)'),
),
'#default_value' => $fee['initial_charge_unit'],
);
$form['regular'] = array(
'#type' => 'fieldset',
'#title' => t('Regular interval'),
'#collapsible' => FALSE,
'#description' => t('Specify the length of the billing period for this fee.'),
'#attributes' => array('class' => 'interval-fieldset'),
);
$form['regular']['regular_interval_value'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(uc_range(1, 52)),
'#default_value' => $fee['regular_interval_value'],
);
$form['regular']['regular_interval_unit'] = array(
'#type' => 'select',
'#options' => array(
'days' => t('day(s)'),
'weeks' => t('week(s)'),
'months' => t('month(s)'),
'years' => t('year(s)'),
),
'#default_value' => $fee['regular_interval_unit'],
);
$form['number_intervals'] = array(
'#type' => 'textfield',
'#title' => t('Number of billing periods'),
'#description' => t('Specify how many times the recurring fee will be charged.'),
'#size' => 16,
'#default_value' => $fee['number_intervals'],
'#required' => TRUE,
);
return uc_product_feature_form($form);
}
function uc_recurring_feature_form_validate($form_id, $form_values) {
if (intval($form_values['number_intervals']) <= 0) {
form_set_error('number_intervals', t('Only positive whole number values are accepted for the number of billing periods.'));
}
}
function uc_recurring_feature_form_submit($form_id, $form_values) {
if (empty($form_values['pfid'])) {
$fee['pfid'] = db_next_id('{uc_product_features}_pfid');
}
else {
$fee['pfid'] = $form_values['pfid'];
}
$fee = array(
'pfid' => $fee['pfid'],
'model' => $form_values['model'],
'fee_amount' => $form_values['fee_amount'],
'initial_charge' => $form_values['initial_charge_value'] .' '. $form_values['initial_charge_unit'],
'regular_interval' => $form_values['regular_interval_value'] .' '. $form_values['regular_interval_unit'],
'number_intervals' => intval($form_values['number_intervals']),
);
uc_recurring_fee_save('product', $fee);
$args = array(
'@product' => empty($fee['model']) ? t('this product') : t('product @model', array('@model' => $fee['model'])),
'!amount' => uc_currency_format($fee['fee_amount']),
'!initial' => $fee['initial_charge'],
'!regular' => $fee['regular_interval'],
'!intervals' => t('!num times', array('!num' => $fee['number_intervals'] - 1)),
);
$data = array(
'pfid' => $fee['pfid'],
'nid' => $form_values['nid'],
'fid' => 'recurring',
'description' => t('When @product is purchased, add a fee for !amount charged first after !initial and every !regular after that !intervals.', $args),
);
return uc_product_feature_save($data);
}
// Adds the settings for the recurring module on the feature settings form.
function uc_recurring_settings_form() {
$form['uc_recurring_handler'] = array(
'#type' => 'select',
'#title' => t('Recurring fee handler'),
'#description' => t('Select a module to process recurring fees on your site.'),
'#options' => drupal_map_assoc(module_implements('recurring_fee', TRUE)),
'#default_value' => variable_get('uc_recurring_handler', 'uc_recurring'),
);
foreach (_payment_method_list() as $method) {
$options[$method['id']] = $method['name'];
}
$form['uc_recurring_payment_methods'] = array(
'#type' => 'checkboxes',
'#title' => t('Valid payment methods for orders with recurring fees'),
'#description' => t('Only selected payment methods will be available for customers purchasing products with recurring fees.
It is up to you to make sure your chosen handler is compatible with the payment methods you select.
For example, the uc_recurring handler is only compatible with the Credit Card payment method.'),
'#options' => $options,
'#default_value' => variable_get('uc_recurring_payment_methods', array()),
);
$form['uc_recurring_checkout_message'] = array(
'#type' => 'textarea',
'#title' => t('Recurring free checkout form message'),
'#description' => t('Enter a message to be displayed on the checkout form page when a customer has products in the cart with recurring fees.
Leave blank to not display any message.'),
'#default_value' => variable_get('uc_recurring_checkout_message', ''),
);
$form['uc_recurring_checkout_process'] = array(
'#type' => 'checkbox',
'#title' => t('Attempt to process recurring fees during checkout.'),
'#description' => t('If not selected, you must have an alternate way of processing fees.
With the default handler, this is only possible in credit card debug mode.'),
'#default_value' => variable_get('uc_recurring_checkout_process', TRUE),
);
$form['uc_recurring_checkout_fail'] = array(
'#type' => 'radios',
'#title' => t('Action to take if a recurring fee fails to process during checkout'),
'#description' => t('Regardless of your selection, an admin comment will report the failure.
Note: Even if you select the first option, checkout will complete if another payment has already been captured.'),
'#options' => array(
'fail' => t('Return a failed message and do not complete checkout.'),
'proceed' => t('Return a failed message but complete checkout.'),
'silent' => t('Show no message and complete checkout.'),
),
'#default_value' => variable_get('uc_recurring_checkout_fail', 'fail'),
);
return $form;
}
// Displays a table for users to administer their recurring fees.
function uc_recurring_user_table($uid) {
$rows = array();
$output = '';
// Setup a header array for the table.
$header = array(t('Order'), t('Amount'), t('Interval'), t('Next charge'), t('Remaining'), t('Operations'));
// Loop through the fees sorted by the order ID descending.
$result = db_query("SELECT * FROM {uc_recurring_users} WHERE uid = %d AND remaining_intervals > 0 ORDER BY order_id DESC", $uid);
while ($fee = db_fetch_array($result)) {
$ops = array();
// Get the $ops from the module implementing the handler.
$callback = $fee['fee_handler'] .'_recurring_fee_ops';
if (function_exists($callback)) {
$ops = $callback('user', $fee);
}
// Add the row to the table for display.
$rows[] = array(
l($fee['order_id'], 'user/'. $uid .'/order/'. $fee['order_id']),
uc_currency_format($fee['fee_amount']),
array('data' => check_plain($fee['regular_interval']), 'nowrap' => 'nowrap'),
$fee['remaining_intervals'] == 0 ? '-' : format_date($fee['next_charge'], 'small'),
$fee['remaining_intervals'],
array('data' => implode(' ', $ops), 'nowrap' => 'nowrap'),
);
}
// Only display the table if fees were found.
if (count($rows) > 0) {
$output = theme('table', $header, $rows);
}
return $output;
}
// Displays the confirm form for cancelling a recurring fee.
function uc_recurring_user_cancel_form($uid, $rfid) {
$form['uid'] = array(
'#type' => 'value',
'#value' => $uid,
);
$form['rfid'] = array(
'#type' => 'value',
'#value' => $rfid,
);
return confirm_form($form, t('Are you sure you want to cancel your recurring fee?'), 'user/'. $uid, t('This action cannot be undone and may result in the termination of subscription services.'), t('Confirm'), t('Cancel'));
}
function uc_recurring_user_cancel_form_submit($form_id, $form_values) {
uc_recurring_fee_cancel($form_values['rfid']);
drupal_set_message(t('The recurring fee has been cancelled.'));
return 'user/'. $form_values['uid'];
}
// Displays a table for the administration of recurring fees.
function uc_recurring_admin() {
$output = drupal_get_form('uc_recurring_admin_filter_form');
$header = array(
array('data' => t('ID'), 'field' => 'ru.rfid', 'sort' => 'desc'),
array('data' => t('Order'), 'field' => 'ru.order_id'),
array('data' => t('Amount'), 'field' => 'ru.fee_amount'),
array('data' => t('Next'), 'field' => 'ru.next_charge'),
array('data' => t('Interval'), 'field' => 'ru.regular_interval'),
array('data' => t('Left'), 'field' => 'ru.remaining_intervals'),
array('data' => t('Total')),
array('data' => t('Operations')),
);
if (arg(4) == 'view' && intval(arg(6)) > 0) {
if (arg(5) == 'fee') {
$result = db_query("SELECT * FROM {uc_recurring_users} AS ru WHERE ru.rfid = %d", arg(6));
}
elseif (arg(5) == 'order') {
$result = db_query("SELECT * FROM {uc_recurring_users} AS ru WHERE ru.order_id = %d", arg(6));
}
}
else {
$result = pager_query("SELECT * FROM {uc_recurring_users} AS ru" . tablesort_sql($header), 30);
}
while ($fee = db_fetch_array($result)) {
$ops = array();
// Get the $ops from the module implementing the handler.
$callback = $fee['fee_handler'] .'_recurring_fee_ops';
if (function_exists($callback)) {
$ops = $callback('fee_admin', $fee);
}
$rows[] = array(
l($fee['rfid'], 'admin/store/orders/recurring/view/fee/'. $fee['rfid']),
l($fee['order_id'], 'admin/store/orders/'. $fee['order_id']),
uc_currency_format($fee['fee_amount']),
$fee['remaining_intervals'] == 0 ? '-' : format_date($fee['next_charge'], 'small'),
array('data' => check_plain($fee['regular_interval']), 'nowrap' => 'nowrap'),
$fee['remaining_intervals'],
$fee['remaining_intervals'] + $fee['charged_intervals'],
array('data' => implode(' ', $ops), 'nowrap' => 'nowrap'),
);
}
$output .= theme('table', $header, $rows);
$output .= theme('pager', NULL, 30, 0);
if (arg(4) == 'view') {
$output .= l(t('Back to the full list.'), 'admin/store/orders/recurring');
}
return $output;
}
// Filter by a specific order ID.
function uc_recurring_admin_filter_form() {
$form['type'] = array(
'#type' => 'select',
'#options' => array(
'order' => t('Order ID'),
'fee' => t('Fee ID'),
),
'#default_value' => arg(5) == 'fee' ? 'fee' : 'order',
'#prefix' => '',
'#suffix' => '
',
);
$form['id'] = array(
'#type' => 'textfield',
'#default_value' => arg(6),
'#size' => 10,
'#prefix' => '',
'#suffix' => '
',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Filter'),
'#attributes' => array('style' => 'margin: .85em 0em;'),
);
return $form;
}
function uc_recurring_admin_filter_form_submit($form_id, $form_values) {
if (intval($form_values['id']) > 0) {
return 'admin/store/orders/recurring/view/'. $form_values['type'] .'/'. $form_values['id'];
}
}
// Confirm a recurring fee charge.
function uc_recurring_admin_charge_form() {
$fee = uc_recurring_fee_load('user', arg(4));
$form['message'] = array(
'#value' => ''. t('Are you sure you want to charge the customer !amount at this time?', array('!amount' => uc_currency_format($fee['fee_amount']))) .'
',
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Charge'),
'#suffix' => l(t('Cancel'), uc_referer_uri()),
);
return $form;
}
function uc_recurring_admin_charge_form_submit($form_id, $form_values) {
$fee = uc_recurring_fee_load('user', arg(4));
// Attempt to process the charge.
if (uc_recurring_charge($fee)) {
// Update the fee in the database.
$next_charge = strtotime('+'. $fee['regular_interval']);
db_query("UPDATE {uc_recurring_users} SET next_charge = %d, remaining_intervals = remaining_intervals - 1, charged_intervals = charged_intervals + 1 WHERE rfid = %d", $next_charge, $fee['rfid']);
drupal_set_message(t('Recurring fee @fee charged successfully.', array('@fee' => arg(4))));
}
else {
drupal_set_message(t('Attempt to charge recurring fee @fee failed.', array('@fee' => arg(4))), 'error');
}
return 'admin/store/orders/recurring/view/fee/'. arg(4);
}
// Let an admin edit a recurring fee.
function uc_recurring_admin_edit_form() {
drupal_add_css(drupal_get_path('module', 'uc_recurring') .'/uc_recurring.css');
$fee = uc_recurring_fee_load('user', arg(4));
list($fee['regular_interval_value'], $fee['regular_interval_unit']) = explode(' ', $fee['regular_interval']);
$form['fee_amount'] = array(
'#type' => 'textfield',
'#title' => t('Recurring fee amount'),
'#description' => t('Charge this amount each billing period.'),
'#default_value' => $fee['fee_amount'],
'#size' => 16,
'#field_prefix' => variable_get('uc_sign_after_amount', FALSE) ? '' : variable_get('uc_currency_sign', '$'),
'#field_suffix' => variable_get('uc_sign_after_amount', FALSE) ? variable_get('uc_currency_sign', '$') : '',
);
$form['remaining_intervals'] = array(
'#type' => 'textfield',
'#title' => t('Remaining billing periods'),
'#description' => t('Specify how many more times to charge the fee.'),
'#size' => 16,
'#default_value' => $fee['remaining_intervals'],
);
$form['regular'] = array(
'#type' => 'fieldset',
'#title' => t('Regular interval'),
'#collapsible' => FALSE,
'#description' => t('Modify the length of the billing period for this fee. Changing this value will reset the timer for the next charge. You can also charge the fee manually to collect payment ahead of time and reset the interval.'),
'#attributes' => array('class' => 'interval-fieldset'),
);
$form['regular']['regular_interval_value'] = array(
'#type' => 'select',
'#options' => drupal_map_assoc(uc_range(1, 52)),
'#default_value' => $fee['regular_interval_value'],
);
$form['regular']['regular_interval_unit'] = array(
'#type' => 'select',
'#options' => array(
'days' => t('day(s)'),
'weeks' => t('week(s)'),
'months' => t('month(s)'),
'years' => t('year(s)'),
),
'#default_value' => $fee['regular_interval_unit'],
);
$form['reset_next_charge'] = array(
'#type' => 'checkbox',
'#title' => t('Reset the next charge timer upon form submission using the specified interval.'),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
'#suffix' => l(t('Cancel'), uc_referer_uri()),
);
return $form;
}
function uc_recurring_admin_edit_form_submit($form_id, $form_values) {
$interval = $form_values['regular_interval_value'] .' '. $form_values['regular_interval_unit'];
db_query("UPDATE {uc_recurring_users} SET fee_amount = %f, regular_interval = '%s', "
."remaining_intervals = %d WHERE rfid = %d", $form_values['fee_amount'],
$interval, $form_values['remaining_intervals'], arg(4));
if ($form_values['reset_next_charge']) {
$next_charge = strtotime('+'. $interval);
db_query("UPDATE {uc_recurring_users} SET next_charge = %d WHERE rfid = %d", $next_charge, arg(4));
}
drupal_set_message(t('The changes to the fee have been saved.'));
return 'admin/store/orders/recurring/view/fee/'. arg(4);
}
// Confirm a recurring fee deletion.
function uc_recurring_admin_delete_form() {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Delete'),
'#suffix' => l(t('Cancel'), uc_referer_uri()),
);
return $form;
}
function uc_recurring_admin_delete_form_submit($form_id, $form_values) {
uc_recurring_fee_delete(array('pfid' => arg(4)), 'user');
drupal_set_message(t('Recurring fee @fee deleted.', array('@fee' => arg(4))));
return 'admin/store/orders/recurring';
}
/**
* Saves a reference transaction fee to the database.
*
* Recurring handlers for payment gateways processing recurring fees through a
* reference transaction should use the uc_recurring_byref system. By calling
* this function from a recurring fee handler and passing on the order and fee
* objects as received, the system will add the fee to the single table that
* will be processed on cron.
*
* @param $order
* The order object for the recurring fee.
* @param $fee
* The fee data object.
* @param $handler
* The name of the recurring fee handler using the uc_recurring_byref system.
* @param $callback
* The name of the charge function for the gateway that will be used to charge
* the reference transactions.
* @param $ref_id
* The reference ID that will be used to process the reference transaction at
* the payment gateway.
*/
function uc_recurring_byref_save($order, $fee, $handler, $callback, $ref_id) {
// Build the data array stored for the fee.
$data = array(
'charge_callback' => $callback,
'ref_id' => $ref_id,
'model' => $fee->model,
);
$fee = array(
'rfid' => db_next_id('{uc_product_users}_rfid'),
'uid' => $order->uid,
'fee_handler' => 'uc_recurring_byref:'. $handler,
'next_charge' => strtotime('+'. $fee->initial_charge),
'fee_amount' => $fee->fee_amount,
'regular_interval' => $fee->regular_interval,
'remaining_intervals' => $fee->number_intervals,
'charged_intervals' => 0,
'order_id' => $order->order_id,
'data' => serialize($data),
);
uc_recurring_fee_save('user', $fee);
uc_order_comment_save($order->order_id, 0, t('Recurring fee !fee added to order.', array('!url' => url('admin/store/orders/recurring/view/fee/'. $fee['rfid']), '!fee' => $fee['rfid'])));
return TRUE;
}
/**
* Saves a recurring fee either for a product or for a user.
*/
function uc_recurring_fee_save($type, $data) {
switch ($type) {
case 'product':
// First attempt to update an existing row.
db_query("UPDATE {uc_recurring_products} SET model = '%s', fee_amount = %f, initial_charge = '%s', regular_interval = '%s', number_intervals = %d WHERE pfid = %d",
$data['model'], $data['fee_amount'], $data['initial_charge'], $data['regular_interval'], $data['number_intervals'], $data['pfid']);
// Otherwise insert this feature as a new row.
if (db_affected_rows() == 0) {
db_query("INSERT INTO {uc_recurring_products} (pfid, model, fee_amount, initial_charge, regular_interval, number_intervals) VALUES (%d, '%s', %f, '%s', '%s', %d)",
$data['pfid'], $data['model'], $data['fee_amount'], $data['initial_charge'], $data['regular_interval'], $data['number_intervals']);
}
break;
case 'user':
// First attempt to update an existing row.
db_query("UPDATE {uc_recurring_users} SET uid = %d, fee_handler = '%s', next_charge = %d, fee_amount = %f, regular_interval = '%s', remaining_intervals = %d, charged_intervals = %d, order_id = %d, data = '%s' WHERE rfid = %d",
$data['uid'], $data['fee_handler'], $data['next_charge'], $data['fee_amount'], $data['regular_interval'], $data['remaining_intervals'], $data['charged_intervals'], $data['order_id'], $data['data'], $data['rfid']);
// Otherwise insert this feature as a new row.
if (db_affected_rows() == 0) {
db_query("INSERT INTO {uc_recurring_users} (rfid, uid, fee_handler, next_charge, fee_amount, regular_interval, remaining_intervals, charged_intervals, order_id, data, created) VALUES (%d, %d, '%s', %d, %f, '%s', %d, %d, %d, '%s', %d)",
$data['rfid'], $data['uid'], $data['fee_handler'], $data['next_charge'], $data['fee_amount'], $data['regular_interval'], $data['remaining_intervals'], $data['charged_intervals'], $data['order_id'], $data['data'], time());
}
break;
}
}
/**
* Loads a recurring fee either from a product or for a user.
*
* @param $type
* 'product' to load a recurring fee product feature.
* 'user' to load a recurring fee schedule for a user.
* @param $id
* The ID of the fee to load, either the product feature ID or the recurring
* fee ID from the appropriate table.
* @return
* An associative array of data for the specified fee.
*/
function uc_recurring_fee_load($type, $id) {
switch ($type) {
case 'product':
$fee = db_fetch_array(db_query("SELECT * FROM {uc_recurring_products} WHERE pfid = %d", $id));
if (!empty($fee)) {
list($fee['initial_charge_value'], $fee['initial_charge_unit']) = explode(' ', $fee['initial_charge']);
list($fee['regular_interval_value'], $fee['regular_interval_unit']) = explode(' ', $fee['regular_interval']);
}
break;
case 'user':
$fee = db_fetch_array(db_query("SELECT * FROM {uc_recurring_users} WHERE rfid = %d", $id));
if ($fee['fee_handler'] == 'uc_recurring') {
$fee['data'] = unserialize($fee['data']);
if ($key = uc_credit_encryption_key()) {
$crypt = new uc_encryption_class;
$fee['data']['payment_details']['cc_number'] = $crypt->decrypt($key, $fee['data']['payment_details']['cc_number']);
if (variable_get('uc_credit_debug', FALSE)) {
$fee['data']['payment_details']['cc_cvv'] = $crypt->decrypt($key, $fee['data']['payment_details']['cc_cvv']);
}
$fee['data']['payment_details']['cc_exp_month'] = $crypt->decrypt($key, $fee['data']['payment_details']['cc_exp_month']);
$fee['data']['payment_details']['cc_exp_year'] = $crypt->decrypt($key, $fee['data']['payment_details']['cc_exp_year']);
uc_store_encryption_errors($crypt, 'uc_recurring');
}
}
break;
}
return $fee;
}
/**
* Deletes a recurring fee from a product or user.
*
* @param $type
* Either 'product' or 'user' to specify what type of delete needs to happen.
* @param $id
* The ID of the recurring fee to be removed from the appropriate table.
*/
function uc_recurring_fee_delete($feature, $type = 'product') {
switch ($type) {
case 'product':
db_query("DELETE FROM {uc_recurring_products} WHERE pfid = %d", $feature['pfid']);
break;
case 'user':
module_invoke_all('recurring_api', 'delete', $feature['fid']);
db_query("DELETE FROM {uc_recurring_users} WHERE rfid = %d", $feature['pfid']);
break;
}
}
/**
* Cancels a user's recurring fee by setting remaining intervals to 0.
*
* @param $rfid
* The recurring fee's ID.
*/
function uc_recurring_fee_cancel($rfid) {
db_query("UPDATE {uc_recurring_users} SET remaining_intervals = 0 WHERE rfid = %d", $rfid);
}
/**
* Returns an array of recurring fees associated with any product on an order.
*
* @param $order
* The order object in question.
* @return
* An array of recurring fee objects containing all their data from the DB.
*/
function uc_recurring_find_fees($order) {
if (!is_array($order->products) || count($order->products) == 0) {
return array();
}
$models = array();
$nids = array();
foreach ((array) $order->products as $product) {
$nids[] = $product->nid;
$models[] = check_plain($product->model);
}
$fees = array();
$result = db_query("SELECT rp.*, nid FROM {uc_recurring_products} AS rp LEFT JOIN {uc_product_features} AS pf ON rp.pfid = pf.pfid WHERE rp.model IN ('". implode("', '", $models) ."') OR (rp.model = '' AND pf.nid IN ('". implode("', '", $nids) ."'))");
while ($fee = db_fetch_object($result)) {
$fees[] = $fee;
}
return $fees;
}
/**
* Passes the information onto the specified fee handler for processing.
*
* @param $order
* The order object the fees are attached to.
* @param $fee
* The fee object to be processed.
* @return
* TRUE or FALSE indicating whether or not the processing was successful.
*/
function uc_recurring_process($order, $fee) {
$handler = variable_get('uc_recurring_handler', 'uc_recurring') .'_recurring_fee';
if (!function_exists($handler)) {
drupal_set_message(t('The handler for processing recurring fees cannot be found.'), 'error');
return FALSE;
}
if ($handler($order, $fee) == TRUE) {
return TRUE;
}
return FALSE;
}
// Processes credit cards for the default handler.
function uc_recurring_charge($fee) {
static $show = TRUE;
// Get the charge function for the handler's credit card gateway.
$func = $fee['data']['charge_callback'];
// Whoa... bad function? ABORT! ABORT!
if (!function_exists($func)) {
if ($show) {
watchdog('uc_recurring', t('Recurring payments failed to process due to invalid credit card gateway.'), WATCHDOG_ERROR);
$show = FALSE;
}
return FALSE;
}
// Build the data array for the request.
$data = array(
'txn_type' => UC_CREDIT_REFERENCE_TXN,
'ref_id' => $fee['data']['ref_id'],
);
// Run the charge.
$result = $func($fee['order_id'], $fee['fee_amount'], $data);
// Handle the result.
if ($result['success'] === TRUE) {
uc_payment_enter($fee['order_id'], 'credit', $fee['fee_amount'], 0, $result['data'], t('Recurring fee payment.') .'
'. $result['comment']);
uc_order_comment_save($fee['order_id'], 0, t('!amount recurring fee collected for @model. (ID: !fee)', array('!url' => url('admin/store/orders/recurring/view/fee/'. $fee['rfid']), '!fee' => $fee['rfid'], '!amount' => uc_currency_format($fee['fee_amount']), '@model' => $fee['data']['model'])));
// Modules can hook into the charge process using hook_recurring_api().
module_invoke_all('recurring_api', 'charge', $fee);
// Provide a couple Workflow events for folks to hook into.
workflow_ng_invoke_event('fee_charge_successful', uc_order_load($fee['order_id']));
if ($fee['remaining_intervals'] == 1) {
workflow_ng_invoke_event('fee_expires', uc_order_load($fee['order_id']));
}
}
else {
uc_order_comment_save($fee['order_id'], 0, t('Error: Recurring fee !fee for product @model failed.', array('!url' => url('admin/store/orders/recurring/view/fee/'. $fee['rfid']), '!fee' => $fee['rfid'], '@model' => $fee['data']['model'])));
watchdog('uc_recurring', t('Failed to capture recurring fee of !amount for product @model on order !order_id.', array('!amount' => $fee['fee_amount'], '@model' => $fee['data']['model'], '!order_id' => $fee['order_id'])), WATCHDOG_ERROR, l(t('order !order_id', array('!order_id' => $fee['order_id'])), 'admin/store/orders/'. $fee['order_id']));
// Modules can hook into the charge process using hook_recurring_api().
module_invoke_all('recurring_api', 'fail', $fee);
// Provide a Workflow event for folks to hook into.
workflow_ng_invoke_event('fee_charge_fails', uc_order_load($fee['order_id']));
}
return $result['success'];
}