/**
  * Process an standard auth/vault transaction
  *
  * @param Realex_API $realex_client realex api client
  * @param WC_Order $order the order
  * @param array $data optional post data to use in place of $_POST
  * @param boolean $increment_retry_count optional whether to increment the retry
  *        count to avoid order number clashes, defaults to true.  It's important
  *        that this be true for, and only for, the first request of any transaction
  */
 public function authorize($realex_client, $order, $data = null, $increment_retry_count = true)
 {
     // paying with a tokenized cc?
     $vault_card_ref = $this->get_post('realex_card_ref', $data);
     // subscription support, this is a scheduled subscription payment, and not a live payment
     // TODO: this is terrible, refactor all this when possible
     $subscription = false;
     if (get_post_meta($order->id, '_realex_cardref')) {
         $vault_card_ref = get_post_meta($order->id, '_realex_cardref', true);
         $subscription = true;
     }
     // user in the vault?
     $vault_payer_ref = null;
     if ($order->get_user_id()) {
         $vault_payer_ref = get_user_meta($order->get_user_id(), 'woocommerce_realex_payerref', true);
     }
     // create/update the customer in the vault as needed
     if ($this->vault_available() && !$subscription) {
         if ($vault_payer_ref) {
             // update the vault payer info
             $this->update_vault_payer($realex_client, $vault_payer_ref, $order);
         }
         // new vault payer (non-guest checkout)
         if (!$vault_payer_ref && $this->get_post('realex_vault_new', $data) && $order->get_user_id()) {
             $vault_payer_ref = $this->get_new_payer_ref($order->get_user_id());
             $this->create_new_vault_payer($realex_client, $vault_payer_ref, $order);
         }
         // add a new card?
         if ($vault_payer_ref && $this->get_post('realex_vault_new', $data) && !$vault_card_ref) {
             $card_type = $this->get_post('realex_cardType', $data);
             // generate a unique vault card ref and add the card
             $vault_card_ref = $this->get_new_card_ref($order->get_user_id(), $card_type);
             $vault_new_card_response = $this->create_new_vault_card($realex_client, $vault_payer_ref, $vault_card_ref, $order, $data);
             if ($vault_new_card_response && $vault_new_card_response->result == '501' && stripos($vault_new_card_response->message, 'There is no such payer ref') !== false) {
                 // the payerref we have on file is invalid: delete it and attempt to create a new one and add this card to it
                 $this->delete_payer_ref($order->get_user_id());
                 $vault_payer_ref = $this->get_new_payer_ref($order->get_user_id());
                 $this->create_new_vault_payer($realex_client, $vault_payer_ref, $order);
                 if ($vault_payer_ref) {
                     // if vault payer was added try one last time to add the card
                     $vault_card_ref = $this->get_new_card_ref($order->get_user_id(), $card_type);
                     $this->create_new_vault_card($realex_client, $vault_payer_ref, $vault_card_ref, $order, $data);
                 }
             }
         }
     }
     // with subscriptions, the payment total can be zero for the initial setup
     if ($order->payment_total == 0) {
         if ($vault_card_ref) {
             // record the card token used for this order
             update_post_meta($order->id, '_realex_cardref', (string) $vault_card_ref);
         }
         return;
     }
     $cards = null;
     if ($vault_card_ref) {
         // paying using an existing token
         $cards = get_user_meta($order->get_user_id(), 'woocommerce_realex_cc', true);
         // validate the input
         if (!isset($cards[$vault_card_ref])) {
             SV_WC_Helper::wc_add_notice(__("An error occurred, try an alternate form of payment", 'woocommerce-gateway-realex'), 'error');
             return;
         }
         // perform the auth transaction using the supplied token (never 3DSecure)
         $response = $this->vault_auth_request($realex_client, $vault_payer_ref, $cards[$vault_card_ref]['type'], $vault_card_ref, $order);
     } else {
         // perform the regular auth transaction
         $response = $this->auth_request($realex_client, $order, $data, $increment_retry_count);
     }
     if (!$response) {
         SV_WC_Helper::wc_add_notice(__('Connection error', 'woocommerce-gateway-realex'), 'error');
         return;
     }
     if ('00' == $response->result) {
         // Successful payment
         if (!$realex_client->verify_transaction_signature($response)) {
             // response was not properly signed by realex
             SV_WC_Helper::wc_add_notice(__('Error - invalid transaction signature, check your Realex settings.', 'woocommerce-gateway-realex'), 'error');
             // if debug mode load the response into the messages object
             if ($this->is_debug_mode()) {
                 $this->response_debug_message($response, $vault_card_ref ? "Response: receipt-in" : "Response: auth", 'error');
             }
             return;
         }
         // if debug mode load the response into the messages object
         if ($this->is_debug_mode()) {
             $this->response_debug_message($response, $vault_card_ref ? "Response: receipt-in" : "Response: auth", 'message', true);
         }
         // collect the credit card information used for this transaction, whether it was a vaulted transaction or a regular
         if ($vault_card_ref) {
             $card = (object) $cards[$vault_card_ref];
             $card_type = $card->type;
             $last4 = $card->last4;
             $expiration_month = $card->expiration_month;
             $expiration_year = $card->expiration_year;
         } else {
             $card_type = $this->get_post('realex_cardType', $data);
             $last4 = substr($this->get_post('realex_accountNumber', $data), -4);
             $expiration_month = $this->get_post('realex_expirationMonth', $data);
             $expiration_year = $this->get_post('realex_expirationYear', $data);
         }
         // update the order record with success
         /* translators: Placeholders: %1$s - credit card type, %2$s - credit card last 4, %3$s - credit card expiration MM/YY */
         $order_note = sprintf(__('Credit Card Transaction Approved: %1$s ending in %2$s (%3$s).', 'woocommerce-gateway-realex'), $this->card_type_options[$card_type], $last4, $expiration_month . '/' . $expiration_year);
         $order_note = apply_filters('wc_gateway_realex_order_note', $order_note, $order, $data, $card_type, $cards);
         // avs response, if check was performed.
         if ($this->avs_check()) {
             // There's also 'M' for Matched, and 'X' if AVS check was not performed
             $avs_response_codes = array('N' => 'Not matched', 'I' => 'Problem with check', 'U' => 'Unable to check', 'P' => 'Partial match');
             $code = (string) $response->avspostcoderesponse;
             if (isset($avs_response_codes[$code])) {
                 $order_note .= "\n" . sprintf(__('AVS post code response: %s', 'woocommerce-gateway-realex'), $avs_response_codes[$code]);
             }
             $code = (string) $response->avsaddressresponse;
             if (isset($avs_response_codes[$code])) {
                 $order_note .= "\n" . sprintf(__('AVS address response: %s', 'woocommerce-gateway-realex'), $avs_response_codes[$code]);
             }
         }
         if (!$subscription) {
             // normal execution
             $order->add_order_note($order_note);
             $order->payment_complete();
             // store the payment reference in the order (add_post_meta not update_post_meta)
             add_post_meta($order->id, '_realex_payment_reference', (string) $response->pasref);
             if ($vault_card_ref) {
                 // record the card token used for this order (add_post_meta not update_post_meta)
                 add_post_meta($order->id, '_realex_cardref', (string) $vault_card_ref);
             }
             WC()->cart->empty_cart();
             // Return thank you redirect
             return array('result' => 'success', 'redirect' => $this->get_return_url($order));
         } else {
             // special subscription handling
             return $response;
         }
     } else {
         // Failure: it's important that the order status be set to 'failed' so that a new order number can be generated,
         // because Realex does not allow the same order number to be used once a payment attempt has failed
         /* translators: Placeholders: %1$s - response result, %2$s - failure message */
         $error_note = sprintf(__('Realex Credit Card payment failed (Result: %1$s - "%2$s").', 'woocommerce-gateway-realex'), $response->result, $response->message);
         if (!$subscription) {
             $this->order_failed($order, $error_note);
         }
         $message = __('The transaction has been declined by your bank, please contact your bank for more details, or try an alternative payment method.  Your order has been recorded, please contact us if you wish to provide payment over the phone.', 'woocommerce-gateway-realex');
         if ($vault_card_ref && $response->result == 520) {
             if (stripos($response->message, 'There is no such Payer') !== false) {
                 // payerref does not exist
                 $this->delete_payer_ref($order->get_user_id());
                 $message = __("Internal error, please use an alternate form of payment", 'woocommerce-gateway-realex');
             } elseif (stripos($response->message, 'There is no such Payment Method') !== false) {
                 // card ref does not exist
                 $this->delete_card_ref($vault_card_ref);
                 $message = __("Unknown card, please use an alternate form of payment", 'woocommerce-gateway-realex');
             }
         }
         // provide some default error message
         if (SV_WC_Helper::wc_notice_count('error') == 0) {
             if ($message) {
                 SV_WC_Helper::wc_add_notice($message, 'error');
             } else {
                 SV_WC_Helper::wc_add_notice(__("An error occurred, please try again or try an alternate form of payment", 'woocommerce-gateway-realex'), 'error');
             }
         }
         // if debug mode load the response into the messages object
         if ($this->is_debug_mode()) {
             $this->response_debug_message($response, $vault_card_ref ? "Response: receipt-in" : "Response: auth", 'error');
         }
         // special subscription handling
         if ($subscription) {
             return $response;
         }
     }
 }
 /**
  * Pay page tokenized payment method checkout process, adapted from
  * WooCommerce core
  *
  * @since 1.7.1
  */
 public function process_checkout()
 {
     $wc_gateway_realex = new WC_Gateway_Realex();
     // Validate
     $wc_gateway_realex->validate_fields();
     // Process
     if (SV_WC_Helper::wc_notice_count('error') == 0) {
         // Process Payment
         $result = $wc_gateway_realex->process_payment($_POST['order_id']);
         // Redirect to success/confirmation/payment page
         if ('success' == $result['result']) {
             $result = apply_filters('woocommerce_payment_successful_result', $result);
             if (is_ajax()) {
                 echo '<!--WC_START-->' . json_encode($result) . '<!--WC_END-->';
                 exit;
             } else {
                 wp_redirect($result['redirect']);
                 exit;
             }
         }
     }
     // If we reached this point then there were errors
     if (is_ajax()) {
         ob_start();
         wc_print_notices();
         $messages = ob_get_clean();
         $response = array('result' => 'failure', 'messages' => isset($messages) ? $messages : '');
         wp_send_json($response);
         exit;
     }
 }