Skip to content
commerce_payment.module 42.7 KiB
Newer Older
/**
 * @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', // TODO: Update to use a custom callback.
      ),
      'bundle keys' => array(
        'bundle' => 'payment_method',
      ),
      'bundles' => array(),
      'load hook' => 'commerce_payment_transaction_load',
      'view modes' => array(
        'administrator' => array(
          'label' => t('Administrator'),
      '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.
    ),
  );

  foreach (commerce_payment_methods() as $method_id => $payment_method) {
    $return['commerce_payment_transaction']['bundles'][$method_id] = array(
      'label' => $payment_method['title'],
/**
 * 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.'),
/**
 * 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_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(
    'help' => t('Use the button below to proceed to the payment server.'),
  return $checkout_pages;
}

/**
 * Implements hook_commerce_checkout_pane_info().
 */
function commerce_payment_commerce_checkout_pane_info() {
  $checkout_panes = array();
  $checkout_panes['commerce_payment'] = array(
    'file' => 'includes/commerce_payment.checkout_pane.inc',
  $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...

    // 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'] = REQUEST_TIME;

        // 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.
  $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,
      $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'];
      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) {
}

/**
 * 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_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];
/**
 * 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) {
  // 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();
    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'];
/**
 * 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');
      COMMERCE_PAYMENT_STATUS_PENDING => array(
        'status' => COMMERCE_PAYMENT_STATUS_PENDING,
        'title' => t('Pending'),
        'icon' => drupal_get_path('module', 'commerce_payment') . '/theme/icon-pending.png',
      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,
      ),
    );
  }

/**
 * 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(
}

/**
 * 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) {
  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':