You are here

function commerce_cardonfile_order_charge_card in Commerce Card on File 7.2

Process a charge for a given an order

Wrapper function for _commerce_cardonfile_order_invoke_process_card() to trigger rules events

Parameters

$order: An order object

$charge: Charge array of amount, currency_code

$card: The card entity.

$forced_instance_id: Payment instance ID to enforce the usage of a specific payment gateway

Return value

TRUE if the order was processed successfully

1 call to commerce_cardonfile_order_charge_card()
commerce_cardonfile_rules_action_order_charge_card in ./commerce_cardonfile.rules.inc
Rules action callback for commerce_cardonfile_order_charge_card
1 string reference to 'commerce_cardonfile_order_charge_card'
commerce_cardonfile_recurring_default_rules_configuration in modules/recurring/commerce_cardonfile_recurring.rules_defaults.inc
Implements hook_default_rules_configuration().

File

./commerce_cardonfile.module, line 1221
Supports card on file functionality for credit card payment methods by associating card data reference IDs from payment gateways with user accounts.

Code

function commerce_cardonfile_order_charge_card($order, $charge = array(), $card = NULL, $forced_instance_id = NULL) {
  $response = array(
    'status' => FALSE,
    'code' => COMMERCE_COF_PROCESS_CODE_INSUFFICIENT_DATA,
    'message' => '',
    'message_variables' => array(),
  );

  // Exit if no order id
  if (empty($order->order_id)) {
    $response['message'] = 'Order ID is not provided.';
    return $response;
  }
  $response['message_variables'] += array(
    '@order_id' => $order->order_id,
  );

  // Exit if no user associated with the order
  if (empty($order->uid)) {
    $response['message'] = 'Order owner not provided for order @order_id.';
    return $response;
  }
  $response['message_variables'] += array(
    '@uid' => $order->uid,
  );

  // determine charge amount
  // set charge to order balance if none provided
  if (empty($charge)) {
    $charge = commerce_payment_order_balance($order);
  }

  // exit if no charge
  if (empty($charge) || empty($charge['amount']) || empty($charge['currency_code'])) {
    $response['message'] = 'Charge amount not provided for order @order_id.';
    return $response;
  }
  $response['message_variables'] += array(
    '@charge' => commerce_currency_format($charge['amount'], $charge['currency_code']),
  );

  // exit if no card data provided
  if (empty($card)) {
    $response['message'] = 'Card data not provided for order @order_id.';
    return $response;
  }
  $response['card_chosen'] = $card;
  if (!commerce_cardonfile_order_can_charge_card($order, $card)) {

    // check for expiration to set a specific code
    if (!commerce_cardonfile_validate_card_expiration($card)) {
      $response['code'] = COMMERCE_COF_PROCESS_CODE_CARD_EXPIRED;
      $response['message'] = 'Card on file has expired for user @uid\'s card @card_id when attempting to process order @order_id.';
    }
    else {
      $response['code'] = COMMERCE_COF_PROCESS_CODE_CARD_NOT_CHARGEABLE;
      $response['message'] = 'Card provided cannot be charged for the order @order_id and user @uid.';
    }
    return $response;
  }

  // resolve payment method instance input
  $instance_is_forced = FALSE;
  if (!empty($forced_instance_id)) {
    $instance_is_forced = TRUE;

    // set the order data so chargeable hook can use to determine the card
    $order->data['payment_method'] = $forced_instance_id;
    $response['message_variables'] += array(
      '@instance_id' => $forced_instance_id,
    );
  }
  else {
    $forced_instance_id = NULL;
  }
  if ($instance_is_forced && isset($card->instance_id) && $card->instance_id != $forced_instance_id) {
    $response['code'] = COMMERCE_COF_PROCESS_CODE_CARD_NOT_CHARGEABLE;
    $response['message'] = 'Card provided is not registered with the requested payment method: Order @order_id, user @uid, payment instance @instance_id';
    return $response;
  }

  // Wrap up the order
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);

  // load payment method
  $payment_method = commerce_payment_method_instance_load($card->instance_id);
  if (empty($payment_method)) {
    $response['code'] = COMMERCE_COF_PROCESS_CODE_METHOD_EMPTY;
    $response['message'] = 'The payment method instance (@instance_id) is not available on the system.';
    return $response;
  }
  $response['message_variables'] += array(
    '@method' => isset($payment_method['short_title']) ? $payment_method['short_title'] : $payment_method['method_id'],
  );

  // determine payment method's callback function
  $func = commerce_cardonfile_payment_method_callback($payment_method, 'charge callback');

  // Exit if no callback - sanity check since it should have this if "can charge"
  if (empty($func)) {
    $response['code'] = COMMERCE_COF_PROCESS_CODE_METHOD_NOT_CAPABLE;
    $response['message'] = 'The payment method @method instance (@instance_id) does not implement a valid card on file "charge callback".';
    return $response;
  }

  // invoke callback function
  $method_return = $func($payment_method, $card, $order, $charge);

  // process return from gateway module
  // Backwards compatibility: returned value can be a boolean FALSE or a
  // specified failure code.
  if ($method_return === FALSE || !in_array($method_return, array(
    COMMERCE_PAYMENT_STATUS_SUCCESS,
    COMMERCE_PAYMENT_STATUS_PENDING,
  ))) {

    // Failure
    $response['status'] = FALSE;

    // If the returned value doesn't specify the failure code, save a generic
    // one.
    $response['code'] = $method_return === FALSE ? COMMERCE_COF_PROCESS_CODE_METHOD_FAILURE : $method_return;
    $response['message'] = 'The payment method @method instance (@instance_id) failed for order @order_id, user @uid, card @card_id, charge amount @charge.';
  }
  else {

    // Success
    $response['status'] = TRUE;
    $response['code'] = $method_return;
    $response['message'] = 'The payment method  @method instance (@instance_id) was successful for order @order_id, user @uid, card @card_id, charge amount @charge.';

    // load a fresh order in case it was modified during method callback
    $order = commerce_order_load($order->order_id);
    $order_wrapper = entity_metadata_wrapper('commerce_order', $order);

    // Store the last processed card on the order card reference field
    if (isset($order_wrapper->commerce_cardonfile)) {
      $order_ref_card_id = $order_wrapper->commerce_cardonfile
        ->value();
      if (empty($order_ref_card_id) || $order_ref_card_id != $card->card_id) {
        $order_wrapper->commerce_cardonfile = array(
          'card_id' => $card->card_id,
        );
        $order_wrapper
          ->save();
      }
    }
  }
  if (!empty($response['card_chosen'])) {
    $card_chosen = $response['card_chosen'];
  }
  elseif (!empty($card)) {
    $card_chosen = $card;
  }
  if (empty($response) || empty($response['status'])) {
    rules_invoke_all('commerce_cardonfile_charge_failed', $card_chosen, $order, $charge, $response);
  }
  else {
    rules_invoke_all('commerce_cardonfile_charge_success', $card_chosen, $order, $charge, $response);
  }
  return $response;
}