Создание модуля оплаты Bitcoin в OpenCart: часть 1

29 Мая 2022г. в 05:07

Прежде чем приступить к созданию расширения, установите OpenCart на локальную машину. Затем скачайте BitPay API Library из вложения в эту статью и поместите эту папку в корень магазина, как показано на скриншоте ниже:



Для создания и настройки Bitcoin Payment Module нам нужен интерфейс администратора. Поэтому мы создадим нашу панель администратора по шаблону MVC.

Создание контроллера админки
Перейдите в admin/controller/payment

Создайте PHP файл и назовите его bitpay.php

Откройте этот файл в вашей IDE и создайте класс, придерживаясь способа именования OpenCart. Например, class ControllerPaymentBitpay extends Controller {}

Внутри класса контроллера определите стандартную функцию index

Внутри этой функции вставьте код ниже

Загрузка языка и моделей
$this->language->load('payment/bitpay');
$this->load->model('localisation/order_status'); // loading the model to get system Order Statuses

$this->language->load('payment/bitpay');
$this->load->model('localisation/order_status'); // loading the model to get system Order Statuses
Настройка заголовка страницы
Заголовок страницы задается через:

$this->document->setTitle($this->language->get('text_title')); // Setting the document heading title

$this->document->setTitle($this->language->get('text_title')); // Setting the document heading title
Парсинг элементов контроллера в представление
/* Assigning and parsing the from values */

$this->data['heading_title'] = $this->language->get('text_title');
$this->data['text_enabled'] = $this->language->get('text_enabled'); // gets default language text
$this->data['text_disabled'] = $this->language->get('text_disabled'); // gets default language text
$this->data['text_high'] = $this->language->get('text_high');
$this->data['text_medium'] = $this->language->get('text_medium');
$this->data['text_low'] = $this->language->get('text_low');
$this->data['api_key'] = $this->language->get('text_api_key');
$this->data['confirmed_status'] = $this->language->get('text_confirmed_status');
$this->data['invalid_status'] = $this->language->get('text_invalid_status');
$this->data['transaction_speed'] = $this->language->get('text_transaction_speed');
$this->data['test_mode'] = $this->language->get('text_test_mode');
$this->data['status'] = $this->language->get('text_status');
$this->data['sort_order'] = $this->language->get('text_sort_order');
$this->data['button_save'] = $this->language->get('button_save'); // gets default language text
$this->data['button_cancel'] = $this->language->get('button_cancel'); // gets default language text
$this->data['tab_general'] = $this->language->get('tab_general'); // gets default language text

/* End Text Assignation */
/* Assigning and parsing the from values */

$this->data['heading_title'] = $this->language->get('text_title');
$this->data['text_enabled'] = $this->language->get('text_enabled'); // gets default language text
$this->data['text_disabled'] = $this->language->get('text_disabled'); // gets default language text
$this->data['text_high'] = $this->language->get('text_high');
$this->data['text_medium'] = $this->language->get('text_medium');
$this->data['text_low'] = $this->language->get('text_low');
$this->data['api_key'] = $this->language->get('text_api_key');
$this->data['confirmed_status'] = $this->language->get('text_confirmed_status');
$this->data['invalid_status'] = $this->language->get('text_invalid_status');
$this->data['transaction_speed'] = $this->language->get('text_transaction_speed');
$this->data['test_mode'] = $this->language->get('text_test_mode');
$this->data['status'] = $this->language->get('text_status');
$this->data['sort_order'] = $this->language->get('text_sort_order');
$this->data['button_save'] = $this->language->get('button_save'); // gets default language text
$this->data['button_cancel'] = $this->language->get('button_cancel'); // gets default language text
$this->data['tab_general'] = $this->language->get('tab_general'); // gets default language text

/* End Text Assignation */
Роутинг кнопок save и cancel
Следующий код назначает триггеры на Action / Save & Cancel URL’ы:

$this->data['action'] = HTTPS_SERVER . 'index.php?route=payment/'.$this->payment_module_name.'&token=' . $this->session->data['token'];
$this->data['cancel'] = HTTPS_SERVER . 'index.php?route=extension/payment&token=' . $this->session->data['token'];

$this->data['action'] = HTTPS_SERVER . 'index.php?route=payment/'.$this->payment_module_name.'&token=' . $this->session->data['token'];
$this->data['cancel'] = HTTPS_SERVER . 'index.php?route=extension/payment&token=' . $this->session->data['token'];
Получение доступных статусов заказа
Следующий код берет все статусы заказа из Localisation-Model (которая захардкодена в OpenCart) и парсит их в представление:

$this->data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();

$this->data['order_statuses'] = $this->model_localisation_order_status->getOrderStatuses();
Получение данных из базы данных
Наш процесс сохранению переходит в раздел модели. Следующий код вытягивает данные модуля (если они есть) и парсит их в представление.

/* Fetching the Field Values saved in Database*/
$this->data['bitpay_api_key'] = $this->config->get('bitpay_api_key');

$this->data['bitpay_confirmed_status_id'] = $this->config->get('bitpay_confirmed_status_id');

$this->data['bitpay_invalid_status_id'] = $this->config->get('bitpay_invalid_status_id');

$this->data['bitpay_transaction_speed'] = $this->config->get('bitpay_transaction_speed');

$this->data['bitpay_test_mode'] = $this->config->get('bitpay_test_mode');

$this->data['bitpay_status'] = $this->config->get('bitpay_status');

$this->data['bitpay_sort_order'] = $this->config->get('bitpay_sort_order');
/* Database fields fetching ends */

/* Fetching the Field Values saved in Database*/
$this->data['bitpay_api_key'] = $this->config->get('bitpay_api_key');

$this->data['bitpay_confirmed_status_id'] = $this->config->get('bitpay_confirmed_status_id');

$this->data['bitpay_invalid_status_id'] = $this->config->get('bitpay_invalid_status_id');

$this->data['bitpay_transaction_speed'] = $this->config->get('bitpay_transaction_speed');

$this->data['bitpay_test_mode'] = $this->config->get('bitpay_test_mode');

$this->data['bitpay_status'] = $this->config->get('bitpay_status');

$this->data['bitpay_sort_order'] = $this->config->get('bitpay_sort_order');
/* Database fields fetching ends */
Рендеринг шаблона представления
Следующий код добавляет дочерние элементы в наш шаблон представления и рендерит его для пользователя:

$this->template = 'payment/bitpay.tpl'; // Template Assigning
$this->children = array(
'common/header',
'common/footer'
); // Provide the list of template childeren

$this->response->setOutput($this->render(TRUE), $this->config->get('config_compression')); // Rendering the Template Output

$this->template = 'payment/bitpay.tpl'; // Template Assigning
$this->children = array(
'common/header',
'common/footer'
); // Provide the list of template childeren

$this->response->setOutput($this->render(TRUE), $this->config->get('config_compression')); // Rendering the Template Output
Создание представления админки
Перейдите в admin/view/template/payment

Создайте файл bitplay.tpl

Можете поместить в него свой HTML. Я сделал это следующим образом.

Добавление хедера
Чтобы загрузить хедер, который мы передали в качестве дочернего элемента в 1.6, используйте:

<?php echo $header; ?>

<?php echo $header; ?>
Блок предупреждений
Блок предупреждений – div секция, которая будет показывать предупреждения для пользователя, если таковые есть:

<?php if ($error_warning) { ?> <div class="warning"><?php echo $error_warning; ?></div><?php } ?>

<?php if ($error_warning) { ?> <div class="warning"><?php echo $error_warning; ?></div><?php } ?>
Весь шаблон
Весь HTML будет примерно таким:

<div id="content">

<?php
/* Warning Block*/
if ($error_warning) { ?>
<div class="warning"><?php echo $error_warning; ?></div>
<?php }
/* Warning Block Ends */
?>
<div class="box">
<div class="left"></div>
<div class="right"></div>
<div class="heading">
<h1><?php echo $heading_title; ?></h1>
<div class="buttons"><a onclick="$('#form').submit();" class="button"><span><?php echo $button_save; ?></span></a><a onclick="location = '<?php echo $cancel; ?>';" class="button"><span><?php echo $button_cancel; ?></span></a></div>
</div>
<div class="content">
<form action="<?php echo $action; ?>" method="post" enctype="multipart/form-data" id="form">
<table class="form">
<tr>
<td><span class="required">*</span> <?php echo $api_key; ?></td>
<td><input type="text" name="bitpay_api_key" value="<?php echo $bitpay_api_key; ?>" style="width:300px;" />
<?php
// API Key Error case block
if ($error_api_key) { ?>
<span class="error"><?php echo $error_api_key; ?></span>
<?php }
// Error Case Block Ends
?></td>
</tr>
<tr>
<td><?php echo $confirmed_status; ?></td>
<td><select name="bitpay_confirmed_status_id">
<?php foreach ($order_statuses as $order_status) { ?>
<?php if ($order_status['order_status_id'] == $bitpay_confirmed_status_id) { ?>
<option value="<?php echo $order_status['order_status_id']; ?>" selected="selected"><?php echo $order_status['name']; ?></option>
<?php } else { ?>
<option value="<?php echo $order_status['order_status_id']; ?>"><?php echo $order_status['name']; ?></option>
<?php } ?>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $invalid_status; ?></td>
<td><select name="bitpay_invalid_status_id">
<?php foreach ($order_statuses as $order_status) { ?>
<?php if ($order_status['order_status_id'] == $bitpay_invalid_status_id) { ?>
<option value="<?php echo $order_status['order_status_id']; ?>" selected="selected"><?php echo $order_status['name']; ?></option>
<?php } else { ?>
<option value="<?php echo $order_status['order_status_id']; ?>"><?php echo $order_status['name']; ?></option>
<?php } ?>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $transaction_speed; ?></td>
<td><select name="bitpay_transaction_speed">
<?php if ($bitpay_transaction_speed == 'high') { ?>
<option value="high" selected="selected"><?php echo $text_high; ?></option>
<?php } else { ?>
<option value="high"><?php echo $text_high; ?></option>
<?php } ?>
<?php if ($bitpay_transaction_speed == 'medium') { ?>
<option value="medium" selected="selected"><?php echo $text_medium; ?></option>
<?php } else { ?>
<option value="medium"><?php echo $text_medium; ?></option>
<?php } ?>
<?php if ($bitpay_transaction_speed == 'low') { ?>
<option value="low" selected="selected"><?php echo $text_low; ?></option>
<?php } else { ?>
<option value="low"><?php echo $text_low; ?></option>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $test_mode; ?></td>
<td><select name="bitpay_test_mode">
<?php if ($bitpay_test_mode) { ?>
<option value="1" selected="selected"><?php echo $text_enabled; ?></option>
<option value="0"><?php echo $text_disabled; ?></option>
<?php } else { ?>
<option value="1"><?php echo $text_enabled; ?></option>
<option value="0" selected="selected"><?php echo $text_disabled; ?></option>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $status; ?></td>
<td><select name="bitpay_status">
<?php if ($bitpay_status) { ?>
<option value="1" selected="selected"><?php echo $text_enabled; ?></option>
<option value="0"><?php echo $text_disabled; ?></option>
<?php } else { ?>
<option value="1"><?php echo $text_enabled; ?></option>
<option value="0" selected="selected"><?php echo $text_disabled; ?></option>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $sort_order; ?></td>
<td><input type="text" name="bitpay_sort_order" value="<?php echo $bitpay_sort_order; ?>" size="1" /></td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php echo $footer; //loading the footer ?>

<div id="content">

<?php
/* Warning Block*/
if ($error_warning) { ?>
<div class="warning"><?php echo $error_warning; ?></div>
<?php }
/* Warning Block Ends */
?>
<div class="box">
<div class="left"></div>
<div class="right"></div>
<div class="heading">
<h1><?php echo $heading_title; ?></h1>
<div class="buttons"><a onclick="$('#form').submit();" class="button"><span><?php echo $button_save; ?></span></a><a onclick="location = '<?php echo $cancel; ?>';" class="button"><span><?php echo $button_cancel; ?></span></a></div>
</div>
<div class="content">
<form action="<?php echo $action; ?>" method="post" enctype="multipart/form-data" id="form">
<table class="form">
<tr>
<td><span class="required">*</span> <?php echo $api_key; ?></td>
<td><input type="text" name="bitpay_api_key" value="<?php echo $bitpay_api_key; ?>" style="width:300px;" />
<?php
// API Key Error case block
if ($error_api_key) { ?>
<span class="error"><?php echo $error_api_key; ?></span>
<?php }
// Error Case Block Ends
?></td>
</tr>
<tr>
<td><?php echo $confirmed_status; ?></td>
<td><select name="bitpay_confirmed_status_id">
<?php foreach ($order_statuses as $order_status) { ?>
<?php if ($order_status['order_status_id'] == $bitpay_confirmed_status_id) { ?>
<option value="<?php echo $order_status['order_status_id']; ?>" selected="selected"><?php echo $order_status['name']; ?></option>
<?php } else { ?>
<option value="<?php echo $order_status['order_status_id']; ?>"><?php echo $order_status['name']; ?></option>
<?php } ?>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $invalid_status; ?></td>
<td><select name="bitpay_invalid_status_id">
<?php foreach ($order_statuses as $order_status) { ?>
<?php if ($order_status['order_status_id'] == $bitpay_invalid_status_id) { ?>
<option value="<?php echo $order_status['order_status_id']; ?>" selected="selected"><?php echo $order_status['name']; ?></option>
<?php } else { ?>
<option value="<?php echo $order_status['order_status_id']; ?>"><?php echo $order_status['name']; ?></option>
<?php } ?>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $transaction_speed; ?></td>
<td><select name="bitpay_transaction_speed">
<?php if ($bitpay_transaction_speed == 'high') { ?>
<option value="high" selected="selected"><?php echo $text_high; ?></option>
<?php } else { ?>
<option value="high"><?php echo $text_high; ?></option>
<?php } ?>
<?php if ($bitpay_transaction_speed == 'medium') { ?>
<option value="medium" selected="selected"><?php echo $text_medium; ?></option>
<?php } else { ?>
<option value="medium"><?php echo $text_medium; ?></option>
<?php } ?>
<?php if ($bitpay_transaction_speed == 'low') { ?>
<option value="low" selected="selected"><?php echo $text_low; ?></option>
<?php } else { ?>
<option value="low"><?php echo $text_low; ?></option>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $test_mode; ?></td>
<td><select name="bitpay_test_mode">
<?php if ($bitpay_test_mode) { ?>
<option value="1" selected="selected"><?php echo $text_enabled; ?></option>
<option value="0"><?php echo $text_disabled; ?></option>
<?php } else { ?>
<option value="1"><?php echo $text_enabled; ?></option>
<option value="0" selected="selected"><?php echo $text_disabled; ?></option>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $status; ?></td>
<td><select name="bitpay_status">
<?php if ($bitpay_status) { ?>
<option value="1" selected="selected"><?php echo $text_enabled; ?></option>
<option value="0"><?php echo $text_disabled; ?></option>
<?php } else { ?>
<option value="1"><?php echo $text_enabled; ?></option>
<option value="0" selected="selected"><?php echo $text_disabled; ?></option>
<?php } ?>
</select></td>
</tr>
<tr>
<td><?php echo $sort_order; ?></td>
<td><input type="text" name="bitpay_sort_order" value="<?php echo $bitpay_sort_order; ?>" size="1" /></td>
</tr>
</table>
</form>
</div>
</div>
</div>
<?php echo $footer; //loading the footer ?>
Мы закончили работу с макетом модуля. Теперь необходимо сохранить Module Data в базу данных. Для этого нам понадобится вставить немного кода внутрь контроллера.

Сохранение данных
Внутри стандартной функции контроллера вставьте код, как ниже:

if (($this->request->server['REQUEST_METHOD'] == 'POST') && ($this->validate())) // Checking if the data is coming through post and validate it
{
$this->model_setting_setting->editSetting($this->payment_module_name, $this->request->post); // saving all the data into the database
$this->session->data['success'] = $this->language->get('text_success'); // Success Identifier
$this->redirect(HTTPS_SERVER . 'index.php?route=extension/payment&token=' . $this->session->data['token']); // Redirecting to the payment modules list
}

if (($this->request->server['REQUEST_METHOD'] == 'POST') && ($this->validate())) // Checking if the data is coming through post and validate it
{
$this->model_setting_setting->editSetting($this->payment_module_name, $this->request->post); // saving all the data into the database
$this->session->data['success'] = $this->language->get('text_success'); // Success Identifier
$this->redirect(HTTPS_SERVER . 'index.php?route=extension/payment&token=' . $this->session->data['token']); // Redirecting to the payment modules list
}
Валидация ввода
Код ниже – это функция валидации обязательных данных:

private $error = array(); // All errors will be saved into this array
private function validate()
{
// User Module Permission Check
if (!$this->user->hasPermission('modify', 'payment/'.$this->payment_module_name))
{
$this->error['warning'] = $this->language->get('error_permission');
}
// User Module Permission Check Ends

// Check if the API Key is not empty
if (!$this->request->post['bitpay_api_key'])
{
$this->error['api_key'] = $this->language->get('error_api_key');
}
// Api Check ends here

// If there is no error, the function returns True else False
if (!$this->error)
{
return TRUE;
}
else
{
return FALSE;
}
// Boolean Check Ends here
}

private $error = array(); // All errors will be saved into this array
private function validate()
{
// User Module Permission Check
if (!$this->user->hasPermission('modify', 'payment/'.$this->payment_module_name))
{
$this->error['warning'] = $this->language->get('error_permission');
}
// User Module Permission Check Ends

// Check if the API Key is not empty
if (!$this->request->post['bitpay_api_key'])
{
$this->error['api_key'] = $this->language->get('error_api_key');
}
// Api Check ends here

// If there is no error, the function returns True else False
if (!$this->error)
{
return TRUE;
}
else
{
return FALSE;
}
// Boolean Check Ends here
}
Мы создали макет модуля оплаты для нашей панели администратора. Можете проверить панель. Но прежде не забудьте разрешить пользователю доступ.

Заключение
В этом уроке мы изучили административную часть модуля оплаты. Лучше дать администратору сайта больше прав через интерфейс, чем заставлять его писать код. В следующей статье мы реализуем модуль оплаты для front end.