WIP
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
* @property User $user
|
||||
* @property ModelCustomerCustomerGroup $model_customer_customer_group
|
||||
* @property ModelLocalisationOrderStatus $model_localisation_order_status
|
||||
* @property DB $db
|
||||
*/
|
||||
class ControllerExtensionModuleTgshop extends Controller
|
||||
{
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
/>
|
||||
<script>
|
||||
$('#{{ 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) {
|
||||
alert('Сначала введите Telegram Bot Token!');
|
||||
return;
|
||||
@@ -251,19 +251,19 @@
|
||||
</div>
|
||||
<script>
|
||||
$('#{{ 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) {
|
||||
alert('Сначала введите Telegram Bot Token!');
|
||||
return;
|
||||
}
|
||||
|
||||
const chatId = $('#module_tgshop_chat_id').val(); // fetch from input
|
||||
const chatId = $('#module_tgshop_chat_id').val().trim();
|
||||
if (! chatId) {
|
||||
alert('Сначала введите Chat ID!');
|
||||
return;
|
||||
}
|
||||
|
||||
const template = $('#{{ settingKey }}').val();
|
||||
const template = $('#{{ settingKey }}').val().trim();
|
||||
if (! template) {
|
||||
alert('Сначала задайте шаблон!');
|
||||
return;
|
||||
@@ -272,7 +272,7 @@
|
||||
fetch('/index.php?route=extension/tgshop/handle&api_action=testTgMessage', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
token: telegramToken,
|
||||
|
||||
@@ -55,7 +55,8 @@ class Application extends Container
|
||||
$dotenv->load();
|
||||
|
||||
$errorHandler = new ErrorHandler(
|
||||
$this->get(Logger::class)
|
||||
$this->get(Logger::class),
|
||||
$this,
|
||||
);
|
||||
|
||||
$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;
|
||||
|
||||
use ErrorException;
|
||||
use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
|
||||
use Openguru\OpenCartFramework\Exceptions\NonLoggableExceptionInterface;
|
||||
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||
use Openguru\OpenCartFramework\Http\Response;
|
||||
@@ -15,10 +16,12 @@ use Throwable;
|
||||
class ErrorHandler
|
||||
{
|
||||
private $logger;
|
||||
private Application $app;
|
||||
|
||||
public function __construct(Logger $logger)
|
||||
public function __construct(Logger $logger, Application $application)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->app = $application;
|
||||
}
|
||||
|
||||
public function register(): void
|
||||
@@ -42,6 +45,15 @@ class ErrorHandler
|
||||
|
||||
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) {
|
||||
$this->logger->logException($exception);
|
||||
}
|
||||
|
||||
@@ -13,33 +13,35 @@ class Request
|
||||
private $files;
|
||||
private $server;
|
||||
private $content;
|
||||
private array $headers = [];
|
||||
|
||||
public function __construct(
|
||||
array $query,
|
||||
array $request,
|
||||
array $cookies,
|
||||
array $files,
|
||||
array $server,
|
||||
array $query,
|
||||
array $request,
|
||||
array $cookies,
|
||||
array $files,
|
||||
array $server,
|
||||
array $headers = [],
|
||||
string $content = null
|
||||
)
|
||||
{
|
||||
) {
|
||||
$this->query = $query;
|
||||
$this->request = $request;
|
||||
$this->cookies = $cookies;
|
||||
$this->files = $files;
|
||||
$this->server = $server;
|
||||
$this->headers = $headers;
|
||||
$this->content = $content;
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
if ($this->content === null || $this->content === '') {
|
||||
$this->content = (string)file_get_contents('php://input');
|
||||
$this->content = (string) file_get_contents('php://input');
|
||||
}
|
||||
|
||||
return $this->content;
|
||||
@@ -49,7 +51,7 @@ class Request
|
||||
{
|
||||
$content = $this->getContent();
|
||||
|
||||
if (!$content) {
|
||||
if (! $content) {
|
||||
return $default;
|
||||
}
|
||||
|
||||
@@ -84,7 +86,7 @@ class Request
|
||||
public function header(string $name): ?string
|
||||
{
|
||||
$headers = [];
|
||||
foreach (getallheaders() as $key => $value) {
|
||||
foreach ($this->headers as $key => $value) {
|
||||
$headers[mb_strtolower($key)] = trim($value);
|
||||
}
|
||||
|
||||
|
||||
@@ -78,4 +78,13 @@ class SignatureValidator
|
||||
|
||||
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;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use JsonException;
|
||||
use Openguru\OpenCartFramework\Application;
|
||||
use Openguru\OpenCartFramework\Http\Request;
|
||||
use Openguru\OpenCartFramework\Logger\Logger;
|
||||
|
||||
class TelegramService
|
||||
@@ -18,7 +21,8 @@ class TelegramService
|
||||
$this->client = $this->createGuzzleClient("https://api.telegram.org/bot{$botToken}/");
|
||||
}
|
||||
|
||||
public function escapeTelegramMarkdownV2(string $text): string {
|
||||
public function escapeTelegramMarkdownV2(string $text): string
|
||||
{
|
||||
$specials = ['_', '*', '[', ']', '(', ')', '~', '`', '>', '#', '+', '-', '=', '|', '{', '}', '.', '!'];
|
||||
foreach ($specials as $char) {
|
||||
$text = str_replace($char, '\\' . $char, $text);
|
||||
@@ -33,10 +37,10 @@ class TelegramService
|
||||
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) {
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
$query = [
|
||||
@@ -48,8 +52,6 @@ class TelegramService
|
||||
$this->client->get('sendMessage', [
|
||||
'query' => $query,
|
||||
]);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function createGuzzleClient(string $uri): Client
|
||||
@@ -66,4 +68,29 @@ class TelegramService
|
||||
|
||||
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');
|
||||
$chatId = $request->json('chat_id');
|
||||
|
||||
if (! $token) {
|
||||
return new JsonResponse([
|
||||
'message' => 'Не задан Telegram BotToken',
|
||||
]);
|
||||
}
|
||||
|
||||
if (! $chatId) {
|
||||
return new JsonResponse([
|
||||
'message' => 'Не задан ChatID.',
|
||||
]);
|
||||
}
|
||||
|
||||
$variables = [
|
||||
'{store_name}' => $this->settings->get('oc_store_name'),
|
||||
'{order_id}' => 777,
|
||||
'{customer}' => 'Иван Вастльевич',
|
||||
'{customer}' => 'Иван Васильевич',
|
||||
'{email}' => 'telegram@opencart.com',
|
||||
'{phone}' => '+79999999999',
|
||||
'{comment}' => 'Это тестовый заказ',
|
||||
@@ -108,11 +120,13 @@ class SettingsHandler
|
||||
$this->telegramService
|
||||
->setBotToken($token)
|
||||
->sendMessage($chatId, $message);
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => 'Сообщение отправлено. Проверьте Telegram.',
|
||||
]);
|
||||
} catch (ClientException $exception) {
|
||||
$json = json_decode($exception->getResponse()->getBody(), true);
|
||||
|
||||
return new JsonResponse([
|
||||
'message' => $json['description'],
|
||||
]);
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
namespace App\ServiceProviders;
|
||||
|
||||
use App\Exceptions\CustomExceptionHandler;
|
||||
use Openguru\OpenCartFramework\Container\ServiceProvider;
|
||||
use Openguru\OpenCartFramework\Contracts\ExceptionHandlerInterface;
|
||||
use Openguru\OpenCartFramework\Router\Router;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
@@ -10,5 +12,8 @@ class AppServiceProvider extends ServiceProvider
|
||||
public function register(): void
|
||||
{
|
||||
$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;
|
||||
$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();
|
||||
} else {
|
||||
$data['text_error'] = $this->oc->language->get('text_empty');
|
||||
$data['totals'] = [];
|
||||
$data['total'] = 0;
|
||||
$data['total_text'] = '';
|
||||
$data['products'] = [];
|
||||
$data['total_products_count'] = 0;
|
||||
unset($this->oc->session->data['success']);
|
||||
|
||||
@@ -8,6 +8,7 @@ use Exception;
|
||||
use Openguru\OpenCartFramework\Config\Settings;
|
||||
use Openguru\OpenCartFramework\Logger\Logger;
|
||||
use Openguru\OpenCartFramework\QueryBuilder\Connections\ConnectionInterface;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
||||
use Rakit\Validation\Validator;
|
||||
use RuntimeException;
|
||||
@@ -82,7 +83,7 @@ class OrderCreateService
|
||||
$orderId = null;
|
||||
|
||||
$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);
|
||||
|
||||
if (! $success) {
|
||||
@@ -163,52 +164,10 @@ class OrderCreateService
|
||||
|
||||
$this->cartService->flush();
|
||||
|
||||
$chatId = $this->settings->get('telegram.chat_id');
|
||||
$template = $this->settings->get('telegram.owner_notification_template');
|
||||
$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,
|
||||
];
|
||||
$orderData['order_id'] = $orderId;
|
||||
$orderData['total'] = $cart['total_text'] ?? '';
|
||||
|
||||
if ($chatId && $template) {
|
||||
$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);
|
||||
}
|
||||
}
|
||||
$this->sendNotifications($orderData, $data['tgData']);
|
||||
}
|
||||
|
||||
private function validate(array $data): void
|
||||
@@ -230,4 +189,47 @@ class OrderCreateService
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user