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