name = 'ibanpro'; $this->tab = 'advertising_marketing'; $this->version = '1.0.0'; $this->author = 'panariga'; $this->need_instance = 0; parent::__construct(); $this->displayName = $this->trans('Iban.pro'); $this->description = $this->trans('Iban.pro module for Ukraine'); $this->confirmUninstall = $this->trans('Are you sure about removing these details?'); $this->ps_versions_compliancy = array( 'min' => '1.7', 'max' => _PS_VERSION_, ); } public function install() { return parent::install() && $this->registerHook('displayOrderConfirmation') && $this->registerHook('displayOrderDetail') && $this->registerHook('paymentOptions'); } public function uninstall() { if (!parent::uninstall()) { return false; } return true; } /** * Entry point for the module's configuration page. * Handles form submission and displays the configuration form. */ public function getContent() { $output = ''; // Process form submission if it occurs if (Tools::isSubmit('submitIbanTransferModule')) { $output .= $this->postProcess(); } // Display the configuration form return $output . $this->renderForm(); } /** * Saves the configuration settings when the form is submitted. */ protected function postProcess() { if (Tools::isSubmit('submitIbanTransferModule')) { // A list of configuration keys to update. This makes it easy to add more later. $configKeys = [ 'IBANTRANSFER_IBAN', 'IBANTRANSFER_RECEIVER_NAME', 'IBANTRANSFER_RECEIVER_CODE', 'IBANTRANSFER_DESCRIPTION_APPEND', 'IBANTRANSFER_API_TOKEN', 'IBANTRANSFER_OS_CREATION', 'IBANTRANSFER_OS_FULL_PAYMENT', 'IBANTRANSFER_OS_PARTIAL_PAYMENT', ]; foreach ($configKeys as $key) { // We use Tools::getValue which sanitizes the input Configuration::updateValue($key, Tools::getValue($key)); } return $this->displayConfirmation($this->l('Settings have been updated successfully.')); } return ''; } /** * Renders the configuration form using PrestaShop's HelperForm. */ public function renderForm() { // Define the structure of the configuration form $fields_form = []; // === Fieldset 1: Bank Account Details === $fields_form[0]['form'] = [ 'legend' => [ 'title' => $this->l('Bank Account Details'), 'icon' => 'icon-university', // A more appropriate icon ], 'input' => [ [ 'type' => 'text', 'label' => $this->l('IBAN Account'), 'name' => 'IBANTRANSFER_IBAN', 'required' => true, 'desc' => $this->l('Enter the full IBAN for receiving payments.'), 'class' => 'fixed-width-xxl', ], [ 'type' => 'text', 'label' => $this->l('Receiver Name'), 'name' => 'IBANTRANSFER_RECEIVER_NAME', 'required' => true, 'desc' => $this->l('Enter the full name of the account holder (individual or company).'), 'class' => 'fixed-width-xl', ], [ 'type' => 'text', 'label' => $this->l('Receiver ID Code (EDRPOU/TIN)'), 'name' => 'IBANTRANSFER_RECEIVER_CODE', 'required' => true, 'desc' => $this->l('Enter the unique identification code for the receiver.'), 'class' => 'fixed-width-lg', ], [ 'type' => 'text', 'label' => $this->l('Append to Payment Description'), 'name' => 'IBANTRANSFER_DESCRIPTION_APPEND', 'required' => false, 'desc' => $this->l('This text will be added after the default description (e.g., "Payment for order #..."). You can use {order_reference} or {cart_id} as placeholders.'), 'class' => 'fixed-width-xxl', ], ], 'submit' => [ 'title' => $this->l('Save'), 'class' => 'btn btn-default pull-right', ], ]; // === Fieldset 2: Order Statuses Configuration === $order_states = OrderState::getOrderStates((int)$this->context->language->id); $fields_form[1]['form'] = [ 'legend' => [ 'title' => $this->l('Order Statuses'), 'icon' => 'icon-cogs', ], 'input' => [ [ 'type' => 'select', 'label' => $this->l('Order Status on Creation'), 'name' => 'IBANTRANSFER_OS_CREATION', 'options' => [ 'query' => $order_states, 'id' => 'id_order_state', 'name' => 'name', ], 'desc' => $this->l('The status for a new order created with this payment method (e.g., "Awaiting bank wire payment").'), ], [ 'type' => 'select', 'label' => $this->l('Order Status on Full Payment'), 'name' => 'IBANTRANSFER_OS_FULL_PAYMENT', 'options' => [ 'query' => $order_states, 'id' => 'id_order_state', 'name' => 'name', ], 'desc' => $this->l('The status when the full payment is confirmed (e.g., "Payment accepted").'), ], [ 'type' => 'select', 'label' => $this->l('Order Status on Partial Payment'), 'name' => 'IBANTRANSFER_OS_PARTIAL_PAYMENT', 'options' => [ 'query' => $order_states, 'id' => 'id_order_state', 'name' => 'name', ], 'desc' => $this->l('Optional: The status if a partial payment is received (e.g., "On backorder (paid)").'), ], ], 'submit' => [ 'title' => $this->l('Save'), 'class' => 'btn btn-default pull-right', ], ]; // === Fieldset 3: API Integration === $fields_form[2]['form'] = [ 'legend' => [ 'title' => $this->l('API Integration (iban.pro)'), 'icon' => 'icon-key', ], 'input' => [ [ 'type' => 'password', // Using 'password' type hides the token 'label' => $this->l('iban.pro Access Token'), 'name' => 'IBANTRANSFER_API_TOKEN', 'required' => false, 'desc' => $this->l('Enter your API access token from iban.pro to generate QR codes.'), 'class' => 'fixed-width-xxl', ], ], 'submit' => [ 'title' => $this->l('Save'), 'class' => 'btn btn-default pull-right', ], ]; // --- HelperForm Boilerplate --- $helper = new HelperForm(); $helper->module = $this; $helper->name_controller = $this->name; $helper->token = Tools::getAdminTokenLite('AdminModules'); $helper->currentIndex = AdminController::$currentIndex . '&configure=' . $this->name; $helper->default_form_language = (int)Configuration::get('PS_LANG_DEFAULT'); $helper->title = $this->displayName; $helper->show_toolbar = true; $helper->toolbar_scroll = true; $helper->submit_action = 'submitIbanTransferModule'; // Load current values from the database $configFields = [ 'IBANTRANSFER_IBAN', 'IBANTRANSFER_RECEIVER_NAME', 'IBANTRANSFER_RECEIVER_CODE', 'IBANTRANSFER_DESCRIPTION_APPEND', 'IBANTRANSFER_API_TOKEN', 'IBANTRANSFER_OS_CREATION', 'IBANTRANSFER_OS_FULL_PAYMENT', 'IBANTRANSFER_OS_PARTIAL_PAYMENT' ]; foreach ($configFields as $field) { $helper->fields_value[$field] = Configuration::get($field); } return $helper->generateForm($fields_form); } public function hookdisplayOrderDetail(array $params) { return $this->hookdisplayOrderConfirmation($params); } public function hookdisplayOrderConfirmation($params) { return $this->getTPL($params['order']); } public function getTPL(Order $order) { $address = new Address($order->id_address_invoice); $total = $order->total_paid; $description = $order->reference . '; оплата замовлення #' . $order->id . '; ' . $address->lastname . ' ' . $address->firstname; $NBULink = $this->getNBUv2($total, $description); if (trim(shell_exec('command -v qrencode'))) { // 3. If it exists, generate the QR code. // CRITICAL: Always use escapeshellarg() on variables passed to shell commands // to prevent command injection vulnerabilities. $escapedNBULink = escapeshellarg($NBULink); $qr = shell_exec("qrencode -o - -s 4 -t SVG $escapedNBULink"); // 4. (Optional but recommended) Add a final check in case the command fails for other reasons. if (empty($qr)) { $qr = $this->context->smarty->fetch($this->local_path . 'views/templates/front/emptyQR.svg'); } } else { // 5. If 'qrencode' is not found, assign the placeholder. $qr = $this->context->smarty->fetch($this->local_path . 'views/templates/front/emptyQR.svg'); } if ($order->payment == $this->paymentMethodName) { $this->context->smarty->assign([ 'paymentMethodName' => 'ibanpro', ]); } $this->context->smarty->assign([ 'reciever_name' => Configuration::get('IBANTRANSFER_RECEIVER_NAME'), 'iban' => Configuration::get('IBANTRANSFER_IBAN'), 'reciever_code' => Configuration::get('IBANTRANSFER_RECEIVER_CODE'), 'description' => $description . ' ' . Configuration::get('IBANTRANSFER_DESCRIPTION_APPEND'), 'total' => $total, 'NBULink' => $NBULink, 'qr' => $qr, ]); return $this->display(__FILE__, '/views/templates/front/displayOrderConfirmation.tpl'); } public function getNBUv2($amount, $description) { $display = ''; $ibanSUM = sprintf('UAH%s', number_format((float) $amount, 2, '.', '')); $codeContentsV2 = implode("\n", array( mb_convert_encoding('BCD', 'ASCII'), mb_convert_encoding(sprintf('%03d', '2'), 'ASCII'), mb_convert_encoding('2', 'ASCII'), mb_convert_encoding('UCT', 'ASCII'), '', mb_convert_encoding(Configuration::get('IBANTRANSFER_RECEIVER_NAME'), 'windows-1251'), mb_convert_encoding(Configuration::get('IBANTRANSFER_IBAN'), 'ASCII'), mb_convert_encoding($ibanSUM, 'ASCII'), mb_convert_encoding(Configuration::get('IBANTRANSFER_RECEIVER_CODE'), 'windows-1251'), '', '', mb_convert_encoding($description, 'windows-1251'), $display, '', )); return 'https://bank.gov.ua/qr/' . $this->base64url_encode($codeContentsV2); } public static function base64url_encode($data) { // First of all you should encode $data to Base64 string $b64 = base64_encode($data); // Make sure you get a valid result, otherwise, return FALSE, as the base64_encode() function do if ($b64 === false) { return false; } // Convert Base64 to Base64URL by replacing “+” with “-” and “/” with “_” $url = strtr($b64, '+/', '-_'); // Remove padding character from the end of line and return the Base64URL result return rtrim($url, '='); } public function hookPaymentOptions($params) { if (!$this->active) { return; } if (!Configuration::get('IBANTRANSFER_IBAN') || !Configuration::get('IBANTRANSFER_RECEIVER_CODE') || !Configuration::get('IBANTRANSFER_RECEIVER_NAME')) { return; } $payment_options = [ $this->getPaymentOption(), ]; return $payment_options; } public function getPaymentOption() { $externalOption = new \PrestaShop\PrestaShop\Core\Payment\PaymentOption(); $externalOption->setCallToActionText($this->l('Оплата переказом за реквізитами IBAN')) ->setAction($this->context->link->getModuleLink($this->name, 'validation', ['methodPayment' => 'onlinePayment'], true)) // ->setInputs([ 'onlinePayment' => true, ]) ->setAdditionalInformation($this->context->smarty->fetch('module:' . $this->name . '/views/templates/front/payment_info.tpl')) // ->setLogo(Media::getMediaPath(_PS_MODULE_DIR_ . $this->name . '/payment.jpg')) ; return $externalOption; } }