WIP
This commit is contained in:
0
docs/create_telegram_bot.md
Normal file
0
docs/create_telegram_bot.md
Normal file
@@ -15,6 +15,7 @@
|
|||||||
* @property User $user
|
* @property User $user
|
||||||
* @property ModelCustomerCustomerGroup $model_customer_customer_group
|
* @property ModelCustomerCustomerGroup $model_customer_customer_group
|
||||||
* @property ModelLocalisationOrderStatus $model_localisation_order_status
|
* @property ModelLocalisationOrderStatus $model_localisation_order_status
|
||||||
|
* @property DB $db
|
||||||
*/
|
*/
|
||||||
class ControllerExtensionModuleTgshop extends Controller
|
class ControllerExtensionModuleTgshop extends Controller
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -161,7 +161,7 @@
|
|||||||
/>
|
/>
|
||||||
<script>
|
<script>
|
||||||
$('#{{ settingKey }}-btn').click(function () {
|
$('#{{ settingKey }}-btn').click(function () {
|
||||||
const telegramToken = $('#module_tgshop_bot_token').val(); // fetch from input
|
const telegramToken = $('#module_tgshop_bot_token').val().trim(); // fetch from input
|
||||||
if (! telegramToken) {
|
if (! telegramToken) {
|
||||||
alert('Сначала введите Telegram Bot Token!');
|
alert('Сначала введите Telegram Bot Token!');
|
||||||
return;
|
return;
|
||||||
@@ -251,19 +251,19 @@
|
|||||||
</div>
|
</div>
|
||||||
<script>
|
<script>
|
||||||
$('#{{ settingKey }}-btn-test').click(function () {
|
$('#{{ settingKey }}-btn-test').click(function () {
|
||||||
const telegramToken = $('#module_tgshop_bot_token').val(); // fetch from input
|
const telegramToken = $('#module_tgshop_bot_token').val().trim();
|
||||||
if (! telegramToken) {
|
if (! telegramToken) {
|
||||||
alert('Сначала введите Telegram Bot Token!');
|
alert('Сначала введите Telegram Bot Token!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const chatId = $('#module_tgshop_chat_id').val(); // fetch from input
|
const chatId = $('#module_tgshop_chat_id').val().trim();
|
||||||
if (! chatId) {
|
if (! chatId) {
|
||||||
alert('Сначала введите Chat ID!');
|
alert('Сначала введите Chat ID!');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const template = $('#{{ settingKey }}').val();
|
const template = $('#{{ settingKey }}').val().trim();
|
||||||
if (! template) {
|
if (! template) {
|
||||||
alert('Сначала задайте шаблон!');
|
alert('Сначала задайте шаблон!');
|
||||||
return;
|
return;
|
||||||
@@ -272,7 +272,7 @@
|
|||||||
fetch('/index.php?route=extension/tgshop/handle&api_action=testTgMessage', {
|
fetch('/index.php?route=extension/tgshop/handle&api_action=testTgMessage', {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
'Content-Type': 'application/json',
|
||||||
},
|
},
|
||||||
body: JSON.stringify({
|
body: JSON.stringify({
|
||||||
token: telegramToken,
|
token: telegramToken,
|
||||||
|
|||||||
@@ -55,7 +55,8 @@ class Application extends Container
|
|||||||
$dotenv->load();
|
$dotenv->load();
|
||||||
|
|
||||||
$errorHandler = new ErrorHandler(
|
$errorHandler = new ErrorHandler(
|
||||||
$this->get(Logger::class)
|
$this->get(Logger::class),
|
||||||
|
$this,
|
||||||
);
|
);
|
||||||
|
|
||||||
$errorHandler->register();
|
$errorHandler->register();
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Openguru\OpenCartFramework\Contracts;
|
||||||
|
|
||||||
|
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
interface ExceptionHandlerInterface
|
||||||
|
{
|
||||||
|
public function respond(Throwable $exception): ?JsonResponse;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace Openguru\OpenCartFramework;
|
namespace Openguru\OpenCartFramework;
|
||||||
|
|
||||||
use ErrorException;
|
use ErrorException;
|
||||||
|
use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
|
||||||
use Openguru\OpenCartFramework\Exceptions\NonLoggableExceptionInterface;
|
use Openguru\OpenCartFramework\Exceptions\NonLoggableExceptionInterface;
|
||||||
use Openguru\OpenCartFramework\Http\JsonResponse;
|
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||||
use Openguru\OpenCartFramework\Http\Response;
|
use Openguru\OpenCartFramework\Http\Response;
|
||||||
@@ -15,10 +16,12 @@ use Throwable;
|
|||||||
class ErrorHandler
|
class ErrorHandler
|
||||||
{
|
{
|
||||||
private $logger;
|
private $logger;
|
||||||
|
private Application $app;
|
||||||
|
|
||||||
public function __construct(Logger $logger)
|
public function __construct(Logger $logger, Application $application)
|
||||||
{
|
{
|
||||||
$this->logger = $logger;
|
$this->logger = $logger;
|
||||||
|
$this->app = $application;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function register(): void
|
public function register(): void
|
||||||
@@ -42,6 +45,15 @@ class ErrorHandler
|
|||||||
|
|
||||||
public function handleException(Throwable $exception): void
|
public function handleException(Throwable $exception): void
|
||||||
{
|
{
|
||||||
|
if ($this->app->has(ExceptionHandlerInterface::class)) {
|
||||||
|
$customHandler = $this->app->get(ExceptionHandlerInterface::class);
|
||||||
|
$response = $customHandler->respond($exception);
|
||||||
|
if ($response !== null) {
|
||||||
|
$response->send();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!$exception instanceof NonLoggableExceptionInterface) {
|
if (!$exception instanceof NonLoggableExceptionInterface) {
|
||||||
$this->logger->logException($exception);
|
$this->logger->logException($exception);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ class Request
|
|||||||
private $files;
|
private $files;
|
||||||
private $server;
|
private $server;
|
||||||
private $content;
|
private $content;
|
||||||
|
private array $headers = [];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
array $query,
|
array $query,
|
||||||
@@ -20,20 +21,21 @@ class Request
|
|||||||
array $cookies,
|
array $cookies,
|
||||||
array $files,
|
array $files,
|
||||||
array $server,
|
array $server,
|
||||||
|
array $headers = [],
|
||||||
string $content = null
|
string $content = null
|
||||||
)
|
) {
|
||||||
{
|
|
||||||
$this->query = $query;
|
$this->query = $query;
|
||||||
$this->request = $request;
|
$this->request = $request;
|
||||||
$this->cookies = $cookies;
|
$this->cookies = $cookies;
|
||||||
$this->files = $files;
|
$this->files = $files;
|
||||||
$this->server = $server;
|
$this->server = $server;
|
||||||
|
$this->headers = $headers;
|
||||||
$this->content = $content;
|
$this->content = $content;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function createFromGlobals(): Request
|
public static function createFromGlobals(): Request
|
||||||
{
|
{
|
||||||
return new static($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER);
|
return new static($_GET, $_POST, $_COOKIE, $_FILES, $_SERVER, getallheaders());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getContent(): string
|
public function getContent(): string
|
||||||
@@ -84,7 +86,7 @@ class Request
|
|||||||
public function header(string $name): ?string
|
public function header(string $name): ?string
|
||||||
{
|
{
|
||||||
$headers = [];
|
$headers = [];
|
||||||
foreach (getallheaders() as $key => $value) {
|
foreach ($this->headers as $key => $value) {
|
||||||
$headers[mb_strtolower($key)] = trim($value);
|
$headers[mb_strtolower($key)] = trim($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,4 +78,13 @@ class SignatureValidator
|
|||||||
|
|
||||||
return implode(PHP_EOL, $array);
|
return implode(PHP_EOL, $array);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function ensureUserWantsToReceiveMessages($request): void
|
||||||
|
{
|
||||||
|
$initDataString = rawurldecode($request->header('X-Telegram-Initdata'));
|
||||||
|
|
||||||
|
if (! $initDataString) {
|
||||||
|
throw new TelegramInvalidSignatureException('Invalid Telegram signature!');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramInvalidSignatureException.php
Normal file → Executable file
0
module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramInvalidSignatureException.php
Normal file → Executable file
@@ -3,6 +3,9 @@
|
|||||||
namespace Openguru\OpenCartFramework\Telegram;
|
namespace Openguru\OpenCartFramework\Telegram;
|
||||||
|
|
||||||
use GuzzleHttp\Client;
|
use GuzzleHttp\Client;
|
||||||
|
use JsonException;
|
||||||
|
use Openguru\OpenCartFramework\Application;
|
||||||
|
use Openguru\OpenCartFramework\Http\Request;
|
||||||
use Openguru\OpenCartFramework\Logger\Logger;
|
use Openguru\OpenCartFramework\Logger\Logger;
|
||||||
|
|
||||||
class TelegramService
|
class TelegramService
|
||||||
@@ -18,7 +21,8 @@ class TelegramService
|
|||||||
$this->client = $this->createGuzzleClient("https://api.telegram.org/bot{$botToken}/");
|
$this->client = $this->createGuzzleClient("https://api.telegram.org/bot{$botToken}/");
|
||||||
}
|
}
|
||||||
|
|
||||||
public function escapeTelegramMarkdownV2(string $text): string {
|
public function escapeTelegramMarkdownV2(string $text): string
|
||||||
|
{
|
||||||
$specials = ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'];
|
$specials = ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'];
|
||||||
foreach ($specials as $char) {
|
foreach ($specials as $char) {
|
||||||
$text = str_replace($char, '\\' . $char, $text);
|
$text = str_replace($char, '\\' . $char, $text);
|
||||||
@@ -33,10 +37,10 @@ class TelegramService
|
|||||||
return str_replace(array_keys($variables), $values, $template);
|
return str_replace(array_keys($variables), $values, $template);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendMessage(int $chatId, string $text): bool
|
public function sendMessage(int $chatId, string $text): void
|
||||||
{
|
{
|
||||||
if (! $this->botToken) {
|
if (! $this->botToken) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$query = [
|
$query = [
|
||||||
@@ -48,8 +52,6 @@ class TelegramService
|
|||||||
$this->client->get('sendMessage', [
|
$this->client->get('sendMessage', [
|
||||||
'query' => $query,
|
'query' => $query,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createGuzzleClient(string $uri): Client
|
private function createGuzzleClient(string $uri): Client
|
||||||
@@ -66,4 +68,29 @@ class TelegramService
|
|||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function ensureUserWantsToReceiveMessages(): bool
|
||||||
|
{
|
||||||
|
/** @var Request $request */
|
||||||
|
$request = Application::getInstance()->get(Request::class);
|
||||||
|
|
||||||
|
$initDataString = $request->header('X-Telegram-Initdata');
|
||||||
|
|
||||||
|
if (! $initDataString) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_str($initDataString, $initData);
|
||||||
|
if (! isset($initData['user'])) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
$user = json_decode($initData['user'], true, 512, JSON_THROW_ON_ERROR);
|
||||||
|
return ! empty($user['allows_write_to_pm']);
|
||||||
|
} catch (JsonException $e) {
|
||||||
|
$this->logger->logException($e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
0
module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramValidateInitDataMiddleware.php
Normal file → Executable file
0
module/oc_telegram_shop/upload/oc_telegram_shop/framework/Telegram/TelegramValidateInitDataMiddleware.php
Normal file → Executable file
@@ -0,0 +1,21 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Exceptions;
|
||||||
|
|
||||||
|
use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
|
||||||
|
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||||
|
use Openguru\OpenCartFramework\Http\Response;
|
||||||
|
use Openguru\OpenCartFramework\Telegram\TelegramInvalidSignatureException;
|
||||||
|
use Throwable;
|
||||||
|
|
||||||
|
class CustomExceptionHandler implements ExceptionHandlerInterface
|
||||||
|
{
|
||||||
|
public function respond(Throwable $exception): ?JsonResponse
|
||||||
|
{
|
||||||
|
if ($exception instanceof TelegramInvalidSignatureException) {
|
||||||
|
return new JsonResponse(['error' => 'Invalid Signature'], Response::HTTP_BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -89,10 +89,22 @@ class SettingsHandler
|
|||||||
$token = $request->json('token');
|
$token = $request->json('token');
|
||||||
$chatId = $request->json('chat_id');
|
$chatId = $request->json('chat_id');
|
||||||
|
|
||||||
|
if (! $token) {
|
||||||
|
return new JsonResponse([
|
||||||
|
'message' => 'Не задан Telegram BotToken',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! $chatId) {
|
||||||
|
return new JsonResponse([
|
||||||
|
'message' => 'Не задан ChatID.',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
$variables = [
|
$variables = [
|
||||||
'{store_name}' => $this->settings->get('oc_store_name'),
|
'{store_name}' => $this->settings->get('oc_store_name'),
|
||||||
'{order_id}' => 777,
|
'{order_id}' => 777,
|
||||||
'{customer}' => 'Иван Вастльевич',
|
'{customer}' => 'Иван Васильевич',
|
||||||
'{email}' => 'telegram@opencart.com',
|
'{email}' => 'telegram@opencart.com',
|
||||||
'{phone}' => '+79999999999',
|
'{phone}' => '+79999999999',
|
||||||
'{comment}' => 'Это тестовый заказ',
|
'{comment}' => 'Это тестовый заказ',
|
||||||
@@ -108,11 +120,13 @@ class SettingsHandler
|
|||||||
$this->telegramService
|
$this->telegramService
|
||||||
->setBotToken($token)
|
->setBotToken($token)
|
||||||
->sendMessage($chatId, $message);
|
->sendMessage($chatId, $message);
|
||||||
|
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'message' => 'Сообщение отправлено. Проверьте Telegram.',
|
'message' => 'Сообщение отправлено. Проверьте Telegram.',
|
||||||
]);
|
]);
|
||||||
} catch (ClientException $exception) {
|
} catch (ClientException $exception) {
|
||||||
$json = json_decode($exception->getResponse()->getBody(), true);
|
$json = json_decode($exception->getResponse()->getBody(), true);
|
||||||
|
|
||||||
return new JsonResponse([
|
return new JsonResponse([
|
||||||
'message' => $json['description'],
|
'message' => $json['description'],
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -2,7 +2,9 @@
|
|||||||
|
|
||||||
namespace App\ServiceProviders;
|
namespace App\ServiceProviders;
|
||||||
|
|
||||||
|
use App\Exceptions\CustomExceptionHandler;
|
||||||
use Openguru\OpenCartFramework\Container\ServiceProvider;
|
use Openguru\OpenCartFramework\Container\ServiceProvider;
|
||||||
|
use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
|
||||||
use Openguru\OpenCartFramework\Router\Router;
|
use Openguru\OpenCartFramework\Router\Router;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
@@ -10,5 +12,8 @@ class AppServiceProvider extends ServiceProvider
|
|||||||
public function register(): void
|
public function register(): void
|
||||||
{
|
{
|
||||||
$this->container->get(Router::class)->loadRoutesFromFile(__DIR__ . '/../routes.php');
|
$this->container->get(Router::class)->loadRoutesFromFile(__DIR__ . '/../routes.php');
|
||||||
|
$this->container->singleton(ExceptionHandlerInterface::class, function () {
|
||||||
|
return new CustomExceptionHandler();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -283,11 +283,13 @@ class CartService
|
|||||||
|
|
||||||
$lastTotal = $totals[count($totals) - 1] ?? false;
|
$lastTotal = $totals[count($totals) - 1] ?? false;
|
||||||
$data['total'] = $lastTotal ? $lastTotal['value'] : 0;
|
$data['total'] = $lastTotal ? $lastTotal['value'] : 0;
|
||||||
|
$data['total_text'] = $lastTotal ? $this->oc->currency->format($lastTotal['value'], $this->oc->session->data['currency']) : 0;
|
||||||
$data['total_products_count'] = $this->oc->cart->countProducts();
|
$data['total_products_count'] = $this->oc->cart->countProducts();
|
||||||
} else {
|
} else {
|
||||||
$data['text_error'] = $this->oc->language->get('text_empty');
|
$data['text_error'] = $this->oc->language->get('text_empty');
|
||||||
$data['totals'] = [];
|
$data['totals'] = [];
|
||||||
$data['total'] = 0;
|
$data['total'] = 0;
|
||||||
|
$data['total_text'] = '';
|
||||||
$data['products'] = [];
|
$data['products'] = [];
|
||||||
$data['total_products_count'] = 0;
|
$data['total_products_count'] = 0;
|
||||||
unset($this->oc->session->data['success']);
|
unset($this->oc->session->data['success']);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use Exception;
|
|||||||
use Openguru\OpenCartFramework\Config\Settings;
|
use Openguru\OpenCartFramework\Config\Settings;
|
||||||
use Openguru\OpenCartFramework\Logger\Logger;
|
use Openguru\OpenCartFramework\Logger\Logger;
|
||||||
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
|
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
|
||||||
|
use Openguru\OpenCartFramework\Support\Arr;
|
||||||
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
||||||
use Rakit\Validation\Validator;
|
use Rakit\Validation\Validator;
|
||||||
use RuntimeException;
|
use RuntimeException;
|
||||||
@@ -82,7 +83,7 @@ class OrderCreateService
|
|||||||
$orderId = null;
|
$orderId = null;
|
||||||
|
|
||||||
$this->database->transaction(
|
$this->database->transaction(
|
||||||
function () use ($orderData, $products, $totals, $orderStatusId, $now, &$orderId) {
|
function () use (&$orderData, $products, $totals, $orderStatusId, $now, &$orderId) {
|
||||||
$success = $this->database->insert(db_table('order'), $orderData);
|
$success = $this->database->insert(db_table('order'), $orderData);
|
||||||
|
|
||||||
if (! $success) {
|
if (! $success) {
|
||||||
@@ -163,52 +164,10 @@ class OrderCreateService
|
|||||||
|
|
||||||
$this->cartService->flush();
|
$this->cartService->flush();
|
||||||
|
|
||||||
$chatId = $this->settings->get('telegram.chat_id');
|
$orderData['order_id'] = $orderId;
|
||||||
$template = $this->settings->get('telegram.owner_notification_template');
|
$orderData['total'] = $cart['total_text'] ?? '';
|
||||||
$variables = [
|
|
||||||
'{store_name}' => $orderData['store_name'],
|
|
||||||
'{order_id}' => $orderId,
|
|
||||||
'{customer}' => $orderData['firstname'] . ' ' . $orderData['lastname'],
|
|
||||||
'{email}' => $orderData['email'],
|
|
||||||
'{phone}' => $orderData['telephone'],
|
|
||||||
'{comment}' => $orderData['comment'],
|
|
||||||
'{address}' => $orderData['shipping_address_1'],
|
|
||||||
'{total}' => $total,
|
|
||||||
'{ip}' => $orderData['ip'],
|
|
||||||
'{created_at}' => $now,
|
|
||||||
];
|
|
||||||
|
|
||||||
if ($chatId && $template) {
|
$this->sendNotifications($orderData, $data['tgData']);
|
||||||
$message = $this->telegramService->prepareMessage($template, $variables);
|
|
||||||
try {
|
|
||||||
$this->telegramService->sendMessage($chatId, $message);
|
|
||||||
} catch (Exception $exception) {
|
|
||||||
$this->logger->error(
|
|
||||||
'Telegram sendMessage error: ' . json_encode([
|
|
||||||
'chat_id' => $chatId,
|
|
||||||
'text' => $message,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
$this->logger->logException($exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$customerChatId = $data['tgData']['id'] ?? null;
|
|
||||||
$template = $this->settings->get('telegram.customer_notification_template');
|
|
||||||
if ($customerChatId && $template) {
|
|
||||||
$message = $this->telegramService->prepareMessage($template, $variables);
|
|
||||||
try {
|
|
||||||
$this->telegramService->sendMessage($customerChatId, $message);
|
|
||||||
} catch (Exception $exception) {
|
|
||||||
$this->logger->error(
|
|
||||||
'Telegram sendMessage error: ' . json_encode([
|
|
||||||
'chat_id' => $chatId,
|
|
||||||
'text' => $message,
|
|
||||||
])
|
|
||||||
);
|
|
||||||
$this->logger->logException($exception);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function validate(array $data): void
|
private function validate(array $data): void
|
||||||
@@ -230,4 +189,47 @@ class OrderCreateService
|
|||||||
throw new OrderValidationFailedException($validation->errors());
|
throw new OrderValidationFailedException($validation->errors());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function sendNotifications(array $orderData, array $tgInitData): void
|
||||||
|
{
|
||||||
|
$variables = [
|
||||||
|
'{store_name}' => $orderData['store_name'],
|
||||||
|
'{order_id}' => $orderData['order_id'],
|
||||||
|
'{customer}' => $orderData['firstname'] . ' ' . $orderData['lastname'],
|
||||||
|
'{email}' => $orderData['email'],
|
||||||
|
'{phone}' => $orderData['telephone'],
|
||||||
|
'{comment}' => $orderData['comment'],
|
||||||
|
'{address}' => $orderData['shipping_address_1'],
|
||||||
|
'{total}' => $orderData['total'],
|
||||||
|
'{ip}' => $orderData['ip'],
|
||||||
|
'{created_at}' => $orderData['date_added'],
|
||||||
|
];
|
||||||
|
|
||||||
|
$chatId = $this->settings->get('telegram.chat_id');
|
||||||
|
$template = $this->settings->get('telegram.owner_notification_template');
|
||||||
|
|
||||||
|
if ($chatId && $template) {
|
||||||
|
$message = $this->telegramService->prepareMessage($template, $variables);
|
||||||
|
try {
|
||||||
|
$this->telegramService->sendMessage($chatId, $message);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
$this->logger->error("Telegram sendMessage to owner error. ChatID: $chatId, Message: $message");
|
||||||
|
$this->logger->logException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowsWriteToPm = Arr::get($tgInitData, 'user.allows_write_to_pm', false);
|
||||||
|
$customerChatId = Arr::get($tgInitData, 'user.id');
|
||||||
|
$template = $this->settings->get('telegram.customer_notification_template');
|
||||||
|
|
||||||
|
if ($allowsWriteToPm && $customerChatId && $template) {
|
||||||
|
$message = $this->telegramService->prepareMessage($template, $variables);
|
||||||
|
try {
|
||||||
|
$this->telegramService->sendMessage($customerChatId, $message);
|
||||||
|
} catch (Exception $exception) {
|
||||||
|
$this->logger->error("Telegram sendMessage to customer error. ChatID: $chatId, Message: $message");
|
||||||
|
$this->logger->logException($exception);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -19,9 +19,6 @@ app
|
|||||||
.use(VueTelegramPlugin);
|
.use(VueTelegramPlugin);
|
||||||
|
|
||||||
const settings = useSettingsStore();
|
const settings = useSettingsStore();
|
||||||
const categoriesStore = useCategoriesStore();
|
|
||||||
categoriesStore.fetchTopCategories();
|
|
||||||
categoriesStore.fetchCategories();
|
|
||||||
|
|
||||||
settings.load()
|
settings.load()
|
||||||
.then(() => {
|
.then(() => {
|
||||||
@@ -37,6 +34,11 @@ settings.load()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.then(() => {
|
||||||
|
const categoriesStore = useCategoriesStore();
|
||||||
|
categoriesStore.fetchTopCategories();
|
||||||
|
categoriesStore.fetchCategories();
|
||||||
|
})
|
||||||
.then(() => new AppMetaInitializer(settings).init())
|
.then(() => new AppMetaInitializer(settings).init())
|
||||||
.then(() => app.mount('#app'))
|
.then(() => app.mount('#app'))
|
||||||
.then(() => window.Telegram.WebApp.ready())
|
.then(() => window.Telegram.WebApp.ready())
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ export const useCartStore = defineStore('cart', {
|
|||||||
|
|
||||||
getters: {
|
getters: {
|
||||||
canCheckout: (state) => {
|
canCheckout: (state) => {
|
||||||
if (state.isLoading) {
|
if (state.isLoading || state.error_warning.length > 0) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ export const useCheckoutStore = defineStore('checkout', {
|
|||||||
tgData: null,
|
tgData: null,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
isLoading: false,
|
||||||
validationErrors: {},
|
validationErrors: {},
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@@ -27,19 +28,28 @@ export const useCheckoutStore = defineStore('checkout', {
|
|||||||
actions: {
|
actions: {
|
||||||
async makeOrder() {
|
async makeOrder() {
|
||||||
try {
|
try {
|
||||||
|
this.isLoading = true;
|
||||||
const data = window.Telegram.WebApp.initDataUnsafe;
|
const data = window.Telegram.WebApp.initDataUnsafe;
|
||||||
|
|
||||||
if (! data.allows_write_to_pm) {
|
console.log("Allows write to PM: ", data.user.allows_write_to_pm);
|
||||||
await window.Telegram.WebApp.requestWriteAccess((granted) => {
|
|
||||||
|
if (! data.user.allows_write_to_pm) {
|
||||||
|
console.log("Sending request");
|
||||||
|
const granted = await new Promise(resolve => {
|
||||||
|
window.Telegram.WebApp.requestWriteAccess((granted) => {
|
||||||
|
resolve(granted);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (granted) {
|
if (granted) {
|
||||||
|
data.user.allows_write_to_pm = true;
|
||||||
console.log('Пользователь разрешил отправку сообщений');
|
console.log('Пользователь разрешил отправку сообщений');
|
||||||
} else {
|
} else {
|
||||||
alert('Вы не дали разрешение — бот не сможет отправлять вам уведомления');
|
alert('Вы не дали разрешение — бот не сможет отправлять вам уведомления');
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.customer.tgData = data.user;
|
this.customer.tgData = data;
|
||||||
await storeOrder(this.customer);
|
await storeOrder(this.customer);
|
||||||
await window.Telegram.WebApp.HapticFeedback.notificationOccurred('success');
|
await window.Telegram.WebApp.HapticFeedback.notificationOccurred('success');
|
||||||
await useCartStore().getProducts();
|
await useCartStore().getProducts();
|
||||||
@@ -53,6 +63,8 @@ export const useCheckoutStore = defineStore('checkout', {
|
|||||||
window.Telegram.WebApp.HapticFeedback.notificationOccurred('error');
|
window.Telegram.WebApp.HapticFeedback.notificationOccurred('error');
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -90,7 +90,9 @@
|
|||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
:disabled="cart.canCheckout === false"
|
:disabled="cart.canCheckout === false"
|
||||||
@click="goToCheckout"
|
@click="goToCheckout"
|
||||||
>Перейти к оформлению</button>
|
>
|
||||||
|
Перейти к оформлению
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,14 @@
|
|||||||
<div
|
<div
|
||||||
class="fixed px-4 pb-10 pt-4 bottom-0 left-0 w-full bg-base-200 z-50 flex flex-col justify-between items-center gap-2 border-t-1 border-t-base-300">
|
class="fixed px-4 pb-10 pt-4 bottom-0 left-0 w-full bg-base-200 z-50 flex flex-col justify-between items-center gap-2 border-t-1 border-t-base-300">
|
||||||
<div v-if="error" class="text-error text-sm">{{ error }}</div>
|
<div v-if="error" class="text-error text-sm">{{ error }}</div>
|
||||||
<button class="btn btn-primary w-full" @click="onCreateBtnClick">Создать заказ</button>
|
<button
|
||||||
|
:disabled="checkout.isLoading"
|
||||||
|
class="btn btn-primary w-full"
|
||||||
|
@click="onCreateBtnClick"
|
||||||
|
>
|
||||||
|
<span v-if="checkout.isLoading" class="loading loading-spinner loading-sm"></span>
|
||||||
|
{{ btnText }}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
@@ -65,13 +72,16 @@ import {useCheckoutStore} from "@/stores/CheckoutStore.js";
|
|||||||
import TgInput from "@/components/Form/TgInput.vue";
|
import TgInput from "@/components/Form/TgInput.vue";
|
||||||
import TgTextarea from "@/components/Form/TgTextarea.vue";
|
import TgTextarea from "@/components/Form/TgTextarea.vue";
|
||||||
import {useRouter} from "vue-router";
|
import {useRouter} from "vue-router";
|
||||||
import {ref} from "vue";
|
import {computed, ref} from "vue";
|
||||||
|
|
||||||
const checkout = useCheckoutStore();
|
const checkout = useCheckoutStore();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
|
||||||
const error = ref(null);
|
const error = ref(null);
|
||||||
|
|
||||||
|
const btnText = computed(() => {
|
||||||
|
return checkout.isLoading ? 'Подождите...' : 'Создать заказ';
|
||||||
|
});
|
||||||
|
|
||||||
async function onCreateBtnClick() {
|
async function onCreateBtnClick() {
|
||||||
try {
|
try {
|
||||||
error.value = null;
|
error.value = null;
|
||||||
|
|||||||
@@ -72,9 +72,10 @@
|
|||||||
<button
|
<button
|
||||||
class="btn btn-primary btn-lg w-full"
|
class="btn btn-primary btn-lg w-full"
|
||||||
:class="isInCart ? 'btn-success' : 'btn-primary'"
|
:class="isInCart ? 'btn-success' : 'btn-primary'"
|
||||||
:disabled="canAddToCart === false"
|
:disabled="cart.isLoading || canAddToCart === false"
|
||||||
@click="actionBtnClick"
|
@click="actionBtnClick"
|
||||||
>
|
>
|
||||||
|
<span v-if="cart.isLoading" class="loading loading-spinner loading-sm"></span>
|
||||||
{{ btnText }}
|
{{ btnText }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user