Files
ibanpro/ibanpro.php
2025-11-15 15:02:22 +02:00

374 lines
14 KiB
PHP

<?php
if (!defined('_PS_VERSION_')) {
exit;
}
class IbanPro extends PaymentModule
{
public $paymentMethodName = 'IBAN';
public function __construct()
{
$this->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('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 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;
}
}