'Payment settings', 'description' => 'Configure the payment settings.', 'page callback' => 'uc_payment_settings_overview', 'access arguments' => array('administer store'), 'file' => 'uc_payment.admin.inc', ); $items['admin/store/settings/payment/overview'] = array( 'title' => 'Overview', 'description' => 'View the payment settings.', 'access arguments' => array('administer store'), 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/store/settings/payment/edit'] = array( 'title' => 'Edit', 'description' => 'Edit the payment settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_payment_settings_form'), 'access arguments' => array('administer store'), 'weight' => -5, 'type' => MENU_LOCAL_TASK, 'file' => 'uc_payment.admin.inc', ); $items['admin/store/settings/payment/edit/basic'] = array( 'title' => 'Payment settings', 'description' => 'Edit the basic payment settings.', 'access arguments' => array('administer store'), 'weight' => -10, 'type' => MENU_DEFAULT_LOCAL_TASK, ); $items['admin/store/settings/payment/edit/methods'] = array( 'title' => 'Payment methods', 'description' => 'Edit the payment method settings.', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_payment_methods_form'), 'access arguments' => array('administer store'), 'weight' => -5, 'type' => MENU_LOCAL_TASK, 'file' => 'uc_payment.admin.inc', ); $items['admin/store/settings/payment/edit/gateways'] = array( 'title' => 'Payment gateways', 'description' => 'Edit the payment gateway settings.', 'access arguments' => array('administer store'), 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_payment_gateways_form'), 'weight' => 0, 'type' => MENU_LOCAL_TASK, 'file' => 'uc_payment.admin.inc', ); $items['cart/checkout/line_items'] = array( 'title' => 'Return order totals', 'page callback' => 'uc_payment_get_totals', 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); $items['cart/checkout/payment_details/%'] = array( 'title' => 'Payment details', 'description' => 'Add the payment details to the checkout pane.', 'page callback' => 'uc_payment_get_details', 'page arguments' => array(3), 'access arguments' => array('access content'), 'type' => MENU_CALLBACK, ); $items['admin/store/orders/%uc_order/payment_details/%'] = array( 'title' => 'Payment details', 'description' => 'Add the payment details to the order pane.', 'page callback' => 'uc_payment_get_details', 'page arguments' => array(5, 'order', 3), 'access arguments' => array('edit orders'), 'type' => MENU_CALLBACK, ); $items['admin/store/orders/%uc_order/payments/select/%'] = array( 'title' => 'Select payment gateway', 'page callback' => 'uc_payment_gateway_select', 'access arguments' => array('view all orders'), 'type' => MENU_CALLBACK, 'file' => 'uc_payment.admin.inc', ); $items['admin/store/orders/%uc_order/payments'] = array( 'title' => 'Payments', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_payment_by_order_form', 3), 'access callback' => 'uc_payment_tracking_access', 'weight' => 5, 'type' => MENU_LOCAL_TASK, 'file' => 'uc_payment.admin.inc', ); $items['admin/store/orders/%uc_order/payments/%uc_payment/delete'] = array( 'title' => 'Delete payment?', 'description' => 'Delete payment?', 'page callback' => 'drupal_get_form', 'page arguments' => array('uc_payment_delete_confirm_form', 3, 5), 'access callback' => 'uc_payment_delete_access', 'type' => MENU_CALLBACK, 'file' => 'uc_payment.admin.inc', ); return $items; } /** * Implementation of hook_token_values(). */ function uc_payment_token_values($type, $object = NULL) { $values = array(); 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'); } $context = array( 'revision' => 'formatted', 'type' => 'amount', ); $values['order-payment-balance'] = uc_price(uc_payment_balance($order), $context); break; } return $values; } /** * Implementation of hook_token_list(). (token.module) */ function uc_payment_token_list($type = 'all') { $tokens = array(); 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'); } /** * Access callback to view list of payments. */ function uc_payment_tracking_access() { return user_access('view payments') && variable_get('uc_payment_tracking', TRUE); } /** * Access callback to delete a payment. */ function uc_payment_delete_access() { return user_access('delete payments') && variable_get('uc_payment_deleting', TRUE); } /** * Implementation of hook_theme(). */ function uc_payment_theme() { return array( 'uc_payment_method_table' => array( 'arguments' => array('form' => NULL), 'file' => 'uc_payment.admin.inc', ), 'uc_payment_by_order_form' => array( 'arguments' => array('form' => NULL), 'file' => 'uc_payment.admin.inc', ), 'uc_payment_method_select' => array( 'arguments' => array('form' => NULL), ), ); } /** * Implementation of hook_init(). */ function uc_payment_init() { global $conf; $conf['i18n_variables'][] = 'uc_default_payment_msg'; } /** * Implementation of hook_form_alter(). */ function uc_payment_form_alter(&$form, $form_state, $form_id) { if ($form_id == 'uc_cart_checkout_form') { drupal_add_js('misc/progress.js'); drupal_add_js(drupal_get_path('module', 'uc_payment') .'/uc_payment.js'); } } /******************************************************************************* * 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 ******************************************************************************/ /** * Return a formatted list of line items for an order total preview. * * @param $return * TRUE or FALSE to specify whether or not to return the results instead of * printing them and exiting. * @param $order * Optionally pass in a full order object to use instead of finding it in the * $_POST data. * @return * The formatted HTML of the order total preview if $return is set to TRUE. */ function uc_payment_get_totals($return = FALSE, $order = NULL) { $output = ''; if (empty($order) && is_array($_POST) && isset($_POST['order'])) { $order = unserialize($_POST['order']); } if ($order) { usort($order->line_items, 'uc_weight_sort'); $output = t('Order total preview:') .' '; $grand_total = 0; $context = array( 'type' => 'line_item', 'subject' => array( 'order' => $order, ), ); foreach ($order->line_items as $line) { if (!empty($line['title'])) { $context['revision'] = 'themed'; $context['subject']['line_item'] = $line; $output .= '' .''; if ($line['summed']) { $context['revision'] = 'altered'; } } } $context['revision'] = 'themed'; $context['type'] = 'amount'; unset($context['subject']); $output .= '' .'
'. filter_xss($line['title']) .':'. uc_price($line['amount'], $context) .'
'. t('Order total:') .''. uc_price(uc_order_get_total($order), $context) .'
'; } if ($return) { return $output; } print $output; exit(); } function uc_payment_get_details($method_id, $view = 'cart', $order = NULL) { if ($view == 'cart') { if (!($order = uc_order_load($_SESSION['cart_order']))) { $_SESSION['cart_order'] = NULL; $order = NULL; } if ($order->order_status != 0 || ($user->uid && $user->uid != $order->uid)) { $order = NULL; } } $func = _payment_method_data($method_id, 'callback'); if (function_exists($func)) { $output = $func($view .'-details', $order); } print $output; exit(); } function _total_sort($a, $b) { if ($a[0] == $b[0]) { return 0; } return ($a[0] < $b[0]) ? -1 : 1; } function uc_payments_table() { $table = array( '#type' => 'tapir_table', '#tree' => TRUE, '#columns' => array( 'received' => array( 'cell' => t('Received'), 'weight' => 0, ), 'user' => array( 'cell' => t('User'), 'weight' => 1, ), 'method' => array( 'cell' => t('Method'), 'weight' => 2, ), 'amount' => array( 'cell' => t('Amount'), 'weight' => 3, ), 'balance' => array( 'cell' => t('Balance'), 'weight' => 4, ), 'comment' => array( 'cell' => t('Comment'), 'weight' => 5, ), 'action' => array( 'cell' => t('Action'), 'weight' => 6, ), ), '#rows' => array(), ); return $table; } /******************************************************************************* * 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', 'Payment failed for order @order_id: @message', array('@order_id' => $order_id, '@message' => $result['message']), WATCHDOG_WARNING, l(t('view order'), 'admin/store/orders/'. $order_id)); } // 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; $context = array( 'revision' => 'formatted', 'type' => 'amount', ); $log_message = t('@method payment for @amount entered by @user.', array('@method' => $method_name, '@amount' => uc_price($amount, $context), '@user' => uc_get_initials($user->uid))); uc_order_log_changes($order_id, array($log_message)); } db_query("INSERT INTO {uc_payment_receipts} (order_id, method, amount, uid, data, comment, received) VALUES (%d, '%s', %f, %d, '%s', '%s', %d)", $order_id, $method_name, $amount, $uid, $data, $comment, time()); $order = uc_order_load($order_id); $account = user_load($uid); module_invoke_all('uc_payment_entered', $order, $method, $amount, $account, $data, $comment); ca_pull_trigger('uc_payment_entered', $order, $account); } /** * 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); $context = array( 'revision' => 'formatted', 'type' => 'payment', 'subject' => array( 'payment' => $payment, ), ); $log_message = t('@method payment for @amount deleted by @user.', array('@method' => $payment->method, '@amount' => uc_price($payment->amount, $context), '@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 an order. * * @param $order_id * The order's id. * @param $action * Unused... * @return * Array of payment objects or FALSE if there are none. */ 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]; } } }