'admin/store/settings/payment',
'title' => t('Payment settings'),
'callback' => 'uc_payment_settings_overview',
'access' => user_access('administer store'),
'description' => t('Configure the payment settings.'),
);
$items[] = array(
'path' => 'admin/store/settings/payment/overview',
'title' => t('Overview'),
'access' => user_access('administer store'),
'description' => t('View the payment settings.'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[] = array(
'path' => 'admin/store/settings/payment/edit',
'title' => t('Edit'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_payment_settings_form'),
'access' => user_access('administer store'),
'description' => t('Edit the payment settings.'),
'type' => MENU_LOCAL_TASK,
'weight' => -5,
);
$items[] = array(
'path' => 'admin/store/settings/payment/edit/basic',
'title' => t('Payment settings'),
'access' => user_access('administer store'),
'description' => t('Edit the basic payment settings.'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[] = array(
'path' => 'admin/store/settings/payment/edit/methods',
'title' => t('Payment methods'),
'access' => user_access('administer store'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_payment_methods_form'),
'description' => t('Edit the payment method settings.'),
'type' => MENU_LOCAL_TASK,
'weight' => -5,
);
$items[] = array(
'path' => 'admin/store/settings/payment/edit/gateways',
'title' => t('Payment gateways'),
'access' => user_access('administer store'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_payment_gateways_form'),
'description' => t('Edit the payment gateway settings.'),
'type' => MENU_LOCAL_TASK,
'weight' => 0,
);
$items[] = array(
'path' => 'cart/checkout/line_items',
'title' => t('Return order totals'),
'callback' => 'uc_payment_get_totals',
'access' => user_access('access content'),
'type' => MENU_CALLBACK,
);
}
else {
$items[] = array(
'path' => 'cart/checkout/payment_details/'. arg(3),
'title' => t('Payment details'),
'description' => t('Add the payment details to the checkout pane.'),
'callback' => 'uc_payment_get_details',
'callback arguments' => array(arg(3)),
'access' => user_access('access content'),
'type' => MENU_CALLBACK,
);
if (is_numeric(arg(3))) {
$items[] = array(
'path' => 'admin/store/orders/'. arg(3) .'/payment_details/'. arg(5),
'title' => t('Payment details'),
'description' => t('Add the payment details to the order pane.'),
'callback' => 'uc_payment_get_details',
'callback arguments' => array(arg(5), 'order'),
'access' => user_access('edit orders'),
'type' => MENU_CALLBACK,
);
$items[] = array(
'path' => 'admin/store/orders/'. arg(3) .'/payments/select/'. arg(6),
'title' => t('Select payment gateway'),
'callback' => 'uc_payment_gateway_select',
'callback arguments' => array(arg(3), arg(6)),
'access' => user_access('view all orders'),
'type' => MENU_CALLBACK,
);
if (variable_get('uc_payment_tracking', TRUE)) {
$items[] = array(
'path' => 'admin/store/orders/'. arg(3) .'/payments',
'title' => t('Payments'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_payment_by_order_form', arg(3)),
'access' => user_access('view payments'),
'weight' => 5,
'type' => MENU_LOCAL_TASK
);
}
if (is_numeric(arg(5)) && arg(6) == 'delete' && variable_get('uc_payment_deleting', TRUE)) {
$items[] = array(
'path' => 'admin/store/orders/'. arg(3) .'/payments/'. arg(5) .'/delete',
'title' => t('Delete payment?'),
'description' => t('Delete payment?'),
'callback' => 'drupal_get_form',
'callback arguments' => array('uc_payment_delete_confirm_form', arg(3), arg(5)),
'access' => user_access('delete payments'),
'type' => MENU_CALLBACK,
);
}
}
}
return $items;
}
/**
* Implementation of hook_token_values().
*/
function uc_payment_token_values($type, $object = NULL) {
switch ($type) {
case 'order':
$order = $object;
$values['order-payment-method'] = _payment_method_data($order->payment_method, 'review');
if (empty($values['order-payment-method'])) {
$values['order-payment-method'] = _payment_method_data($order->payment_method, 'name');
}
$values['order-payment-balance'] = uc_currency_format(uc_payment_balance($order));
break;
}
return $values;
}
/**
* Implementation of hook_token_list(). (token.module)
*/
function uc_payment_token_list($type = 'all') {
if ($type == 'order' || $type == 'ubercart' || $type == 'all') {
$tokens['order']['order-payment-method'] = t('The payment method of the order.');
$tokens['order']['order-payment-balance'] = t('The payment balance of the order');
}
return $tokens;
}
/**
* Implementation of hook_perm().
*/
function uc_payment_perm() {
return array('view payments', 'manual payments', 'delete payments');
}
/**
* Implementation of hook_form_alter().
*/
function uc_payment_form_alter($form_id, &$form) {
if ($form_id == 'uc_cart_checkout_form') {
uc_add_js('misc/progress.js');
uc_add_js(drupal_get_path('module', 'uc_payment') .'/uc_payment.js');
}
}
/*******************************************************************************
* Hook Functions (TAPIr)
******************************************************************************/
/**
* Implementation of hook_table_settings().
*/
function uc_payment_table_settings() {
$tables[] = array(
'id' => 'uc_payments_table',
'description' => t('The payments table on the order payments screen.'),
'path' => 'admin/store/settings/tables',
'access' => 'view all orders',
'preview' => FALSE,
);
return $tables;
}
/*******************************************************************************
* Hook Functions (Ubercart)
******************************************************************************/
/**
* Implementation of hook_order().
*/
function uc_payment_order($op, &$arg1) {
switch ($op) {
case 'submit':
$func = _payment_method_data($arg1->payment_method, 'callback');
if (function_exists($func)) {
return $func('order-submit', $arg1);
}
break;
case 'load':
$func = _payment_method_data($arg1->payment_method, 'callback');
if (function_exists($func)) {
$func('order-load', $arg1);
}
break;
case 'save':
$func = _payment_method_data($arg1->payment_method, 'callback');
if (function_exists($func)) {
$func('order-save', $arg1);
}
break;
case 'can_delete':
if (uc_payment_load_payments($arg1->order_id) !== FALSE) {
return FALSE;
}
break;
case 'delete':
db_query("DELETE FROM {uc_payment_receipts} WHERE order_id = %d", $arg1->order_id);
// Call each payment method to delete method specific data from the database.
$methods = _payment_method_list();
foreach ($methods as $method) {
$func = $method['callback'];
if (function_exists($func)) {
$func('order-delete', $arg1);
}
}
break;
}
}
/**
* Implementation of hook_checkout_pane().
*/
function uc_payment_checkout_pane() {
$panes[] = array(
'id' => 'payment',
'title' => t('Payment method'),
'desc' => t('Select a payment method from the enabled payment modules.'),
'callback' => 'uc_checkout_pane_payment',
'weight' => 6,
);
return $panes;
}
/**
* Implementation of hook_order_pane().
*/
function uc_payment_order_pane() {
$panes[] = array(
'id' => 'payment',
'callback' => 'uc_order_pane_payment',
'title' => t('Payment'),
'desc' => t('Specify and collect payment for an order.'),
'class' => 'pos-left',
'weight' => 4,
'show' => array('view', 'edit', 'customer'), //, 'invoice', 'customer'),
);
return $panes;
}
/**
* Implementation of hook_order_state().
*/
function uc_payment_order_state() {
$states[] = array(
'id' => 'payment_received',
'title' => t('Payment received'),
'weight' => 10,
'scope' => 'general',
);
return $states;
}
/*******************************************************************************
* Callback Functions, Forms, and Tables
******************************************************************************/
/**
* Display the payment settings overview.
*/
function uc_payment_settings_overview() {
$sections[] = array(
'edit' => 'admin/store/settings/payment/edit',
'title' => t('Payment settings'),
'items' => array(
t('Payment tracking is !status.', array('!status' => variable_get('uc_payment_tracking', TRUE) ? t('enabled') : t('disabled'))),
t('Payments !status be deleted by approved users.', array('!status' => variable_get('uc_payment_deleting', TRUE) ? t('may') : t('may not'))),
t('Payments are !status in the order logs.', array('!status' => variable_get('uc_payment_logging', TRUE) ? t('tracked') : t('not tracked'))),
t('Default payment details message:
%message', array('%message' => variable_get('uc_default_payment_msg', t('Continue with checkout to complete payment.')))),
),
);
$methods = _payment_method_list();
foreach ($methods as $method) {
$items[] = t('!title is !status for checkout.', array('!title' => $method['name'], '!status' => $method['checkout'] ? t('enabled') : t('disabled')));
}
$sections[] = array(
'edit' => 'admin/store/settings/payment/edit/methods',
'title' => t('Payment methods'),
'items' => $items,
);
$items = array();
$gateways = _payment_gateway_list();
foreach ($gateways as $gateway) {
$items[] = t('!title is !status.', array('!title' => $gateway['title'], '!status' => $gateway['enabled'] ? t('enabled') : t('disabled')));
}
$sections[] = array(
'edit' => 'admin/store/settings/payment/edit/gateways',
'title' => t('Payment gateways'),
'items' => $items,
);
$output = theme('uc_settings_overview', $sections);
return $output;
}
function uc_payment_settings_form() {
$form['uc_payment_tracking'] = array(
'#type' => 'checkbox',
'#title' => t('Enable payment tracking.'),
'#default_value' => variable_get('uc_payment_tracking', TRUE),
);
$form['uc_payment_deleting'] = array(
'#type' => 'checkbox',
'#title' => t('Allow payments to be deleted by users with permission.'),
'#default_value' => variable_get('uc_payment_deleting', TRUE),
);
$form['uc_payment_logging'] = array(
'#type' => 'checkbox',
'#title' => t('Log payments entered and deleted to order log.'),
'#default_value' => variable_get('uc_payment_logging', TRUE),
);
$form['uc_default_payment_msg'] = array(
'#type' => 'textfield',
'#title' => t('Default payment details message'),
'#description' => t('Message displayed when a payment method does not display any further details.'),
'#default_value' => variable_get('uc_default_payment_msg', t('Continue with checkout to complete payment.')),
);
return system_settings_form($form);
}
function uc_payment_methods_form() {
$methods = _payment_method_list();
$form['methods_info'] = array(
'#value' => '
'. $line[2] .': | ' .''. uc_currency_format($line[1]) .' |
'. t('Order total:') .' | ' .''. uc_currency_format($grand_total) .' |
'. t('Order total:') .' '. drupal_render($form['order_total'])
.'
'. t('Current balance:') .' '
. drupal_render($form['balance']) .'
'. tapir_get_table('uc_payments_table', $form) .'
' . ''. drupal_render($form['form_id']) . drupal_render($form['form_token']) .'
'; return $output; } function uc_payments_table($op, $form) { switch ($op) { case 'fields': $fields[] = array('name' => 'received', 'title' => t('Received'), 'weight' => 0, 'enabled' => TRUE); $fields[] = array('name' => 'user', 'title' => t('User'), 'weight' => 1, 'enabled' => TRUE); $fields[] = array('name' => 'method', 'title' => t('Method'), 'weight' => 2, 'enabled' => TRUE); $fields[] = array('name' => 'amount', 'title' => t('Amount'), 'weight' => 3, 'enabled' => TRUE); $fields[] = array('name' => 'balance', 'title' => t('Balance'), 'weight' => 4, 'enabled' => TRUE); $fields[] = array('name' => 'comment', 'title' => t('Comment'), 'weight' => 5, 'enabled' => TRUE); $fields[] = array('name' => 'action', 'title' => t('Action'), 'weight' => 6, 'enabled' => TRUE); return $fields; case 'data': foreach (element_children($form['payments']) as $i) { $data['#attributes'][] = array('valign' => 'top'); $data['received'][] = drupal_render($form['payments'][$i]['received']); $data['user'][] = drupal_render($form['payments'][$i]['user']); $data['method'][] = drupal_render($form['payments'][$i]['method']); $data['amount'][] = drupal_render($form['payments'][$i]['amount']); $data['balance'][] = drupal_render($form['payments'][$i]['balance']); $data['comment'][] = drupal_render($form['payments'][$i]['comment']); $data['action'][] = drupal_render($form['payments'][$i]['action']); } return $data; } } // Confirmation form to delete a payment from an order. function uc_payment_delete_confirm_form($order_id, $receipt_id) { // Attempt to load the payment. $payment = uc_payment_load($receipt_id); // Get outta here if the payment doesn't exist. if ($payment->order_id != $order_id) { drupal_set_message(t('An error loading the payment information occurred.')); drupal_goto('admin/store/orders/'. $order_id .'/payments'); } $desc = ''. t('Payment information:') .' ' . t('@method payment of @amount received on @date.', array('@method' => $payment->method, '@amount' => uc_currency_format($payment->amount), '@date' => format_date($payment->received, 'short'))); $form['order_id'] = array( '#type' => 'value', '#value' => $order_id ); $form['receipt_id'] = array( '#type' => 'value', '#value' => $receipt_id, ); return confirm_form($form, t('Are you sure you want to delete this payment?'), 'admin/store/orders/'. $order_id .'/payments', $desc, t('Delete')); } function uc_payment_delete_confirm_form_submit($form_id, $form_values) { uc_payment_delete($form_values['receipt_id']); drupal_set_message(t('Payment deleted.')); return 'admin/store/orders/'. $form_values['order_id'] .'/payments'; } /** * Select a payment gateway to process a payment when multiple gateways * exist for a given payment method. */ function uc_payment_gateway_select($url_order_id) { $gateways = _payment_gateway_list($_SESSION['uc_payment_method'], TRUE); foreach ($gateways as $gateway) { $options[$gateway['id']] = $gateway['title']; } $output = t('Please choose a payment gateway to use for that payment.'); $output .= drupal_get_form('uc_payment_gateway_select_form', $options, $_SESSION['uc_payment_method'], $_SESSION['uc_payment_order_id'], $_SESSION['uc_payment_amount'], $_SESSION['uc_payment_data']); return $output; } function uc_payment_gateway_select_form($options, $method, $order_id, $amount, $data) { $form['method'] = array( '#type' => 'hidden', '#value' => $method, ); $form['order_id'] = array( '#type' => 'hidden', '#value' => $order_id, ); $form['amount'] = array( '#type' => 'hidden', '#value' => $amount, ); $form['p_data'] = array( '#type' => 'hidden', '#value' => $data, ); $form['p_selected'] = array( '#type' => 'select', '#title' => t('Use gateway'), '#options' => $options, '#default_value' => variable_get('uc_payment_'. $method .'_gateway', ''), ); $form['submit'] = array( '#type' => 'submit', '#value' => t('Process'), ); return $form; } function uc_payment_gateway_select_form_submit($form_id, $form_values) { unset($_SESSION['uc_payment_method']); unset($_SESSION['uc_payment_order_id']); unset($_SESSION['uc_payment_amount']); unset($_SESSION['uc_payment_data']); uc_payment_process($form_values['method'], $form_values['order_id'], $form_values['amount'], unserialize($form_values['p_data']), FALSE, $form_values['p_selected']); drupal_goto('admin/store/orders/'. $form_values['order_id']); } /******************************************************************************* * Module and Helper Functions ******************************************************************************/ /** * Process a payment through an enabled payment gateway. * * @param $method * The ID of the payment method to use to process the payment. * @param $order_id * The ID of the order associated with this payment. * @param $amount * The amount of the payment we're attempting to collect. * @param $data * An array of data passed on to the payment gateway module used to process * the payment for the specified payment method. * @param $default * TRUE or FALSE to indicate we're forcing the use of the default gateway for * the specified payment method. When TRUE, admin messages related to the * payment will be hidden from display so customers don't see them. * @param $selected * The ID of a payment gateway to use to process the payment; normally comes * from the payment gateway select form. * @param $redirect * TRUE or FALSE to indicate whether or not to redirect back to the admin * order view page for the order referenced in $order_id. * @return * TRUE or FALSE indicating whether or not the payment was processed. */ function uc_payment_process($method, $order_id, $amount, $data = NULL, $default = FALSE, $selected = NULL, $redirect = TRUE) { $result = array(); // Get an array of enabled payment gateways available for the payment method. $gateways = _payment_gateway_list($method, TRUE); // Fail if no gateways were found for the specified method. if (empty($gateways)) { // Display an error message if messages weren't silenced. if (!$default) { drupal_set_message(t('You are not able to process %type payments.', array('%type' => _payment_method_data($method, 'name')))); } return FALSE; } // If we only found one gateway for this payment method... if (count($gateways) == 1) { // Get the right key for the payment gateway in the array. $key = array_shift(array_keys($gateways)); // If we can find a callback in the gateway for the payment method... if (function_exists($gateways[$key][$method])) { // Pass the payment data onto the callback and store the result. $result = $gateways[$key][$method]($order_id, $amount, $data); } else { // Otherwise display a failure message to administrators. if (user_access('administer store')) { drupal_set_message(t("Attempted to process a %type payment but the gateway's function was not found.")); } $result['success'] = FALSE; } } else { // Otherwise attempt to find the appropriate gateway function in the array. $callback = FALSE; foreach ($gateways as $gateway) { // If we want the default gateway and this is it, store the callback // and continue. if ($default && $gateway['id'] == variable_get('uc_payment_'. $method .'_gateway', '')) { $callback = $gateway[$method]; continue; } // If we want to use a specific gateway and this is it, store the callback. if (!empty($selected) && $gateway['id'] == $selected) { $callback = $gateway[$method]; } } // If we found a callback... if ($callback !== FALSE) { // Check to see if the function exists and process the payment. if (function_exists($callback)) { $result = $callback($order_id, $amount, $data); } else { // Otherwise display an error message to administrators. if (user_access('administer store')) { drupal_set_message(t('An error has occurred with your payment gateway. The charge function could not be found.')); } $result['success'] = FALSE; } } else { // Otherwise store the info that was passed to us in the session and // redirect to a form where we can choose a payment gateway. $_SESSION['uc_payment_method'] = $method; $_SESSION['uc_payment_order_id'] = $order_id; $_SESSION['uc_payment_amount'] = $amount; $_SESSION['uc_payment_data'] = serialize($data); drupal_goto('admin/store/orders/'. $order_id .'/payments/select/'. $method); } } // If the payment processed successfully... if ($result['success'] === TRUE) { // Log the payment to the order if not disabled. if ($result['log_payment'] !== FALSE) { uc_payment_enter($order_id, $method, $amount, empty($result['uid']) ? 0 : $result['uid'], $result['data'], $result['comment']); } } else { // Otherwise display the failue message in the logs. watchdog('uc_payment', t('Payment failed: @message', array('@message' => $result['message'])), WATCHDOG_WARNING); } // If we have a message for display and aren't simply charging with the // default gateway for a customer... if (!empty($result['message']) && !$default) { drupal_set_message($result['message']); } // Head back to the order if a redirect was specified. if ($redirect) { drupal_goto('admin/store/orders/'. $order_id); } return $result['success']; } /** * Enter a payment for an order. */ function uc_payment_enter($order_id, $method, $amount, $uid, $data, $comment) { $method_name = _payment_method_data($method, 'review'); if (empty($method_name)) { $method_name = _payment_method_data($method, 'name'); } if (is_null($method_name)) { $method_name = t('Other'); } if (is_array($data)) { $data = serialize($data); } if (variable_get('uc_payment_logging', TRUE)) { global $user; $log_message = t('@method payment for @amount entered by @user.', array('@method' => $method_name, '@amount' => uc_currency_format($amount), '@user' => uc_get_initials($user->uid))); uc_order_log_changes($order_id, array($log_message)); } db_query("INSERT INTO {uc_payment_receipts} (receipt_id, order_id, method, amount, uid, data, comment, received) VALUES (%d, %d, '%s', %f, %d, '%s', '%s', %d)", db_next_id('{uc_payment_receipts}_receipt_id'), $order_id, $method_name, $amount, $uid, $data, $comment, time()); if (module_exists('workflow_ng')) { workflow_ng_invoke_event('payment_entered', uc_order_load($order_id)); } } /** * Delete a payment from the database. */ function uc_payment_delete($receipt_id) { if (!is_numeric($receipt_id)) { return FALSE; } if (variable_get('uc_payment_logging', TRUE)) { global $user; $payment = uc_payment_load($receipt_id); $log_message = t('!method payment for !amount deleted by !user.', array('!method' => $payment->method, '!amount' => uc_currency_format($payment->amount), '!user' => uc_get_initials($user->uid))); uc_order_log_changes($payment->order_id, array($log_message)); } db_query("DELETE FROM {uc_payment_receipts} WHERE receipt_id = %d", $receipt_id); } /** * Return the balance of payments on an order. */ function uc_payment_balance($order) { $total = $order->order_total; $payments = uc_payment_load_payments($order->order_id); if ($payments === FALSE) { return $total; } foreach ($payments as $payment) { $total -= $payment->amount; } return $total; } /** * Load a single payment from the database by receipt_id. */ function uc_payment_load($receipt_id) { if (!is_numeric($receipt_id)) { return FALSE; } $result = db_query("SELECT * FROM {uc_payment_receipts} WHERE receipt_id = %d ", $receipt_id); $payment = db_fetch_object($result); return $payment; } /** * Load an array of all the payments for order $order_id. */ function uc_payment_load_payments($order_id, $action = NULL) { $payments = array(); $result = db_query("SELECT * FROM {uc_payment_receipts} WHERE order_id = %d " ."ORDER BY received ASC", $order_id); while ($payment = db_fetch_object($result)) { $payments[] = $payment; } if (count($payments) == 0) { $payments = FALSE; } return $payments; } /** * Build a list of payment methods defined in the enabled modules. */ function _payment_method_list($action = NULL) { static $methods; if (count($methods) > 0 && $action !== 'rebuild') { return $methods; } $methods = module_invoke_all('payment_method'); foreach ($methods as $i => $value) { $methods[$i]['checkout'] = variable_get('uc_payment_method_'. $methods[$i]['id'] .'_checkout', $methods[$i]['checkout']); $methods[$i]['weight'] = variable_get('uc_payment_method_'. $methods[$i]['id'] .'_weight', $methods[$i]['weight']); } usort($methods, 'uc_weight_sort'); return $methods; } /** * Return data from a payment method by method ID and the array key. */ function _payment_method_data($method_id, $key) { $methods = _payment_method_list(); foreach ($methods as $method) { if ($method['id'] == $method_id) { return $method[$key]; } } } /** * Build a list of payment gateways defined in the enabled modules. */ function _payment_gateway_list($filter = NULL, $enabled_only = FALSE) { $gateways = module_invoke_all('payment_gateway'); foreach ($gateways as $i => $value) { $gateways[$i]['enabled'] = variable_get('uc_pg_'. $gateways[$i]['id'] .'_enabled', TRUE); if ($filter != NULL) { if (!isset($gateways[$i][$filter]) || !function_exists($gateways[$i][$filter])) { unset($gateways[$i]); } } if ($enabled_only) { if (!variable_get('uc_pg_'. $gateways[$i]['id'] .'_enabled', TRUE)) { unset($gateways[$i]); } } } return $gateways; } /** * Return data from a payment gateway by gateway ID and the array key. * * @param $gateway_id * The ID of the payment gateway to query. * @param $key * The key of the data being requested. * @return * The requested data. */ function _payment_gateway_data($gateway_id, $key) { // Load all the payment gateways. $gateways = _payment_gateway_list(); // Loop through the array to find the matching gateway. foreach ($gateways as $gateway) { // If this is it, return the requested data. if ($gateway['id'] == $gateway_id) { return $gateway[$key]; } } }