widgetName = JText::translate('VBO_W_VIRTUALTERMINAL_TITLE'); $this->widgetDescr = JText::translate('VBO_W_VIRTUALTERMINAL_DESCR'); $this->widgetId = basename(__FILE__, '.php'); // define widget and icon and style name $this->widgetIcon = ''; $this->widgetStyleName = 'red'; // whether VCM is available if (is_file(VCM_SITE_PATH . DIRECTORY_SEPARATOR . 'helpers' . DIRECTORY_SEPARATOR . 'lib.vikchannelmanager.php')) { $this->vcm_exists = true; } } /** * Custom method for this widget only to load the virtual terminal CC form. * The method is called by the admin controller through an AJAX request. * The visibility should be public, it should not exit the process, and * any content sent to output will be returned to the AJAX response. * In this case we return an array because this method requires "return":1. */ public function loadTerminalForm() { $dbo = JFactory::getDbo(); $wrapper = VikRequest::getString('wrapper', '', 'request'); $bid = VikRequest::getString('bid', '', 'request'); if (!$bid) { // booking ID is mandatory VBOHttpDocument::getInstance()->close(500, JText::translate('VBPEDITBUSYONE')); } // get booking details $booking_info = VikBooking::getBookingInfoFromID($bid); if (!$booking_info) { // booking record not found, try to see if an OTA reservation ID was given $dbo->setQuery( $dbo->getQuery(true) ->select($dbo->qn('id')) ->from($dbo->qn('#__vikbooking_orders')) ->where($dbo->qn('idorderota') . ' = ' . $dbo->q($bid)) ->where($dbo->qn('channel') . ' IS NOT NULL'), 0, 1 ); $ota_id = $dbo->loadResult(); if ($ota_id) { // overwrite booking ID value $bid = $ota_id; // get booking details $booking_info = VikBooking::getBookingInfoFromID($bid); } if (!$booking_info) { // booking record could not be found VBOHttpDocument::getInstance()->close(404, JText::translate('VBPEDITBUSYONE')); } } // ensure a valid payment processor is assigned to the reservation if (!($processor = $this->getPaymentProcessor($booking_info))) { // unable to proceed due to unsupported transaction return [ 'html' => '

' . JText::translate('VBO_PAY_PROCESS_NO_DIRECT_CHARGE') . '

', ]; } // tell whether off-session capturing is supported $off_session_capturing = method_exists($processor, 'isOffSessionCaptureSupported') && $processor->isOffSessionCaptureSupported(); $off_session_tn_data = $off_session_capturing ? VBOModelReservation::getInstance($booking_info, true)->getOffSessionTransactionData() : []; // make sure the permissions are met if ($this->vcm_exists && !JFactory::getUser()->authorise('core.admin', 'com_vikchannelmanager') && !$off_session_tn_data) { // insufficient permissions to handle CC details return [ 'html' => '

' . JText::translate('JERROR_ALERTNOAUTHOR') . '

', ]; } // get the reservation credit card value pairs, if any $cc_value_pairs = VBOModelReservation::getInstance($booking_info, true)->getCardValuePairs(); // currency code $currency_code = !empty($booking_info['chcurrency']) ? $booking_info['chcurrency'] : VikBooking::getCurrencyName(); if (empty($currency_code) || strlen((string)$currency_code) != 3) { // fallback to currency transaction code $currency_code = VikBooking::getCurrencyCodePp(); } // check known CC values to build the hidden transaction values $known_tn_vals = []; $hidden_tn_vals = []; if (isset($cc_value_pairs['name'])) { $known_tn_vals['name'] = $cc_value_pairs['name']; } if (isset($cc_value_pairs['card_number'])) { $known_tn_vals['card_number'] = $cc_value_pairs['card_number']; } if (isset($cc_value_pairs['expiration_date'])) { $known_tn_vals['expiration_date'] = $cc_value_pairs['expiration_date']; } if (isset($cc_value_pairs['cvv'])) { $known_tn_vals['cvv'] = $cc_value_pairs['cvv']; } foreach ($cc_value_pairs as $key => $value) { if (!isset($known_tn_vals[$key])) { $hidden_tn_vals[$key] = $value; } } /** * Consider calculating an outstanding balance in case of previous payments. * * @since 1.16.7 (J) - 1.6.7 (WP) * @since 1.18.0 (J) - 1.8.0 (WP) added support to amount raw for off-session capture. */ $default_total = $booking_info['total']; if ($booking_info['totpaid'] > 0 && $booking_info['totpaid'] < $booking_info['total']) { // default to the outstanding balance $default_total = $booking_info['total'] - $booking_info['totpaid']; } if (!$booking_info['totpaid'] && $off_session_tn_data && ($off_session_tn_data[0]->amount_raw ?? null)) { // default to the payable amount at the time of payment, unless empty $default_total = ((float) $off_session_tn_data[0]->amount_raw) ?: $booking_info['total']; } // tell whether we only have an off-session transaction to capture $only_off_session = $off_session_tn_data && !$cc_value_pairs; // start output buffering ob_start(); ?>
>
$value) { ?>
$html_content, ); } /** * Custom method for this widget only to debit a credit card. * * @return array|void */ public function doDirectCharge() { $wrapper = VikRequest::getString('wrapper', '', 'request'); $bid = VikRequest::getInt('bid', 0, 'request'); $card = VikRequest::getVar('card', [], 'request'); if (!$bid || !$card) { // booking ID and CC details are mandatory VBOHttpDocument::getInstance()->close(500, JText::translate('VBPEDITBUSYONE')); } // get booking details $booking_info = VikBooking::getBookingInfoFromID($bid); if (!$booking_info) { // booking record not found VBOHttpDocument::getInstance()->close(404, JText::translate('VBPEDITBUSYONE')); } // get the eligible payment processor by passing the card details $processor = $this->getPaymentProcessor($booking_info, $card); if (!$processor) { VBOHttpDocument::getInstance()->close(500, JText::translate('VBO_PAY_PROCESS_NO_DIRECT_CHARGE')); } // default transaction response $array_result = [ 'verified' => 0, ]; try { // perform the transaction $array_result = $processor->directCharge(); } catch (Exception $e) { // set error message $array_result['log'] = sprintf(JText::translate('VBO_CC_TN_ERROR') . " \n%s", $e->getMessage()); } if ($array_result['verified'] != 1) { // erroneous response if (!empty($array_result['log']) && is_string($array_result['log'])) { VBOHttpDocument::getInstance()->close(500, $array_result['log']); } else { VBOHttpDocument::getInstance()->close(500, 'Operation failed'); } } // valid transaction response! // update booking details $dbo = JFactory::getDbo(); // get the amount paid $tn_amount = isset($array_result['tot_paid']) ? (float) $array_result['tot_paid'] : null; // get the log string, if any $tn_log = !empty($array_result['log']) ? $array_result['log'] : ''; // update record $upd_record = new stdClass; $upd_record->id = $booking_info['id']; if ($tn_amount) { // update amount paid $upd_record->totpaid = $booking_info['totpaid'] + $tn_amount; // update payable amount (if needed) $new_payable = $booking_info['payable'] - $tn_amount; $new_payable = $new_payable < 0 ? 0 : $new_payable; $upd_record->payable = $new_payable; } if ($tn_log) { $upd_record->paymentlog = $booking_info['paymentlog'] . "\n\n" . date('c') . "\n" . $tn_log; } $upd_record->paymcount = ((int)$booking_info['paymcount'] + 1); // update reservation record $dbo->updateObject('#__vikbooking_orders', $upd_record, 'id'); // payment processor name $pay_process_name = $this->payment_method ? $this->payment_method['name'] : 'CC Direct Charge'; // handle transaction data to eventually support a later transaction of type refund $tn_data = isset($array_result['transaction']) ? $array_result['transaction'] : null; if ($tn_amount) { // check event data payload to store if (is_array($tn_data)) { // set key $tn_data['amount_paid'] = $tn_amount; } elseif (is_object($tn_data)) { // set property $tn_data->amount_paid = $tn_amount; } elseif (!$tn_data) { // build an array (we add the payment name because we know there is no other transaction data) $tn_data = [ 'amount_paid' => $tn_amount, 'payment_method' => $pay_process_name, ]; } } /** * Check if the payment processor returned the information about the amount of processing fees. * * @since 1.16.9 (J) - 1.6.9 (WP) */ if ($tn_data && isset($array_result['tot_fees']) && $array_result['tot_fees']) { // check event data payload to store if (is_array($tn_data)) { // set key $tn_data['processing_fees'] = (float) $array_result['tot_fees']; } elseif (is_object($tn_data)) { // set property $tn_data->processing_fees = (float) $array_result['tot_fees']; } } // current admin-user $now_user = JFactory::getUser(); // Booking History $ev_descr = JText::translate('VBO_W_VIRTUALTERMINAL_TITLE') . " - {$pay_process_name} ({$now_user->name})"; VikBooking::getBookingHistoryInstance()->setBid($booking_info['id'])->setExtraData($tn_data)->store('P' . ($booking_info['paymcount'] > 0 ? 'N' : '0'), $ev_descr); return [ 'success' => 1, 'log' => $tn_log, ]; } /** * Custom method for this widget only to debit a credit card off-session. * * @return ?array * * @since 1.18.0 (J) - 1.8.0 (WP) */ public function doOffSessionCapture() { $app = JFactory::getApplication(); $wrapper = $app->input->getString('wrapper', ''); $bid = $app->input->getUInt('bid', 0); $amount = $app->input->getFloat('amount', 0); $currency = $app->input->getString('currency', ''); if (!$bid || !$amount) { // booking ID and amount are mandatory VBOHttpDocument::getInstance()->close(500, JText::translate('VBPEDITBUSYONE')); } // get booking details $booking_info = VikBooking::getBookingInfoFromID($bid); if (!$booking_info) { // booking record not found VBOHttpDocument::getInstance()->close(404, JText::translate('VBPEDITBUSYONE')); } // collect the previous transaction data list $tn_data = VBOModelReservation::getInstance($booking_info, true)->getOffSessionTransactionData(); if (!$tn_data) { // previous transaction data not found VBOHttpDocument::getInstance()->close(400, 'Missing previous transaction data'); } // set the amount to capture and transaction data before accessing the payment processor $booking_info['total_to_pay'] = $amount; $booking_info['tn_currency'] = $currency; $booking_info['transaction'] = $tn_data[0]; // get the eligible payment processor $processor = $this->getPaymentProcessor($booking_info); if (!$processor) { VBOHttpDocument::getInstance()->close(500, JText::translate('VBO_PAY_PROCESS_NO_DIRECT_CHARGE')); } // default transaction response $array_result = [ 'verified' => 0, ]; try { // perform the transaction $array_result = $processor->offSessionCapture(); } catch (Exception $e) { // set error message $array_result['log'] = sprintf(JText::translate('VBO_CC_TN_ERROR') . " \n%s", $e->getMessage()); } if ($array_result['verified'] != 1) { // erroneous response if (!empty($array_result['log']) && is_string($array_result['log'])) { VBOHttpDocument::getInstance()->close(500, $array_result['log']); } else { VBOHttpDocument::getInstance()->close(500, 'Operation failed'); } } // valid transaction response! // update booking details $dbo = JFactory::getDbo(); // get the amount paid $tn_amount = isset($array_result['tot_paid']) ? (float) $array_result['tot_paid'] : null; // get the log string, if any $tn_log = !empty($array_result['log']) ? $array_result['log'] : ''; // update record $upd_record = new stdClass; $upd_record->id = $booking_info['id']; if ($tn_amount) { // update amount paid $upd_record->totpaid = $booking_info['totpaid'] + $tn_amount; // update payable amount (if needed) $new_payable = $booking_info['payable'] - $tn_amount; $new_payable = $new_payable < 0 ? 0 : $new_payable; $upd_record->payable = $new_payable; } if ($tn_log) { $upd_record->paymentlog = $booking_info['paymentlog'] . "\n\n" . date('c') . "\n" . $tn_log; } $upd_record->paymcount = ((int) $booking_info['paymcount'] + 1); // update reservation record $dbo->updateObject('#__vikbooking_orders', $upd_record, 'id'); // payment processor name $pay_process_name = $this->payment_method ? $this->payment_method['name'] : 'CC Manual Charge (Off-Session)'; // handle transaction data to eventually support a later transaction of type refund $tn_data = isset($array_result['transaction']) ? $array_result['transaction'] : null; if ($tn_amount) { // check event data payload to store if (is_array($tn_data)) { // set key $tn_data['amount_paid'] = $tn_amount; } elseif (is_object($tn_data)) { // set property $tn_data->amount_paid = $tn_amount; } elseif (!$tn_data) { // build an array (we add the payment name because we know there is no other transaction data) $tn_data = [ 'amount_paid' => $tn_amount, 'payment_method' => $pay_process_name, ]; } } /** * Check if the payment processor returned the information about the amount of processing fees. */ if ($tn_data && isset($array_result['tot_fees']) && $array_result['tot_fees']) { // check event data payload to store if (is_array($tn_data)) { // set key $tn_data['processing_fees'] = (float) $array_result['tot_fees']; } elseif (is_object($tn_data)) { // set property $tn_data->processing_fees = (float) $array_result['tot_fees']; } } // current admin-user $now_user = JFactory::getUser(); // Booking History $ev_descr = JText::translate('VBO_W_VIRTUALTERMINAL_TITLE') . " - {$pay_process_name} ({$now_user->name})"; VikBooking::getBookingHistoryInstance()->setBid($booking_info['id'])->setExtraData($tn_data)->store('P' . ($booking_info['paymcount'] > 0 ? 'N' : '0'), $ev_descr); return [ 'success' => 1, 'log' => $tn_log, ]; } /** * Preload the necessary assets. * * @return void */ public function preload() { // JS lang def JText::script('VBOUPLOADFILEDONE'); } /** * Main method to invoke the widget. Contents will be loaded * through AJAX requests, not via PHP when the page loads. * * @param ?VBOMultitaskData $data * * @return void */ public function render(?VBOMultitaskData $data = null) { // increase widget's instance counter static::$instance_counter++; // check whether the widget is being rendered via AJAX when adding it through the customizer $is_ajax = $this->isAjaxRendering(); // generate a unique ID for the sticky notes wrapper instance $wrapper_instance = !$is_ajax ? static::$instance_counter : rand(); $wrapper_id = 'vbo-widget-vterminal-' . $wrapper_instance; // get permissions $vbo_auth_bookings = JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking'); if (!$vbo_auth_bookings) { // display nothing return; } // check multitask data $page_bid = 0; $js_modal_id = ''; if ($data) { $is_modal_rendering = $data->isModalRendering(); $page_bid = $data->getBookingID() ?: $this->options()->fetchBookingId(); if ($is_modal_rendering) { // get modal JS identifier $js_modal_id = $data->getModalJsIdentifier(); } } if (!$page_bid) { // unable to continue from a page outside the booking details or booking ID set ?>

widgetIcon; ?> widgetName; ?>

getPaymentProcessor($card); } catch (Exception $e) { $processor = null; } return $processor; } }