feat: добавлена функциональность политики конфиденциальности и согласия на обработку ПД
Основные изменения: Backend: - Добавлена миграция для поля privacy_consented_at в таблицу telecart_customers - Создан PrivacyPolicyHandler с методами: * checkIsUserPrivacyConsented - проверка наличия согласия пользователя * userPrivacyConsent - сохранение согласия пользователя - Обновлен TelegramService для извлечения userId из initData - Обновлен TelegramServiceProvider для внедрения зависимостей - Добавлены новые маршруты в routes.php - Обновлен SettingsHandler для возврата privacy_policy_link - Обновлен TelegramCustomersHandler для включения privacy_consented_at в ответы - Обновлены тесты TelegramServiceTest Frontend (SPA): - Создан компонент PrivacyPolicy.vue для отображения запроса согласия - Добавлена проверка согласия при инициализации приложения (main.js) - Обновлен App.vue для отображения компонента PrivacyPolicy - Добавлены функции checkIsUserPrivacyConsented и userPrivacyConsent в ftch.js - Обновлен SettingsStore для хранения privacy_policy_link и is_privacy_consented Frontend (Admin): - Добавлено поле privacy_policy_link в настройки (settings.js) - Добавлена настройка ссылки на политику конфиденциальности в GeneralView.vue - Обновлен CustomersView.vue: * Добавлена колонка privacy_consented_at с отображением даты согласия * Добавлена поддержка help-текста для колонок с иконкой вопроса и tooltip * Добавлены help-тексты для колонок last_seen_at, privacy_consented_at, created_at * Улучшено форматирование кода
This commit is contained in:
@@ -88,6 +88,7 @@ class TelegramCustomersHandler
|
||||
'photo_url',
|
||||
'last_seen_at',
|
||||
'referral',
|
||||
'privacy_consented_at',
|
||||
'created_at',
|
||||
'updated_at',
|
||||
])
|
||||
@@ -322,6 +323,7 @@ class TelegramCustomersHandler
|
||||
'photo_url' => $customer['photo_url'],
|
||||
'last_seen_at' => $customer['last_seen_at'],
|
||||
'referral' => $customer['referral'],
|
||||
'privacy_consented_at' => $customer['privacy_consented_at'],
|
||||
'created_at' => $customer['created_at'],
|
||||
'updated_at' => $customer['updated_at'],
|
||||
];
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?php
|
||||
|
||||
use Openguru\OpenCartFramework\Migrations\Migration;
|
||||
|
||||
return new class extends Migration {
|
||||
public function up(): void
|
||||
{
|
||||
$sql = <<<SQL
|
||||
ALTER TABLE `telecart_customers`
|
||||
ADD COLUMN `privacy_consented_at` TIMESTAMP NULL DEFAULT NULL AFTER `referral`;
|
||||
SQL;
|
||||
|
||||
$this->database->statement($sql);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -7,15 +7,24 @@ use GuzzleHttp\Exception\ClientException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Telegram\Enums\ChatAction;
|
||||
use Openguru\OpenCartFramework\Telegram\Exceptions\DecodeTelegramInitDataException;
|
||||
use Openguru\OpenCartFramework\Telegram\Exceptions\TelegramClientException;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class TelegramService
|
||||
{
|
||||
private ?string $botToken;
|
||||
private TelegramInitDataDecoder $initDataDecoder;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(?string $botToken = null)
|
||||
{
|
||||
public function __construct(
|
||||
TelegramInitDataDecoder $initDataDecoder,
|
||||
LoggerInterface $logger,
|
||||
?string $botToken = null
|
||||
) {
|
||||
$this->botToken = $botToken;
|
||||
$this->initDataDecoder = $initDataDecoder;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function escapeTelegramMarkdownV2(string $text): string
|
||||
@@ -194,4 +203,21 @@ class TelegramService
|
||||
|
||||
return $response['result'];
|
||||
}
|
||||
|
||||
public function userId(?string $initDataRaw = null): ?int
|
||||
{
|
||||
if (! $initDataRaw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
$decoded = $this->initDataDecoder->decode($initDataRaw);
|
||||
|
||||
return Arr::get($decoded, 'user.id');
|
||||
} catch (DecodeTelegramInitDataException $e) {
|
||||
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,11 @@ class TelegramServiceProvider extends ServiceProvider
|
||||
$this->container->singleton(TelegramService::class, function (Application $app) {
|
||||
$botToken = $app->getConfigValue('telegram.bot_token');
|
||||
|
||||
return new TelegramService($botToken);
|
||||
return new TelegramService(
|
||||
$app->get(TelegramInitDataDecoder::class),
|
||||
$app->get(LoggerInterface::class),
|
||||
$botToken,
|
||||
);
|
||||
});
|
||||
|
||||
$this->container->singleton(SignatureValidator::class, function (Application $app) {
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace App\Handlers;
|
||||
|
||||
use App\Models\TelegramCustomer;
|
||||
use Carbon\Carbon;
|
||||
use Openguru\OpenCartFramework\Http\JsonResponse;
|
||||
use Openguru\OpenCartFramework\Http\Request;
|
||||
use Openguru\OpenCartFramework\Http\Response;
|
||||
use Openguru\OpenCartFramework\Support\Arr;
|
||||
use Openguru\OpenCartFramework\Telegram\Enums\TelegramHeader;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
||||
use Psr\Log\LoggerInterface;
|
||||
|
||||
class PrivacyPolicyHandler
|
||||
{
|
||||
private TelegramService $telegramService;
|
||||
private TelegramCustomer $telegramCustomer;
|
||||
private LoggerInterface $logger;
|
||||
|
||||
public function __construct(
|
||||
TelegramService $telegramService,
|
||||
TelegramCustomer $telegramCustomer,
|
||||
LoggerInterface $logger
|
||||
) {
|
||||
$this->telegramService = $telegramService;
|
||||
$this->telegramCustomer = $telegramCustomer;
|
||||
$this->logger = $logger;
|
||||
}
|
||||
|
||||
public function checkIsUserPrivacyConsented(Request $request): JsonResponse
|
||||
{
|
||||
$isPrivacyConsented = false;
|
||||
$telegramUserId = $this->telegramService->userId($request->header(TelegramHeader::INIT_DATA));
|
||||
|
||||
if (! $telegramUserId) {
|
||||
return new JsonResponse([
|
||||
'data' => [
|
||||
'is_privacy_consented' => false,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
$customer = $this->telegramCustomer->findByTelegramUserId($telegramUserId);
|
||||
|
||||
if ($customer) {
|
||||
$isPrivacyConsented = Arr::get($customer, 'privacy_consented_at') !== null;
|
||||
}
|
||||
|
||||
return new JsonResponse([
|
||||
'data' => [
|
||||
'is_privacy_consented' => $isPrivacyConsented,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function userPrivacyConsent(Request $request): JsonResponse
|
||||
{
|
||||
$telegramUserId = $this->telegramService->userId($request->header(TelegramHeader::INIT_DATA));
|
||||
|
||||
if ($telegramUserId) {
|
||||
$this->telegramCustomer->updateByTelegramUserId($telegramUserId, [
|
||||
'privacy_consented_at' => Carbon::now()->toDateTimeString(),
|
||||
]);
|
||||
} else {
|
||||
$this->logger->warning(
|
||||
'Could not find customer with telegram user_id: ' . $telegramUserId . ' to give privacy consent.'
|
||||
);
|
||||
}
|
||||
|
||||
return new JsonResponse([], Response::HTTP_NO_CONTENT);
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,7 @@ class SettingsHandler
|
||||
'currency_code' => $this->settings->config()->getStore()->getOcDefaultCurrency(),
|
||||
'texts' => $this->settings->config()->getTexts()->toArray(),
|
||||
'mainpage_blocks' => $this->settings->get('mainpage_blocks', []),
|
||||
'privacy_policy_link' => $this->settings->get('app.privacy_policy_link'),
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,13 +7,15 @@ use App\Handlers\FiltersHandler;
|
||||
use App\Handlers\FormsHandler;
|
||||
use App\Handlers\HealthCheckHandler;
|
||||
use App\Handlers\OrderHandler;
|
||||
use App\Handlers\PrivacyPolicyHandler;
|
||||
use App\Handlers\ProductsHandler;
|
||||
use App\Handlers\SettingsHandler;
|
||||
use App\Handlers\TelegramHandler;
|
||||
use App\Handlers\TelegramCustomerHandler;
|
||||
use App\Handlers\TelegramHandler;
|
||||
|
||||
return [
|
||||
'categoriesList' => [CategoriesHandler::class, 'index'],
|
||||
'checkIsUserPrivacyConsented' => [PrivacyPolicyHandler::class, 'checkIsUserPrivacyConsented'],
|
||||
'checkout' => [CartHandler::class, 'checkout'],
|
||||
'filtersForMainPage' => [FiltersHandler::class, 'getFiltersForMainPage'],
|
||||
'getCart' => [CartHandler::class, 'index'],
|
||||
@@ -27,5 +29,6 @@ return [
|
||||
'settings' => [SettingsHandler::class, 'index'],
|
||||
'storeOrder' => [OrderHandler::class, 'store'],
|
||||
'testTgMessage' => [SettingsHandler::class, 'testTgMessage'],
|
||||
'userPrivacyConsent' => [PrivacyPolicyHandler::class, 'userPrivacyConsent'],
|
||||
'webhook' => [TelegramHandler::class, 'webhook'],
|
||||
];
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Telegram;
|
||||
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramInitDataDecoder;
|
||||
use Openguru\OpenCartFramework\Telegram\TelegramService;
|
||||
use Tests\TestCase;
|
||||
|
||||
@@ -12,7 +13,10 @@ class TelegramServiceTest extends TestCase
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
$this->service = new TelegramService();
|
||||
$this->service = new TelegramService(
|
||||
new TelegramInitDataDecoder(),
|
||||
$this->getNullLogger(),
|
||||
);
|
||||
}
|
||||
|
||||
public function testDoesNotEscapeNormalCharacters(): void
|
||||
|
||||
Reference in New Issue
Block a user