commerce_payment.module in Commerce Core 7
Same filename and directory in other branches
Defines the payment system and checkout integration.
File
modules/payment/commerce_payment.moduleView source
<?php
/**
* @file
* Defines the payment system and checkout integration.
*/
// Local payment transaction status definitions:
// Pending is used when a transaction has been initialized but is still awaiting
// resolution; e.g. a CC authorization awaiting capture or an e-check payment
// pending at the payment provider.
define('COMMERCE_PAYMENT_STATUS_PENDING', 'pending');
// Success is used when a transaction has completed resulting in money being
// transferred from the customer to the store or vice versa.
define('COMMERCE_PAYMENT_STATUS_SUCCESS', 'success');
// Failure is used when a transaction cannot be completed or is rejected.
define('COMMERCE_PAYMENT_STATUS_FAILURE', 'failure');
// Credit card transaction types definitions:
// Used to just authorize an amount on a credit card account.
define('COMMERCE_CREDIT_AUTH_ONLY', 'authorize');
// User to just capture an amount on a credit card account.
define('COMMERCE_CREDIT_CAPTURE_ONLY', 'capture');
// Used to capture funds from a prior authorization.
define('COMMERCE_CREDIT_PRIOR_AUTH_CAPTURE', 'prior_auth_capture');
// Used to authorize and capture money all at once.
define('COMMERCE_CREDIT_AUTH_CAPTURE', 'auth_capture');
// Used to set up a credit card reference through the payment gateway.
define('COMMERCE_CREDIT_REFERENCE_SET', 'reference_set');
// Used to capture funds using a credit card reference.
define('COMMERCE_CREDIT_REFERENCE_TXN', 'reference_txn');
// Used to remove a reference from the payment gateway.
define('COMMERCE_CREDIT_REFERENCE_REMOVE', 'reference_remove');
// Used to credit funds to a reference at the payment gateway.
define('COMMERCE_CREDIT_REFERENCE_CREDIT', 'reference_credit');
// Used to credit funds to a credit card account.
define('COMMERCE_CREDIT_CREDIT', 'credit');
// Used to void a transaction before the transaction clears.
define('COMMERCE_CREDIT_VOID', 'void');
/**
* Implements of hook_entity_info().
*/
function commerce_payment_entity_info() {
$return = array(
'commerce_payment_transaction' => array(
'label' => t('Commerce Payment transaction'),
'controller class' => 'CommercePaymentTransactionEntityController',
'base table' => 'commerce_payment_transaction',
'revision table' => 'commerce_payment_transaction_revision',
'fieldable' => FALSE,
'entity keys' => array(
'id' => 'transaction_id',
'revision' => 'revision_id',
'bundle' => 'payment_method',
'label' => 'transaction_id',
),
'bundle keys' => array(
'bundle' => 'payment_method',
),
'bundles' => array(),
'load hook' => 'commerce_payment_transaction_load',
'view modes' => array(
'administrator' => array(
'label' => t('Administrator'),
'custom settings' => FALSE,
),
),
'uri callback' => 'commerce_payment_transaction_uri',
'access callback' => 'commerce_payment_transaction_access',
'access arguments' => array(
'user key' => 'uid',
'access tag' => 'commerce_payment_transaction_access',
),
'token type' => 'commerce-payment-transaction',
'metadata controller class' => '',
'permission labels' => array(
'singular' => t('payment transaction'),
'plural' => t('payment transactions'),
),
// Prevent Redirect alteration of the payment transaction form.
'redirect' => FALSE,
),
);
foreach (commerce_payment_methods() as $method_id => $payment_method) {
$return['commerce_payment_transaction']['bundles'][$method_id] = array(
'label' => $payment_method['title'],
);
}
return $return;
}
/**
* Entity uri callback: gives modules a chance to specify a path for a payment
* transaction.
*/
function commerce_payment_transaction_uri($transaction) {
// Allow modules to specify a path, returning the first one found.
foreach (module_implements('commerce_payment_transaction_uri') as $module) {
$uri = module_invoke($module, 'commerce_payment_transaction_uri', $transaction);
// If the implementation returned data, use that now.
if (!empty($uri)) {
return $uri;
}
}
return NULL;
}
/**
* Implements hook_hook_info().
*/
function commerce_payment_hook_info() {
$hooks = array(
'commerce_payment_totals_row_info' => array(
'group' => 'commerce',
),
'commerce_payment_totals_row_info_alter' => array(
'group' => 'commerce',
),
'commerce_payment_method_info' => array(
'group' => 'commerce',
),
'commerce_payment_method_info_alter' => array(
'group' => 'commerce',
),
'commerce_payment_methods' => array(
'group' => 'commerce',
),
'commerce_payment_transaction_status_info' => array(
'group' => 'commerce',
),
'commerce_payment_transaction_uri' => array(
'group' => 'commerce',
),
'commerce_transaction_view' => array(
'group' => 'commerce',
),
'commerce_payment_transaction_access' => array(
'group' => 'commerce',
),
'commerce_payment_transaction_insert' => array(
'group' => 'commerce',
),
'commerce_payment_transaction_update' => array(
'group' => 'commerce',
),
'commerce_payment_transaction_delete' => array(
'group' => 'commerce',
),
'commerce_payment_order_paid_in_full' => array(
'group' => 'commerce',
),
);
return $hooks;
}
/**
* Implements hook_permission().
*/
function commerce_payment_permission() {
return array(
'administer payment methods' => array(
'title' => t('Administer payment methods'),
'description' => t('Allows users to configure enabled payment methods.'),
'restrict access' => TRUE,
),
'administer payments' => array(
'title' => t('Administer payments'),
'description' => t('Allows users to perform any payment action for any order and view transaction payloads.'),
'restrict access' => TRUE,
),
'view payments' => array(
'title' => t('View payments'),
'description' => t('Allows users to view the payments made to an order.'),
'restrict access' => TRUE,
),
'create payments' => array(
'title' => t('Create payments'),
'description' => t('Allows users to create new payments for orders.'),
'restrict access' => TRUE,
),
'update payments' => array(
'title' => t('Update payments'),
'description' => t('Allows users to update payments via payment method specific operations links.'),
'restrict access' => TRUE,
),
'delete payments' => array(
'title' => t('Delete payments'),
'description' => t('Allows users to delete payments on orders they can access.'),
'restrict access' => TRUE,
),
);
}
/**
* Implements hook_theme().
*/
function commerce_payment_theme() {
return array(
'commerce_payment_transaction' => array(
'variables' => array(
'order' => NULL,
'transaction' => NULL,
'view_mode' => NULL,
),
),
'commerce_payment_transaction_status_text' => array(
'variables' => array(
'text' => NULL,
'transaction_status' => NULL,
),
),
'commerce_payment_transaction_status_icon' => array(
'variables' => array(
'transaction_status' => NULL,
),
),
'commerce_payment_totals' => array(
'variables' => array(
'rows' => array(),
'form' => NULL,
'totals' => array(),
'view' => NULL,
),
'path' => drupal_get_path('module', 'commerce_payment') . '/theme',
'template' => 'commerce-payment-totals',
),
);
}
/**
* Adds the necessary CSS for the line item summary template.
*/
function template_preprocess_commerce_payment_totals(&$variables) {
drupal_add_css(drupal_get_path('module', 'commerce_payment') . '/theme/commerce_payment.admin.css');
}
/**
* Implements hook_modules_enabled().
*/
function commerce_payment_modules_enabled($modules) {
commerce_payment_methods_reset();
_commerce_payment_default_rules_reset($modules);
}
/**
* Resets default Rules if necessary when modules are enabled or disabled.
*
* @param $modules
* An array of module names that have been enabled or disabled.
*/
function _commerce_payment_default_rules_reset($modules) {
$reset_default_rules = FALSE;
// Look for any module defining a new payment method.
foreach ($modules as $module) {
if (function_exists($module . '_commerce_payment_method_info')) {
$reset_default_rules = TRUE;
}
}
// If we found a module defining a new payment method, we need to rebuild the
// default Rules especially for this module so the default payment method Rule
// will appear properly for this module.
if ($reset_default_rules) {
entity_defaults_rebuild();
rules_clear_cache(TRUE);
}
}
/**
* Implements hook_commerce_checkout_page_info().
*/
function commerce_payment_commerce_checkout_page_info() {
$checkout_pages = array();
$checkout_pages['payment'] = array(
'title' => t('Payment'),
'help' => t('Use the button below to proceed to the payment server.'),
'status_cart' => FALSE,
'locked' => TRUE,
'buttons' => FALSE,
'weight' => 20,
);
return $checkout_pages;
}
/**
* Implements hook_commerce_checkout_pane_info().
*/
function commerce_payment_commerce_checkout_pane_info() {
$checkout_panes = array();
$checkout_panes['commerce_payment'] = array(
'title' => t('Payment'),
'page' => 'review',
'file' => 'includes/commerce_payment.checkout_pane.inc',
'base' => 'commerce_payment_pane',
'weight' => 10,
);
$checkout_panes['commerce_payment_redirect'] = array(
'title' => t('Off-site payment redirect'),
'page' => 'payment',
'locked' => TRUE,
'file' => 'includes/commerce_payment.checkout_pane.inc',
'base' => 'commerce_payment_redirect_pane',
);
return $checkout_panes;
}
/**
* Moves an order ahead to the next page via an order update and redirect.
*
* Redirected payment methods are responsible for calling this method when
* receiving external notifications of successful payment.
*
* @param $order
* An order object.
* @param $log
* Optional log message to use when updating the order status in conjunction
* with the redirect to the next checkout page.
*/
function commerce_payment_redirect_pane_next_page($order, $log = '') {
// Load the order status object for the current order.
$order_status = commerce_order_status_load($order->status);
if ($order_status['state'] == 'checkout' && $order_status['checkout_page'] == 'payment') {
$payment_page = commerce_checkout_page_load($order_status['checkout_page']);
$next_page = $payment_page['next_page'];
$order = commerce_order_status_update($order, 'checkout_' . $next_page, FALSE, NULL, $log);
// Inform modules of checkout completion if the next page is Completed.
if ($next_page == 'complete') {
commerce_checkout_complete($order);
}
}
}
/**
* Moves an order back to the previous page via an order update and redirect.
*
* Redirected payment methods are responsible for calling this method when
* receiving external notifications of failed payment.
*
* @param $order
* An order object.
* @param $log
* Optional log message to use when updating the order status in conjunction
* with the redirect to the previous checkout page.
*/
function commerce_payment_redirect_pane_previous_page($order, $log = '') {
// Load the order status object for the current order.
$order_status = commerce_order_status_load($order->status);
if ($order_status['state'] == 'checkout' && $order_status['checkout_page'] == 'payment') {
$payment_page = commerce_checkout_page_load($order_status['checkout_page']);
$prev_page = $payment_page['prev_page'];
$order = commerce_order_status_update($order, 'checkout_' . $prev_page, FALSE, NULL, $log);
}
}
/**
* Implements hook_commerce_payment_totals_row_info().
*/
function commerce_payment_commerce_payment_totals_row_info($totals, $order) {
$rows = array();
if (count($totals) == 0) {
// Add a row for the remaining balance on the order.
if ($order) {
$balance = commerce_payment_order_balance($order, $totals);
$rows[] = array(
'data' => array(
array(
'data' => t('Order balance'),
'class' => array(
'label',
),
),
array(
'data' => commerce_currency_format($balance['amount'], $balance['currency_code']),
'class' => array(
'balance',
),
),
),
'class' => array(
'order-balance',
),
'weight' => 10,
);
}
}
elseif (count($totals) == 1) {
// Otherwise if there's only a single currency total...
$currency_code = key($totals);
// Add a row for the total amount paid.
$rows[] = array(
'data' => array(
array(
'data' => t('Total paid'),
'class' => array(
'label',
),
),
array(
'data' => commerce_currency_format($totals[$currency_code], $currency_code),
'class' => array(
'total',
),
),
),
'class' => array(
'total-paid',
),
'weight' => 0,
);
// Add a row for the remaining balance on the order.
if ($order) {
$balance = commerce_payment_order_balance($order, $totals);
// Fix false balances.
if (empty($balance)) {
$formatted_amount = commerce_currency_format(0, NULL);
}
else {
$formatted_amount = commerce_currency_format($balance['amount'], $balance['currency_code']);
}
$rows[] = array(
'data' => array(
array(
'data' => t('Order balance'),
'class' => array(
'label',
),
),
array(
'data' => $formatted_amount,
'class' => array(
'balance',
),
),
),
'class' => array(
'order-balance',
),
'weight' => 10,
);
}
}
else {
$weight = 0;
foreach ($totals as $currency_code => $amount) {
$rows[] = array(
'data' => array(
array(
'data' => t('Total paid (@currency_code)', array(
'@currency_code' => $currency_code,
)),
'class' => array(
'label',
),
),
array(
'data' => commerce_currency_format($amount, $currency_code),
'class' => array(
'total',
),
),
),
'class' => array(
'total-paid',
'total-' . $currency_code,
),
'weight' => $weight++,
);
}
}
return $rows;
}
/**
* Implements hook_commerce_payment_transaction_insert().
*
* When a new payment transaction is inserted that is already completed, check
* the order balance and invoke a Rules event if the order is paid in full.
*/
function commerce_payment_commerce_payment_transaction_insert($transaction) {
// If the inserted transaction was marked as a success and placed against a
// valid order...
$transaction_statuses = commerce_payment_transaction_statuses();
if (!empty($transaction_statuses[$transaction->status]['total']) && ($order = commerce_order_load($transaction->order_id))) {
// Then check to make sure the event hasn't been invoked for this order.
if (empty($order->data['commerce_payment_order_paid_in_full_invoked'])) {
// Check the order balance and invoke the event.
$balance = commerce_payment_order_balance($order);
if (!empty($balance) && $balance['amount'] <= 0) {
// If automatic order revisions are enabled site-wide, create a new
// revision now with a log message identifying the payment event.
if (variable_get('commerce_order_auto_revision', TRUE)) {
$order->revision = TRUE;
$order->log = t('Order updated to reflect the order has just been paid in full.');
}
// Invoke the event including a hook of the same name.
rules_invoke_all('commerce_payment_order_paid_in_full', $order, $transaction);
// Update the order's data array to indicate this just happened.
$order->data['commerce_payment_order_paid_in_full_invoked'] = TRUE;
// Save the updated order.
commerce_order_save($order);
}
}
}
}
/**
* Implements hook_commerce_payment_transaction_update().
*
* When an existing transaction is updated from an incomplete status to
* completed, check the order balance and invoke a Rules event if the order is
* paid in full.
*/
function commerce_payment_commerce_payment_transaction_update($transaction) {
commerce_payment_commerce_payment_transaction_insert($transaction);
}
/**
* Implements hook_commerce_order_delete().
*
* Make sure all payment transactions are deleted whenever the
* order they are associated with is deleted.
*/
function commerce_payment_commerce_order_delete($order) {
foreach (commerce_payment_transaction_load_multiple(array(), array(
'order_id' => $order->order_id,
)) as $transaction) {
commerce_payment_transaction_delete($transaction->transaction_id);
}
}
/**
* Implements hook_views_api().
*/
function commerce_payment_views_api() {
return array(
'api' => 3,
'path' => drupal_get_path('module', 'commerce_payment') . '/includes/views',
);
}
/**
* Returns an array of payment methods defined by enabled modules.
*
* @return
* An associative array of payment method objects keyed by the method_id.
*/
function commerce_payment_methods() {
$payment_methods =& drupal_static(__FUNCTION__);
// If the payment methods haven't been defined yet, do so now.
if (!isset($payment_methods)) {
$payment_methods = array();
// Build the payment methods array, including module names for the purpose
// of including files if necessary.
foreach (module_implements('commerce_payment_method_info') as $module) {
foreach (module_invoke($module, 'commerce_payment_method_info') as $method_id => $payment_method) {
$payment_method['method_id'] = $method_id;
$payment_method['module'] = $module;
$payment_methods[$method_id] = $payment_method;
}
}
drupal_alter('commerce_payment_method_info', $payment_methods);
foreach ($payment_methods as $method_id => &$payment_method) {
$defaults = array(
'method_id' => $method_id,
'base' => $method_id,
'title' => '',
'description' => '',
'active' => FALSE,
'checkout' => TRUE,
'terminal' => TRUE,
'offsite' => FALSE,
'offsite_autoredirect' => FALSE,
'callbacks' => array(),
'file' => '',
);
$payment_method += $defaults;
// Default the display title to the title if necessary. The display title
// is used in instances where the payment method has an official name used
// as the title (i.e. PayPal WPS) but a different method of display on
// selection forms (like some text and a set of images).
if (empty($payment_method['display_title'])) {
$payment_method['display_title'] = $payment_method['title'];
}
// Default the short title to the title if necessary. Like the display
// title, the short title is an alternate way of displaying the title to
// the user consisting of plain text but with unnecessary information
// stripped off. The payment method title might be PayPal WPS as it
// distinguishes itself from other PayPal payment services, but you would
// only want to display PayPal to the customer as their means of payment.
if (empty($payment_methods[$method_id]['short_title'])) {
$payment_method['short_title'] = $payment_method['title'];
}
// Merge in default callbacks.
foreach (array(
'settings_form',
'submit_form',
'submit_form_validate',
'submit_form_submit',
'redirect_form',
'redirect_form_back',
'redirect_form_validate',
'redirect_form_submit',
) as $callback) {
if (!isset($payment_method['callbacks'][$callback])) {
$payment_method['callbacks'][$callback] = $payment_method['base'] . '_' . $callback;
}
}
}
}
return $payment_methods;
}
/**
* Resets the cached list of payment methods.
*/
function commerce_payment_methods_reset() {
$payment_methods =& drupal_static('commerce_payment_methods');
$payment_methods = NULL;
entity_info_cache_clear();
}
/**
* Returns a payment method array.
*
* @param $method_id
* The ID of the payment method to return.
*
* @return
* The fully loaded payment method object or FALSE if not found.
*/
function commerce_payment_method_load($method_id) {
$payment_methods = commerce_payment_methods();
return isset($payment_methods[$method_id]) ? $payment_methods[$method_id] : FALSE;
}
/**
* Returns the title of any or all payment methods.
*
* @param $title
* String indicating which title to return, either 'title', 'display_title',
* or 'short_title'.
* @param $method
* Optional parameter specifying the payment method whose title to return.
*
* @return
* Either an array of all payment method titles keyed by the machine name or a
* string containing the title for the specified type. If a type is specified
* that does not exist, this function returns FALSE.
*/
function commerce_payment_method_get_title($title = 'title', $method = NULL) {
$payment_methods = commerce_payment_methods();
// Return a method title if specified and it exists.
if (!empty($method)) {
if (isset($payment_methods[$method])) {
return $payment_methods[$method][$title];
}
else {
// Return FALSE if it does not exist.
return FALSE;
}
}
// Otherwise create an array of all payment method titles.
$titles = array();
foreach ($payment_methods as $key => $value) {
$titles[$key] = $value[$title];
}
return $titles;
}
/**
* Wraps commerce_payment_method_get_title() for the Entity module.
*/
function commerce_payment_method_options_list() {
return commerce_payment_method_get_title();
}
/**
* Returns the specified callback for the given payment method if it exists.
*
* @param $payment_method
* The payment method object.
* @param $callback
* The callback function to return, one of:
* - settings_form
* - submit_form
* - submit_form_validate
* - submit_form_submit
* - redirect_form
* - redirect_form_back
* - redirect_form_validate
* - redirect_form_submit
*
* @return
* A string containing the name of the callback function or FALSE if it could
* not be found.
*/
function commerce_payment_method_callback($payment_method, $callback) {
// Include the payment method file if specified.
if (!empty($payment_method['file'])) {
$parts = explode('.', $payment_method['file']);
module_load_include(array_pop($parts), $payment_method['module'], implode('.', $parts));
}
// If the specified callback function exists, return it.
if (!empty($payment_method['callbacks'][$callback]) && is_callable($payment_method['callbacks'][$callback])) {
return $payment_method['callbacks'][$callback];
}
// Otherwise return FALSE.
return FALSE;
}
/**
* Returns a payment method instance ID given a payment method ID and the Rule
* containing an enabling action with settings.
*
* @param $method_id
* The ID of the payment method.
* @param $rule
* The Rules configuration object used to provide settings for the method.
*
* @return
* A string used as the payment method instance ID.
*/
function commerce_payment_method_instance_id($method_id, $rule) {
$parts = array(
$method_id,
$rule->name,
);
return implode('|', $parts);
}
/**
* Returns a payment method instance array which includes the settings specific
* to the context of the instance.
*
* @param $instance_id
* A payment method instance ID in the form of a pipe delimited string
* containing the method_id and the enabling Rule's name.
*
* @return
* The payment method instance object which is identical to the payment method
* object with the addition of the settings array.
*/
function commerce_payment_method_instance_load($instance_id) {
// Return FALSE if there is no pipe delimeter in the instance ID.
if (strpos($instance_id, '|') === FALSE) {
return FALSE;
}
// Explode the method key into its component parts.
list($method_id, $rule_name) = explode('|', $instance_id);
// Return FALSE if we didn't receive a proper instance ID.
if (empty($method_id) || empty($rule_name)) {
return FALSE;
}
// First load the payment method and add the instance ID.
$payment_method = commerce_payment_method_load($method_id);
$payment_method['instance_id'] = $instance_id;
// Then load the Rule configuration that enables the method.
$rule = rules_config_load($rule_name);
// Return FALSE if it couldn't be loaded.
if (empty($rule)) {
return FALSE;
}
// Iterate over its actions to find one with the matching element ID and pull
// its settings into the payment method object.
$payment_method['settings'] = array();
foreach ($rule
->actions() as $action) {
if (is_callable(array(
$action,
'getElementName',
)) && $action
->getElementName() == 'commerce_payment_enable_' . $method_id) {
if (is_array($action->settings['payment_method']) && !empty($action->settings['payment_method']['settings'])) {
$payment_method['settings'] = $action->settings['payment_method']['settings'];
}
}
}
return $payment_method;
}
/**
* Returns an array of transaction payment status objects for the defined
* payment statuses.
*
* This function invokes hook_commerce_payment_transaction_status_info() so
* other payment modules can define statuses if necessary. However, it doesn't
* allow for altering so that existing payment methods cannot be unset. It still
* does perform an array merge in such a way that the properties for the three
* core statuses defined by this module may be overridden if the same keys are
* used in another module's implementation of the info hook.
*/
function commerce_payment_transaction_statuses() {
$transaction_statuses =& drupal_static(__FUNCTION__);
// If the statuses haven't been defined yet, do so now.
if (!isset($transaction_statuses)) {
$transaction_statuses = module_invoke_all('commerce_payment_transaction_status_info');
$transaction_statuses += array(
COMMERCE_PAYMENT_STATUS_PENDING => array(
'status' => COMMERCE_PAYMENT_STATUS_PENDING,
'title' => t('Pending'),
'icon' => drupal_get_path('module', 'commerce_payment') . '/theme/icon-pending.png',
'total' => FALSE,
),
COMMERCE_PAYMENT_STATUS_SUCCESS => array(
'status' => COMMERCE_PAYMENT_STATUS_SUCCESS,
'title' => t('Success'),
'icon' => drupal_get_path('module', 'commerce_payment') . '/theme/icon-success.png',
'total' => TRUE,
),
COMMERCE_PAYMENT_STATUS_FAILURE => array(
'status' => COMMERCE_PAYMENT_STATUS_FAILURE,
'title' => t('Failure'),
'icon' => drupal_get_path('module', 'commerce_payment') . '/theme/icon-failure.png',
'total' => FALSE,
),
);
}
return $transaction_statuses;
}
/**
* Returns an options list of payment transaction statuses.
*/
function commerce_payment_transaction_status_options_list() {
// Build an array of payment transaction status options.
$options = array();
foreach (commerce_payment_transaction_statuses() as $payment_transaction_status) {
$options[$payment_transaction_status['status']] = $payment_transaction_status['title'];
}
return $options;
}
/**
* Themes the payment transaction entity.
*/
function theme_commerce_payment_transaction($variables) {
return render($variables['content']);
}
/**
* Themes the icon representing a payment transaction status.
*/
function theme_commerce_payment_transaction_status_icon($variables) {
$transaction_status = $variables['transaction_status'];
return theme('image', array(
'path' => $transaction_status['icon'],
'alt' => $transaction_status['title'],
'title' => $transaction_status['title'],
'attributes' => array(
'class' => array(
drupal_html_class($transaction_status['status']),
),
),
));
}
/**
* Themes a text value related to a payment transaction status.
*/
function theme_commerce_payment_transaction_status_text($variables) {
$transaction_status = $variables['transaction_status'];
return '<span class="' . drupal_html_class($transaction_status['status']) . '">' . $variables['text'] . '</span>';
}
/**
* Returns the payment transaction status object for the specified status.
*
* @param $status
* The transaction status string.
*
* @return
* An object containing the transaction status information or FALSE if the
* requested status is not found.
*/
function commerce_payment_transaction_status_load($status) {
$transaction_statuses = commerce_payment_transaction_statuses();
return !empty($transaction_statuses[$status]) ? $transaction_statuses[$status] : FALSE;
}
/**
* Returns an initialized payment transaction object.
*
* @param $method_id
* The method_id of the payment method for the transaction.
*
* @return
* A transaction object with all default fields initialized.
*/
function commerce_payment_transaction_new($method_id = '', $order_id = 0) {
return entity_get_controller('commerce_payment_transaction')
->create(array(
'payment_method' => $method_id,
'order_id' => $order_id,
));
}
/**
* Saves a payment transaction.
*
* @param $transaction
* The full transaction object to save.
*
* @return
* SAVED_NEW or SAVED_UPDATED depending on the operation performed.
*/
function commerce_payment_transaction_save($transaction) {
return entity_get_controller('commerce_payment_transaction')
->save($transaction);
}
/**
* Loads a payment transaction by ID.
*/
function commerce_payment_transaction_load($transaction_id) {
$transactions = commerce_payment_transaction_load_multiple(array(
$transaction_id,
), array());
return $transactions ? reset($transactions) : FALSE;
}
/**
* Loads multiple payment transaction by ID or based on a set of matching conditions.
*
* @see entity_load()
*
* @param $transaction_ids
* An array of transaction IDs.
* @param $conditions
* An array of conditions on the {commerce_payment_transaction} table in the
* form 'field' => $value.
* @param $reset
* Whether to reset the internal transaction loading cache.
*
* @return
* An array of transaction objects indexed by transaction_id.
*/
function commerce_payment_transaction_load_multiple($transaction_ids = array(), $conditions = array(), $reset = FALSE) {
return entity_load('commerce_payment_transaction', $transaction_ids, $conditions, $reset);
}
/**
* Deletes a payment transaction by ID.
*
* @param $transaction_id
* The ID of the transaction to delete.
*
* @return
* TRUE on success, FALSE otherwise.
*/
function commerce_payment_transaction_delete($transaction_id) {
return commerce_payment_transaction_delete_multiple(array(
$transaction_id,
));
}
/**
* Deletes multiple payment transactions by ID.
*
* @param $transaction_ids
* An array of transaction IDs to delete.
*
* @return
* TRUE on success, FALSE otherwise.
*/
function commerce_payment_transaction_delete_multiple($transaction_ids) {
return entity_get_controller('commerce_payment_transaction')
->delete($transaction_ids);
}
/**
* Determines access for a variety of operations on payment transactions.
*
* @param $op
* The operation being performed, one of view, update, create, or delete.
* @param $transaction
* The payment transaction to check.
* @param $account
* The user account attempting the operation; defaults to the current user.
*
* @return
* TRUE or FALSE indicating access for the operation.
*/
function commerce_payment_transaction_access($op, $transaction, $account = NULL) {
if (isset($transaction->order_id)) {
$order = commerce_order_load($transaction->order_id);
if (!$order) {
return FALSE;
}
}
else {
$order = NULL;
}
return commerce_payment_transaction_order_access($op, $order, $account);
}
/**
* Determines access for a variety of operations for payment transactions on a given order.
*
* @param $op
* The payment transaction operation being performed, one of view, update, create, or delete.
* @param $order
* The order to check against (optional if $op == 'create').
* @param $account
* The user account attempting the operation; defaults to the current user.
*
* @return
* TRUE or FALSE indicating access for the operation.
*/
function commerce_payment_transaction_order_access($op, $order, $account = NULL) {
global $user;
if (empty($account)) {
$account = clone $user;
}
// Grant administrators access to do anything.
if (user_access('administer payments', $account)) {
return TRUE;
}
switch ($op) {
// Creating new payment transactions.
case 'create':
if (user_access('create payments', $account)) {
// We currently allow any user to create any payment transaction,
// regardless of the order, because entity_access() doesn't give us a
// way to discriminate on the order.
// @todo: find a way to prevent creating a payment transaction if the
// user doesn't have access to the order.
if (!isset($order) || commerce_order_access('update', $order, $account)) {
return TRUE;
}
}
break;
// Viewing payment transactions.
case 'view':
if (user_access('view payments', $account)) {
if (commerce_order_access('view', $order, $account)) {
return TRUE;
}
}
break;
case 'update':
if (user_access('update payments', $account)) {
if (commerce_order_access('view', $order, $account)) {
return TRUE;
}
}
break;
case 'delete':
if (user_access('delete payments', $account)) {
if (commerce_order_access('update', $order, $account)) {
return TRUE;
}
}
break;
}
return FALSE;
}
/**
* Implements hook_query_TAG_alter().
*
* Implement access control on payment transaction. This is different from other
* entities because the access to a payment transaction is partially delegated
* to its order.
*/
function commerce_payment_query_commerce_payment_transaction_access_alter(QueryAlterableInterface $query) {
// Read the meta-data from the query.
if (!($account = $query
->getMetaData('account'))) {
global $user;
$account = $user;
}
// If the user has the administration permission, nothing to do.
if (user_access('administer payments', $account)) {
return;
}
// Join the payment transaction to their orders.
if (user_access('view payments', $account)) {
$tables =& $query
->getTables();
// Look for an existing commerce_order table.
foreach ($tables as $table) {
if ($table['table'] === 'commerce_order') {
$order_alias = $table['alias'];
break;
}
}
// If not found, attempt a join against the first table.
if (!isset($order_alias)) {
reset($tables);
$base_table = key($tables);
$order_alias = $query
->innerJoin('commerce_order', 'co', '%alias.order_id = ' . $base_table . '.order_id');
}
// Perform the access control on the order.
commerce_entity_access_query_alter($query, 'commerce_order', $order_alias);
}
else {
// The user has access to no payment transaction.
$query
->where('1 = 0');
}
}
/**
* Calculates the balance of an order by subtracting the total of all successful
* transactions from the total of all the line items on the order.
*
* @param $order
* The fully loaded order object whose balance should be calculated.
* @param $totals
* Optionally submit an array of transaction totals keyed by currency code
* with the amount as the value.
*
* @return
* An array containing the amount and currency code representing the balance
* of the order or FALSE if it is impossible to calculate.
*/
function commerce_payment_order_balance($order, $totals = array()) {
$wrapper = entity_metadata_wrapper('commerce_order', $order);
$order_total = $wrapper->commerce_order_total
->value();
// Calculate the transaction totals if not supplied.
if (empty($totals)) {
$transaction_statuses = commerce_payment_transaction_statuses();
foreach (commerce_payment_transaction_load_multiple(array(), array(
'order_id' => $order->order_id,
)) as $transaction) {
// If the payment transaction status indicates it should include the
// current transaction in the total...
if ($transaction_statuses[$transaction->status]['total']) {
// Add the transaction to its currency's running total if it exists...
if (isset($totals[$transaction->currency_code])) {
$totals[$transaction->currency_code] += $transaction->amount;
}
else {
// Or begin a new running total for the currency.
$totals[$transaction->currency_code] = $transaction->amount;
}
}
}
}
// Only return a balance if the totals array contains a single matching currency.
if (count($totals) == 1 && isset($totals[$order_total['currency_code']])) {
return array(
'amount' => $order_total['amount'] - $totals[$order_total['currency_code']],
'currency_code' => $order_total['currency_code'],
);
}
elseif (empty($totals)) {
return array(
'amount' => $order_total['amount'],
'currency_code' => $order_total['currency_code'],
);
}
else {
return FALSE;
}
}
/**
* Returns a sorted array of payment totals table rows.
*
* @param $totals
* An array of payment totals whose keys are currency codes and values are the
* total amount paid in each currency.
* @param $order
* If available, the order object to which the payments apply.
*
* @return
* An array of table row data as expected by theme_table().
*
* @see hook_commerce_payment_totals_row_info()
*/
function commerce_payment_totals_rows($totals, $order) {
// Retrieve rows defined by the hook and allow other modules to alter them.
$rows = module_invoke_all('commerce_payment_totals_row_info', $totals, $order);
drupal_alter('commerce_payment_totals_row_info', $rows, $totals, $order);
// Sort the rows by weight and return the array.
uasort($rows, 'drupal_sort_weight');
return $rows;
}
/**
* Callback for getting payment transaction properties.
*
* @see commerce_payment_entity_property_info()
*/
function commerce_payment_transaction_get_properties($transaction, array $options, $name) {
switch ($name) {
case 'user':
return $transaction->uid;
case 'order':
return !empty($transaction->order_id) ? $transaction->order_id : commerce_order_new();
case 'message':
if ($transaction->message) {
return t($transaction->message, is_array($transaction->message_variables) ? $transaction->message_variables : array());
}
else {
return '';
}
}
}
/**
* Checks if the order has any associated payment transactions.
*
* @param $order
* A commerce_order object.
*
* @return bool
* TRUE if payment transactions exist.
*/
function commerce_payment_order_has_payments($order) {
$query = new EntityFieldQuery();
$query
->entityCondition('entity_type', 'commerce_payment_transaction')
->propertyCondition('order_id', $order->order_id, '=')
->propertyCondition('status', COMMERCE_PAYMENT_STATUS_FAILURE, '!=')
->count();
return $query
->execute() == 1;
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Makes sure the user deleting an order has permissions to delete
* associated payment transactions if they exist on the order
* being deleted. Prevents deletion of the order if the user
* does not have access.
*/
function commerce_payment_form_commerce_order_ui_order_delete_form_alter(&$form, &$form_state) {
if (commerce_payment_order_has_payments($form_state['order'])) {
if (user_access('administer payments')) {
// Add a required checkbox to force user to confirm
// deletion of attached payments as well.
$form['delete_order_payments'] = array(
'#type' => 'checkbox',
'#title' => t('Confirm deletion of order and all of its related payment transactions.'),
'#required' => TRUE,
);
$form['actions']['submit']['#states']['visible'][] = array(
':input[name="delete_order_payments"]' => array(
'checked' => TRUE,
),
);
}
else {
// Inform the user they cannot delete the order due
// to attached payments.
$form['description']['#markup'] = '<p>' . t('This order has payment transactions attached to it. You do not have sufficient permissions to delete it.');
$form['actions']['submit']['#access'] = FALSE;
}
}
}
/**
* Callback for setting payment transaction properties.
*
* @see commerce_payment_entity_property_info()
*/
function commerce_payment_transaction_set_properties($transaction, $name, $value) {
switch ($name) {
case 'user':
$transaction->uid = $value;
break;
case 'order':
$transaction->order_id = $value;
break;
}
}
/**
* Implements hook_entity_load().
*
* Override payment method settings on the fly.
*/
function commerce_payment_entity_load($entities, $type) {
if ($type != 'rules_config') {
return;
}
foreach ($entities as $entity) {
if (!$entity instanceof RulesTriggerableInterface) {
continue;
}
$events = $entity
->events();
// Check for the "Select available payment methods for an order" event.
if (!in_array('commerce_payment_methods', $events)) {
continue;
}
// Iterate over the actions to find one with the matching element ID and
// check if we have payment settings override.
foreach ($entity
->actions() as $name => $action) {
if (is_callable(array(
$action,
'getElementName',
)) && strpos($action
->getElementName(), 'commerce_payment_enable_') === 0) {
if (isset($action->settings['payment_method']) && is_array($action->settings['payment_method']) && !empty($action->settings['payment_method']['settings'])) {
$instance_id = commerce_payment_method_instance_id($action->settings['payment_method']['method_id'], $entity);
$settings = variable_get($instance_id, array());
if (!empty($settings) && is_array($settings)) {
$action->settings['payment_method']['settings'] = array_replace_recursive($action->settings['payment_method']['settings'], $settings);
$action
->processSettings(TRUE);
}
}
}
}
}
}
/**
* Implements hook_form_FORM_ID_alter().
*
* Disable payment method settings form elements that are overridden.
*/
function commerce_payment_form_rules_ui_edit_element_alter(&$form, &$form_state) {
if (!isset($form['parameter']['payment_method'])) {
return;
}
// Retrieve the Rule from the $form_state.
$rule = reset($form_state['build_info']['args']);
$payment_method =& $form['parameter']['payment_method']['settings']['payment_method'];
$instance_id = commerce_payment_method_instance_id($payment_method['method_id']['#value'], $rule);
$settings = variable_get($instance_id, array());
if (!empty($settings) && is_array($settings)) {
$display_override_message = FALSE;
foreach (element_get_visible_children($payment_method['settings']) as $key) {
if (isset($settings[$key]) && isset($payment_method['settings'][$key])) {
$payment_method['settings'][$key]['#disabled'] = TRUE;
$payment_method['settings'][$key]['#required'] = FALSE;
$display_override_message = TRUE;
}
}
// Display a warning message to warn the user about the settings
// that are overridden.
if ($display_override_message) {
drupal_set_message(t('Disabled fields below have been configured in code and cannot be changed here.'), 'warning');
}
}
}
Functions
Name | Description |
---|---|
commerce_payment_commerce_checkout_page_info | Implements hook_commerce_checkout_page_info(). |
commerce_payment_commerce_checkout_pane_info | Implements hook_commerce_checkout_pane_info(). |
commerce_payment_commerce_order_delete | Implements hook_commerce_order_delete(). |
commerce_payment_commerce_payment_totals_row_info | Implements hook_commerce_payment_totals_row_info(). |
commerce_payment_commerce_payment_transaction_insert | Implements hook_commerce_payment_transaction_insert(). |
commerce_payment_commerce_payment_transaction_update | Implements hook_commerce_payment_transaction_update(). |
commerce_payment_entity_info | Implements of hook_entity_info(). |
commerce_payment_entity_load | Implements hook_entity_load(). |
commerce_payment_form_commerce_order_ui_order_delete_form_alter | Implements hook_form_FORM_ID_alter(). |
commerce_payment_form_rules_ui_edit_element_alter | Implements hook_form_FORM_ID_alter(). |
commerce_payment_hook_info | Implements hook_hook_info(). |
commerce_payment_methods | Returns an array of payment methods defined by enabled modules. |
commerce_payment_methods_reset | Resets the cached list of payment methods. |
commerce_payment_method_callback | Returns the specified callback for the given payment method if it exists. |
commerce_payment_method_get_title | Returns the title of any or all payment methods. |
commerce_payment_method_instance_id | Returns a payment method instance ID given a payment method ID and the Rule containing an enabling action with settings. |
commerce_payment_method_instance_load | Returns a payment method instance array which includes the settings specific to the context of the instance. |
commerce_payment_method_load | Returns a payment method array. |
commerce_payment_method_options_list | Wraps commerce_payment_method_get_title() for the Entity module. |
commerce_payment_modules_enabled | Implements hook_modules_enabled(). |
commerce_payment_order_balance | Calculates the balance of an order by subtracting the total of all successful transactions from the total of all the line items on the order. |
commerce_payment_order_has_payments | Checks if the order has any associated payment transactions. |
commerce_payment_permission | Implements hook_permission(). |
commerce_payment_query_commerce_payment_transaction_access_alter | Implements hook_query_TAG_alter(). |
commerce_payment_redirect_pane_next_page | Moves an order ahead to the next page via an order update and redirect. |
commerce_payment_redirect_pane_previous_page | Moves an order back to the previous page via an order update and redirect. |
commerce_payment_theme | Implements hook_theme(). |
commerce_payment_totals_rows | Returns a sorted array of payment totals table rows. |
commerce_payment_transaction_access | Determines access for a variety of operations on payment transactions. |
commerce_payment_transaction_delete | Deletes a payment transaction by ID. |
commerce_payment_transaction_delete_multiple | Deletes multiple payment transactions by ID. |
commerce_payment_transaction_get_properties | Callback for getting payment transaction properties. |
commerce_payment_transaction_load | Loads a payment transaction by ID. |
commerce_payment_transaction_load_multiple | Loads multiple payment transaction by ID or based on a set of matching conditions. |
commerce_payment_transaction_new | Returns an initialized payment transaction object. |
commerce_payment_transaction_order_access | Determines access for a variety of operations for payment transactions on a given order. |
commerce_payment_transaction_save | Saves a payment transaction. |
commerce_payment_transaction_set_properties | Callback for setting payment transaction properties. |
commerce_payment_transaction_statuses | Returns an array of transaction payment status objects for the defined payment statuses. |
commerce_payment_transaction_status_load | Returns the payment transaction status object for the specified status. |
commerce_payment_transaction_status_options_list | Returns an options list of payment transaction statuses. |
commerce_payment_transaction_uri | Entity uri callback: gives modules a chance to specify a path for a payment transaction. |
commerce_payment_views_api | Implements hook_views_api(). |
template_preprocess_commerce_payment_totals | Adds the necessary CSS for the line item summary template. |
theme_commerce_payment_transaction | Themes the payment transaction entity. |
theme_commerce_payment_transaction_status_icon | Themes the icon representing a payment transaction status. |
theme_commerce_payment_transaction_status_text | Themes a text value related to a payment transaction status. |
_commerce_payment_default_rules_reset | Resets default Rules if necessary when modules are enabled or disabled. |