Compare commits

...

9 Commits

Author SHA1 Message Date
vchikalkin
6d0711c471 Enhance Russian localization and add document handling features
- Updated the Russian localization file to include new entries for privacy policy and public offer documents, improving user access to important information.
- Added 'documents' command to the bot's command list, allowing users to easily access document-related features.
- Integrated document handling in the main menu and handlers, enhancing user experience and navigation within the bot.
2025-10-07 19:12:28 +03:00
vchikalkin
8eb2ed8ad7 Refactor Russian localization for contact agreements and enhance user consent messaging
- Updated the Russian localization file to separate and clarify the phone sharing and contact sharing agreements, improving user understanding of consent requirements.
- Modified the contact addition and welcome messages to utilize the new agreement format, ensuring users are informed about their consent to share personal data in a more structured manner.
2025-10-07 18:50:43 +03:00
vchikalkin
20b5c9ada4 Enhance Russian localization and update message formatting
- Added a new payment agreement clause in the Russian localization file, clarifying user consent for payments.
- Updated message formatting in the contact addition and subscription processes to support HTML parsing, improving message presentation and user experience.
- Incorporated the new payment agreement into the subscription flow, ensuring users are informed about their consent to the terms.
2025-10-07 18:43:49 +03:00
vchikalkin
4004a73888 Refactor phone agreement localization and enhance user consent messaging
- Updated the Russian localization file to streamline the phone sharing agreement, improving clarity on user consent for data processing.
- Modified the contact addition and welcome messages to incorporate the new agreement format, ensuring users are informed about their consent to share personal data.
2025-10-07 18:19:02 +03:00
vchikalkin
b0af20140b Add privacy agreement and update environment variables
- Introduced `PRIVACY_URL` to the environment configuration for dynamic linking.
- Updated localization files to include a user consent agreement for sharing phone numbers, linking to the offer and privacy URLs.
- Enhanced welcome and contact addition messages to incorporate the new consent clause, improving user clarity on data handling.
2025-10-07 18:04:12 +03:00
vchikalkin
b3d884a34d Update offer and privacy pages to include user consent for third-party data sharing
- Added a clause in the offer page requiring users to ensure consent from third parties when adding their contact information.
- Updated the privacy policy to clarify that users can share third-party data, emphasizing the need for consent for data processing within the service.
2025-10-07 17:28:23 +03:00
vchikalkin
d2e065dc36 move offer & privacy -> (documents) sub directory 2025-10-07 17:18:59 +03:00
vchikalkin
b91661d779 Update environment variables and enhance offer and privacy pages
- Added `OFFER_URL` and `SUPPORT_TELEGRAM_URL` to environment variable configuration for better flexibility.
- Updated the offer page to dynamically link to the offer URL and improved contact information presentation with a direct link to the support Telegram.
- Revised the privacy policy page to reflect the service name and updated contact details, ensuring clarity and consistency in communication.
2025-10-07 17:05:00 +03:00
vchikalkin
ac053a54b1 apps/web: add generic privacy & offer pages 2025-10-07 16:41:28 +03:00
16 changed files with 643 additions and 13 deletions

View File

@ -45,6 +45,8 @@ commands-list =
Откройте приложение кнопкой "Открыть", чтобы отредактировать свой профиль или создать запись Откройте приложение кнопкой "Открыть", чтобы отредактировать свой профиль или создать запись
support = support =
{ -support-contact } { -support-contact }
documents =
.description = Документы
# Кнопки # Кнопки
btn-add-contact = 👤 Добавить контакт btn-add-contact = 👤 Добавить контакт
@ -53,9 +55,26 @@ btn-pro = 👑 Pro доступ
btn-subscribe = 👑 Приобрести Pro btn-subscribe = 👑 Приобрести Pro
btn-pro-info = Мой Pro доступ btn-pro-info = Мой Pro доступ
btn-open-app = 📱 Открыть приложение btn-open-app = 📱 Открыть приложение
btn-documents = 📋 Документы
btn-back = ◀️ Назад btn-back = ◀️ Назад
# Согласие
share-phone-agreement =
<i> Нажимая кнопку <b>«Отправить номер телефона»</b></i>,
<i>вы:
- соглашаетесь с <a href='{ $offerUrl }'>Публичной офертой</a>
- подтверждаете согласие на обработку персональных данных согласно <a href='{ $privacyUrl }'>Политике конфиденциальности</a></i>
share-contact-agreement =
<i> Отправляя контакт, имя и номер телефона, вы подтверждаете, что имеете согласие этого человека на передачу его контактных данных и на их обработку в рамках нашего сервиса.
(Пункт 4.5 <a href='{ $privacyUrl }'>Политики конфиденциальности</a>)</i>
payment-agreement =
Совершая оплату, вы соглашаетесь с <a href='{ $offerUrl }'>Публичной офертой</a>
agreement-links =
<a href='{ $offerUrl }'>Публичная оферта</a>
<a href='{ $privacyUrl }'>Политика конфиденциальности</a>
# Приветственные сообщения # Приветственные сообщения
msg-welcome = msg-welcome =
👋 Добро пожаловать! 👋 Добро пожаловать!
@ -64,14 +83,14 @@ msg-welcome-back = 👋 С возвращением, { $name }!
# Сообщения о телефоне # Сообщения о телефоне
msg-need-phone = 📱 Чтобы добавить контакт, сначала поделитесь своим номером телефона msg-need-phone = 📱 Чтобы добавить контакт, сначала поделитесь своим номером телефона.
msg-phone-saved = msg-phone-saved =
✅ Спасибо! Мы сохранили ваш номер телефона ✅ Спасибо! Мы сохранили ваш номер телефона
Теперь вы можете открыть приложение или воспользоваться командами бота Теперь вы можете открыть приложение или воспользоваться командами бота
msg-already-registered = msg-already-registered =
✅ Вы уже зарегистрированы в системе ✅ Вы уже зарегистрированы в системе
Для смены номера телефона обратитесь в поддержку (Контакты в профиле бота) <i>Для смены номера телефона обратитесь в поддержку (Контакты в профиле бота)</i>
msg-invalid-phone = ❌ Некорректный номер телефона. Пример: +79999999999 msg-invalid-phone = ❌ Некорректный номер телефона. Пример: +79999999999
# Сообщения о контактах # Сообщения о контактах
@ -85,7 +104,7 @@ msg-contact-added =
✅ Добавили { $fullname } в список ваших контактов ✅ Добавили { $fullname } в список ваших контактов
Пригласите пользователя в приложение, чтобы вы могли добавлять с ним записи Пригласите пользователя в приложение, чтобы вы могли добавлять с ним записи
msg-contact-forward = Перешлите пользователю следующее сообщение, чтобы он мог начать пользоваться ботом ⬇️ msg-contact-forward = <i>Перешлите пользователю следующее сообщение, чтобы он мог начать пользоваться ботом ⬇️</i>
# Сообщения для шаринга # Сообщения для шаринга
msg-share-bot = msg-share-bot =
@ -96,7 +115,7 @@ msg-share-bot =
# Системные сообщения # Системные сообщения
msg-cancel = ❌ Операция отменена msg-cancel = ❌ Операция отменена
msg-unhandled = ❓ Неизвестная команда. Попробуйте /start msg-unhandled = ❓ Неизвестная команда. Попробуйте /start
msg-cancel-operation = Для отмены операции используйте команду /cancel msg-cancel-operation = <i>Для отмены операции используйте команду /cancel</i>
# Ошибки # Ошибки
err-generic = ⚠️ Что-то пошло не так. Попробуйте еще раз через несколько секунд err-generic = ⚠️ Что-то пошло не так. Попробуйте еще раз через несколько секунд

View File

@ -1,6 +1,7 @@
/* eslint-disable sonarjs/cognitive-complexity */ /* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable id-length */ /* eslint-disable id-length */
import { type Context } from '@/bot/context'; import { type Context } from '@/bot/context';
import { env } from '@/config/env';
import { KEYBOARD_SHARE_BOT, KEYBOARD_SHARE_PHONE } from '@/config/keyboards'; import { KEYBOARD_SHARE_BOT, KEYBOARD_SHARE_PHONE } from '@/config/keyboards';
import { parseContact } from '@/utils/contact'; import { parseContact } from '@/utils/contact';
import { combine } from '@/utils/messages'; import { combine } from '@/utils/messages';
@ -21,16 +22,34 @@ export async function addContact(conversation: Conversation<Context, Context>, c
if (!customer) { if (!customer) {
return ctx.reply( return ctx.reply(
await conversation.external(({ t }) => t('msg-need-phone')), await conversation.external(({ t }) =>
KEYBOARD_SHARE_PHONE, combine(
t('msg-need-phone'),
t('share-phone-agreement', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
}),
),
),
{ ...KEYBOARD_SHARE_PHONE, parse_mode: 'HTML' },
); );
} }
// Просим отправить контакт или номер телефона // Просим отправить контакт или номер телефона
await ctx.reply( await ctx.reply(
await conversation.external(({ t }) => await conversation.external(({ t }) =>
combine(t('msg-send-client-contact-or-phone'), t('msg-cancel-operation')), combine(
t('msg-send-client-contact-or-phone'),
t('msg-cancel-operation'),
t('share-contact-agreement', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
}),
), ),
),
{
parse_mode: 'HTML',
},
); );
// Ждём первое сообщение: контакт или текст с номером // Ждём первое сообщение: контакт или текст с номером

View File

@ -66,7 +66,7 @@ export async function subscription(conversation: Conversation<Context, Context>,
return combine(statusLine, fmt`${i}${t('msg-cancel-operation')}${i}`.text); return combine(statusLine, fmt`${i}${t('msg-cancel-operation')}${i}`.text);
}), }),
), ),
{ reply_markup: keyboard }, { parse_mode: 'HTML', reply_markup: keyboard },
); );
// ждём выбора // ждём выбора
@ -95,6 +95,18 @@ export async function subscription(conversation: Conversation<Context, Context>,
month: '2-digit', month: '2-digit',
year: 'numeric', year: 'numeric',
}); });
const agreementText = await conversation.external(({ t }) => {
return t('payment-agreement', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
});
});
await ctx.reply(agreementText, {
parse_mode: 'HTML',
});
return ctx.replyWithInvoice( return ctx.replyWithInvoice(
'Оплата Pro доступа', 'Оплата Pro доступа',
combine( combine(
@ -110,6 +122,7 @@ export async function subscription(conversation: Conversation<Context, Context>,
}, },
], ],
{ {
protect_content: true,
provider_token: env.BOT_PROVIDER_TOKEN, provider_token: env.BOT_PROVIDER_TOKEN,
start_parameter: 'get_access', start_parameter: 'get_access',
}, },

View File

@ -0,0 +1,12 @@
import { handleDocuments } from '../handlers/documents';
import { type Context } from '@/bot/context';
import { logHandle } from '@/bot/helpers/logging';
import { Composer } from 'grammy';
const composer = new Composer<Context>();
const feature = composer.chatType('private');
feature.command('documents', logHandle('command-documents'), handleDocuments);
export { composer as documents };

View File

@ -1,4 +1,5 @@
export * from './add-contact'; export * from './add-contact';
export * from './documents';
export * from './help'; export * from './help';
export * from './pro'; export * from './pro';
export * from './registration'; export * from './registration';

View File

@ -1,6 +1,8 @@
import { type Context } from '@/bot/context'; import { type Context } from '@/bot/context';
import { logHandle } from '@/bot/helpers/logging'; import { logHandle } from '@/bot/helpers/logging';
import { env } from '@/config/env';
import { KEYBOARD_SHARE_PHONE, mainMenu } from '@/config/keyboards'; import { KEYBOARD_SHARE_PHONE, mainMenu } from '@/config/keyboards';
import { combine } from '@/utils/messages';
import { RegistrationService } from '@repo/graphql/api/registration'; import { RegistrationService } from '@repo/graphql/api/registration';
import { Composer } from 'grammy'; import { Composer } from 'grammy';
@ -22,7 +24,19 @@ feature.command('start', logHandle('command-start'), async (ctx) => {
} }
// Новый пользователь — просим поделиться номером // Новый пользователь — просим поделиться номером
return ctx.reply(ctx.t('msg-welcome'), { ...KEYBOARD_SHARE_PHONE, parse_mode: 'HTML' }); return ctx.reply(
combine(
ctx.t('msg-welcome'),
ctx.t('share-phone-agreement', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
}),
),
{
...KEYBOARD_SHARE_PHONE,
parse_mode: 'HTML',
},
);
}); });
export { composer as welcome }; export { composer as welcome };

View File

@ -0,0 +1,18 @@
import { type Context } from '@/bot/context';
import { env } from '@/config/env';
import { KEYBOARD_REMOVE } from '@/config/keyboards';
async function handler(ctx: Context) {
await ctx.reply(
ctx.t('agreement-links', {
offerUrl: env.OFFER_URL,
privacyUrl: env.PRIVACY_URL,
}),
{
...KEYBOARD_REMOVE,
parse_mode: 'HTML',
},
);
}
export { handler as handleDocuments };

View File

@ -1,4 +1,5 @@
export * from './add-contact'; export * from './add-contact';
export * from './documents';
export * from './pro'; export * from './pro';
export * from './share-bot'; export * from './share-bot';
export * from './subscription'; export * from './subscription';

View File

@ -5,7 +5,15 @@ import { type LanguageCode } from '@grammyjs/types';
import { type Api, type Bot, type RawApi } from 'grammy'; import { type Api, type Bot, type RawApi } from 'grammy';
export async function setCommands({ api }: Bot<Context, Api<RawApi>>) { export async function setCommands({ api }: Bot<Context, Api<RawApi>>) {
const commands = createCommands(['start', 'addcontact', 'sharebot', 'help', 'subscribe', 'pro']); const commands = createCommands([
'start',
'addcontact',
'sharebot',
'help',
'subscribe',
'pro',
'documents',
]);
for (const command of commands) { for (const command of commands) {
addLocalizations(command); addLocalizations(command);

View File

@ -4,6 +4,8 @@ export const envSchema = z.object({
BOT_PROVIDER_TOKEN: z.string(), BOT_PROVIDER_TOKEN: z.string(),
BOT_TOKEN: z.string(), BOT_TOKEN: z.string(),
BOT_URL: z.string(), BOT_URL: z.string(),
OFFER_URL: z.string(),
PRIVACY_URL: z.string(),
RATE_LIMIT: z RATE_LIMIT: z
.string() .string()
.transform((value) => Number.parseInt(value, 10)) .transform((value) => Number.parseInt(value, 10))

View File

@ -1,6 +1,12 @@
import { env } from './env'; import { env } from './env';
import { type Context } from '@/bot/context'; import { type Context } from '@/bot/context';
import { handleAddContact, handlePro, handleShareBot, handleSubscribe } from '@/bot/handlers'; import {
handleAddContact,
handleDocuments,
handlePro,
handleShareBot,
handleSubscribe,
} from '@/bot/handlers';
import { Menu } from '@grammyjs/menu'; import { Menu } from '@grammyjs/menu';
import { import {
type InlineKeyboardMarkup, type InlineKeyboardMarkup,
@ -50,6 +56,8 @@ export const mainMenu = new Menu<Context>('main-menu', { autoAnswer: true })
.row() .row()
.text((ctx) => ctx.t('btn-share-bot'), handleShareBot) .text((ctx) => ctx.t('btn-share-bot'), handleShareBot)
.row() .row()
.text((ctx) => ctx.t('btn-documents'), handleDocuments)
.row()
.url( .url(
(ctx) => ctx.t('btn-open-app'), (ctx) => ctx.t('btn-open-app'),
() => { () => {

View File

@ -0,0 +1,242 @@
import { Container } from '@/components/layout';
import { PageHeader } from '@/components/navigation';
import { env } from '@/config/env';
export default function OfferPage() {
return (
<>
<PageHeader title="Публичная оферта" />
<Container className="prose prose-neutral dark:prose-invert">
<section className="mx-auto space-y-8">
<h1 className="text-2xl font-bold">
Договор-оферта на использование сервиса «Запишись.онлайн» (@zapishis_online_bot)
</h1>
<p className="mt-4">
Настоящий документ является публичной офертой в соответствии с пунктом 2 статьи 437
Гражданского кодекса Российской Федерации и представляет собой предложение
индивидуального предпринимателя (самозанятого) далее именуемого «Администрация»,
заключить Договор на использование Сервиса (далее «Договор», «Оферта») с любым
физическим лицом, принявшим условия настоящей Оферты (далее «Пользователь»).
</p>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">1. Термины и определения</h2>
<div className="space-y-3">
<p>
<strong>1.1.</strong> Оферта настоящий документ, постоянно размещенный в сети
Интернет по адресу <a href="#">{env.OFFER_URL}</a>.
</p>
<p>
<strong>1.2.</strong> Акцепт полное и безоговорочное принятие условий Оферты
Пользователем путем оплаты доступа через встроенный платежный бот ЮKassa в Telegram.
</p>
<p>
<strong>1.3.</strong> Сервис Telegram-бот и мини-приложение, позволяющее
пользователям создавать и принимать заказы, управлять расписанием и взаимодействовать
друг с другом без необходимости регистрации.
</p>
<p>
<strong>1.4.</strong> Администрация самозанятое лицо, являющееся разработчиком и
правообладателем Сервиса.
</p>
<p>
<strong>1.5.</strong> Пользователь любое физическое лицо, использующее Сервис в
личных или профессиональных целях.
</p>
<p>
<strong>1.6.</strong> Доступ право использования функционала Сервиса на определённый
оплаченный период (например, неделя, месяц, год).
</p>
<p>
<strong>1.7.</strong> Оплата денежные средства, перечисленные Пользователем через
платёжный бот ЮKassa в Telegram.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">2. Акцепт оферты и заключение договора</h2>
<div className="space-y-3">
<p>
<strong>2.1.</strong> Акцептом настоящей Оферты считается оплата Пользователем доступа
к Сервису любым доступным способом.
</p>
<p>
<strong>2.2.</strong> С момента совершения оплаты Пользователь считается заключившим
Договор с Администрацией на условиях, изложенных в настоящей Оферте.
</p>
<p>
<strong>2.3.</strong> Пользователь подтверждает, что ему понятны все условия настоящей
Оферты и он принимает их без ограничений.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">3. Предмет договора</h2>
<div className="space-y-3">
<p>
<strong>3.1.</strong> Администрация предоставляет Пользователю неисключительное право
(доступ) на использование функционала Сервиса в пределах оплаченного периода времени.
</p>
<p>
<strong>3.2.</strong> Сервис предоставляется в онлайн-формате через Telegram-бота без
установки дополнительного программного обеспечения.
</p>
<p>
<strong>3.3.</strong> Пользователь получает право использовать функционал Сервиса в
личных целях, в том числе для организации и планирования заказов, встреч и тренировок.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">4. Порядок оплаты и использование</h2>
<div className="space-y-3">
<p>
<strong>4.1.</strong> Оплата производится через встроенные инструменты Telegram-бота с
использованием платёжной системы ЮKassa.
</p>
<p>
<strong>4.2.</strong> Комиссия платёжной системы включена в итоговую стоимость.
Администрация не взимает дополнительных платежей.
</p>
<p>
<strong>4.3.</strong> Доступ активируется автоматически после успешного подтверждения
оплаты.
</p>
<p>
<strong>4.4.</strong> Пользователь может продлить доступ путём повторной оплаты.
Автоматическое продление не применяется.
</p>
<p>
<strong>4.5.</strong> Возврат денежных средств возможен только в случае технических
ошибок, по письменному обращению на адрес поддержки.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">5. Права и обязанности сторон</h2>
<div className="space-y-3">
<p>
<strong>5.1.</strong> Пользователь обязуется:
</p>
<ul className="list-inside list-disc space-y-1">
<li>не использовать Сервис в противоправных целях;</li>
<li>
не вмешиваться в работу Сервиса и не предпринимать действий, направленных на
нарушение его функционирования;
</li>
<li>предоставлять достоверную информацию при оплате и использовании Сервиса.</li>
<li>
при добавлении контактов других лиц (например, клиентов, мастеров) гарантировать,
что у него есть согласие этих лиц на передачу и обработку их персональных данных в
рамках Сервиса.
</li>
</ul>
<p>
<strong>5.2.</strong> Администрация обязуется:
</p>
<ul className="list-inside list-disc space-y-1">
<li>
обеспечивать бесперебойную работу Сервиса, за исключением периодов технического
обслуживания;
</li>
<li>
обрабатывать персональные данные Пользователей в соответствии с{' '}
<a href="/privacy">Политикой конфиденциальности</a>;
</li>
<li>принимать обращения и запросы Пользователей по вопросам работы Сервиса.</li>
</ul>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">6. Ответственность сторон</h2>
<div className="space-y-3">
<p>
<strong>6.1.</strong> Сервис предоставляется «как есть». Администрация не несёт
ответственности за временные сбои, потерю данных или недоступность Сервиса, возникшие
по причинам, не зависящим от неё.
</p>
<p>
<strong>6.2.</strong> Пользователь несёт полную ответственность за корректность
совершаемых платежей и действий, совершаемых через свой Telegram-аккаунт.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">7. Обработка персональных данных</h2>
<div className="space-y-3">
<p>
<strong>7.1.</strong> Администрация обрабатывает персональные данные Пользователя в
соответствии с Федеральным законом 152-ФЗ «О персональных данных» и{' '}
<a href="/privacy">Политикой конфиденциальности</a>.
</p>
<p>
<strong>7.2.</strong> Использование Сервиса означает согласие Пользователя на
обработку его персональных данных.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">8. Срок действия и расторжение договора</h2>
<div className="space-y-3">
<p>
<strong>8.1.</strong> Договор вступает в силу с момента оплаты доступа и действует в
течение оплаченного периода.
</p>
<p>
<strong>8.2.</strong> Пользователь может прекратить использование Сервиса в любое
время без возврата оплаченных средств.
</p>
<p>
<strong>8.3.</strong> Администрация вправе приостановить доступ в случае нарушения
Пользователем условий настоящей Оферты.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">9. Заключительные положения</h2>
<div className="space-y-3">
<p>
<strong>9.1.</strong> Настоящий Договор регулируется законодательством Российской
Федерации.
</p>
<p>
<strong>9.2.</strong> Все споры и разногласия решаются путём переговоров, а при
недостижении соглашения в судебном порядке по месту нахождения Администрации.
</p>
<p>
<strong>9.3.</strong> Администрация оставляет за собой право изменять условия Оферты с
размещением новой редакции на сайте.
</p>
</div>
<div className="my-6 border-t border-gray-300 dark:border-gray-600" />
<h2 className="text-2xl font-semibold">10. Контакты</h2>
<p>
Если у Вас есть вопросы по настоящему договору публичной оферты персональных данных,
пожалуйста, свяжитесь с Разработчиком:
</p>
<ul>
<li>
Telegram:{' '}
<strong>
<a href={env.SUPPORT_TELEGRAM_URL}>{env.SUPPORT_TELEGRAM_URL}</a>
</strong>
</li>
</ul>
<div className="h-10" />
</section>
</Container>
</>
);
}

View File

@ -0,0 +1,266 @@
import { Container } from '@/components/layout';
import { PageHeader } from '@/components/navigation';
import { env } from '@/config/env';
export default function PrivacyPolicyPage() {
return (
<>
<PageHeader title="Политика конфиденциальности" />
<Container className="prose prose-neutral md:prose-lg dark:prose-invert max-w-none">
<h1 className="text-2xl font-bold">
Политика конфиденциальности бота / мини-приложения «Запишись.онлайн»
(@zapishis_online_bot)
</h1>
<h2 className="text-2xl font-semibold">1. Термины и определения</h2>
<ol>
<li>
<strong>Telegram</strong> Telegram Messenger Inc. (платформа, на которой работает бот
и мини-приложение).
</li>
<li>
<strong>Платформа</strong> экосистема ботов и мини-приложений Telegram.
</li>
<li>
<strong>Разработчик</strong> физическое лицо, самозанятый, владелец и оператор сервиса
«Запишись.онлайн» (@zapishis_online_bot) (далее «Разработчик»).
</li>
<li>
<strong>Сторонний сервис</strong> бот/мини-приложение Разработчика, предоставляемое в
Платформе.
</li>
<li>
<strong>Пользователь</strong> лицо, использующее Сторонний сервис через свою учетную
запись Telegram (далее «Вы»).
</li>
<li>
<strong>Политика</strong> настоящий документ, регулирующий отношения между
Разработчиком и Пользователем в части сбора и обработки персональных данных.
</li>
</ol>
<h2 className="text-2xl font-semibold">2. Общие положения</h2>
<p>
2.1. Настоящая Политика регулирует исключительно отношения между Разработчиком и
Пользователем. Она не заменяет и не изменяет Политику конфиденциальности Telegram:{' '}
<a href="https://telegram.org/privacy">https://telegram.org/privacy</a>.
</p>
<p>
2.2. Разработчик соблюдает применимые требования платформы Telegram к конфиденциальности и
защите данных.
</p>
<p>
2.3. Использование Сервиса Пользователем и/или активация платного доступа означает
согласие Пользователя с условиями настоящей Политики.
</p>
<p>2.4. Если Вы не согласны с условиями Политики прекратите использование Сервиса.</p>
<h2 className="text-2xl font-semibold">3. Отказ от ответственности</h2>
<p>
3.1. Сторонний сервис является независимым приложением и не поддерживается, не одобряется
и не аффилирован с Telegram (за исключением использования API и инфраструктуры Telegram).
</p>
<p>
3.2. Разработчик вправе изменять настоящую Политику изменения вступают в силу с момента
их публикации. Вы обязаны самостоятельно отслеживать обновления.
</p>
<p>
3.3. Используя Сервис, Вы подтверждаете, что ознакомлены и согласны с условиями
использования Telegram для ботов и мини-приложений:{' '}
<a href="https://telegram.org/tos/bots">https://telegram.org/tos/bots</a>,{' '}
<a href="https://telegram.org/tos/mini-apps">https://telegram.org/tos/mini-apps</a>.
</p>
<p>
3.4. Вы гарантируете, что используете Сервис в соответствии с действующим
законодательством и обладаете правом взаимодействовать с ним (например, достигли возраста,
необходимого для использования услуг).
</p>
<p>
3.5. Вы обязуетесь предоставлять точную и актуальную информацию, если Сервис запрашивает
её.
</p>
<p>
3.6. Любая информация, которую Вы делаете общедоступной самостоятельно (через профиль
Telegram, публичные сообщения и т.п.), может стать доступна другим пользователям и не
подпадает под защиту настоящей Политики в части конфиденциальности этой общедоступной
информации.
</p>
<h2 className="text-2xl font-semibold">4. Сбор персональных данных</h2>
<p>
4.1. Telegram по умолчанию предоставляет сторонним сервисам ограниченный набор данных о
Пользователе подробнее:{' '}
<a href="https://telegram.org/privacy#6-bot-messages">
https://telegram.org/privacy#6-bot-messages
</a>
.
</p>
<p>
4.2. Сторонний сервис может дополнительно получать данные, которые Вы передаёте в чате
бота или в мини-приложении (например, контакт, телефон), если Вы явно их отправляете.
</p>
<p>
4.3. В случае мини-приложения дополнительно могут передаваться данные в соответствии с
правилами мини-приложений Telegram:{' '}
<a href="https://telegram.org/tos/mini-apps#4-privacy">
https://telegram.org/tos/mini-apps#4-privacy
</a>
.
</p>
<p>
4.4. Сторонний сервис может собирать также анонимную статистику использования
(диагностика, события взаимодействия), не связываемую напрямую с персоной.
</p>
<p>
4.5. Пользователь может передавать данные третьих лиц (например, контактные данные
клиентов или мастеров) для использования в Сервисе. При этом пользователь гарантирует, что
эти лица дали согласие на обработку их персональных данных в рамках Сервиса.
</p>
<h2 className="text-2xl font-semibold">5. Какие данные мы собираем и как используем</h2>
<p>
5.1. Разработчик запрашивает, собирает и обрабатывает только те данные, которые необходимы
для корректной работы функций Сервиса, в частности:
</p>
<ul>
<li>Telegram ID и (опционально) отображаемое имя пользователя;</li>
<li>телефон, только если Вы предоставили его добровольно (например, при регистрации);</li>
<li>данные о заказах: дата/время, описание заказа, статус;</li>
<li>
информация о факте покупки Pro-доступа: период доступа, тип покупки (детали платёжной
транзакции обрабатывает платёжный оператор ЮKassa).
</li>
</ul>
<p>5.2. Цели обработки:</p>
<ul>
<li>
предоставление и поддержка работы Сервиса (создание заказов, напоминания, управление
доступом);
</li>
<li>
подтверждение и учет оплат (взаимодействие с платёжным оператором для актуализации
статуса доступа);
</li>
<li>
реализация реферальной программы (хранение связей «кто пригласил/кого пригласили»);
</li>
<li>анализ использования и улучшение сервиса;</li>
<li>выполнение юридических обязательств (хранение информации о транзакциях и др.).</li>
</ul>
<p className="note">
Важно: детальные платёжные данные (реквизиты карт и т.д.) не хранятся у Разработчика их
обрабатывает платёжный оператор (ЮKassa) и Telegram-платежный бот.
</p>
<h2 className="text-2xl font-semibold">6. Передача данных третьим лицам</h2>
<p>
6.1. Разработчик не передаёт персональные данные третьим лицам, за исключением следующих
случаев:
</p>
<ul>
<li>платёжному оператору (ЮKassa) и связанным службам для обработки платежей;</li>
<li>Telegram как платформе для функционирования бота и мини-приложения;</li>
<li>
в случае необходимости исполнителям, оказывающим техническую поддержку, при условии
подписания ими обязательств о конфиденциальности;
</li>
<li>если передача требуется по закону (запросы уполномоченных органов и т.п.).</li>
</ul>
<p>
6.2. Разработчик не продаёт и не передаёт персональные данные для рекламных целей третьим
лицам без Вашего отдельного согласия.
</p>
<h2 className="text-2xl font-semibold">7. Защита и хранение данных</h2>
<p>
7.1. Разработчик применяет разумные технические и организационные меры для защиты
персональных данных (использование надежного VPS, ограничения доступа, резервное
копирование и т.п.).
</p>
<p>
7.2. Доступ к персональным данным имеет только Разработчик (и/или доверенные исполнители
технической поддержки при необходимости).
</p>
<p>
7.3. Данные хранятся на серверах, указанных Разработчиком. Если используются внешние
сервисы/облачные хранилища это будет указано в соответствующих местах Политики или
сообщения при сборе данных.
</p>
<h2 className="text-2xl font-semibold">8. Права и обязанности сторон</h2>
<p>8.1. Права Разработчика:</p>
<ul>
<li>вносить изменения в Политику с публикацией новой версии;</li>
<li>ограничивать доступ к API/сервису при подозрении в злоупотреблениях;</li>
<li>
запросить подтверждение личности при необходимости обработки привилегированных запросов.
</li>
</ul>
<p>8.2. Обязанности Разработчика:</p>
<ul>
<li>обеспечивать доступность Политики и исполнять её условия;</li>
<li>
обрабатывать законные запросы пользователей о доступе, изменении или удалении данных в
разумные сроки (не позднее 30 дней, если иное не установлено законом);
</li>
<li>соблюдать применимое законодательство о защите персональных данных.</li>
</ul>
<p>8.3. Права Пользователя:</p>
<ul>
<li>запросить копию своих персональных данных, хранящихся у Разработчика;</li>
<li>потребовать исправления неточных данных;</li>
<li>
потребовать удаления персональных данных в пределах, допустимых законом (с сохранением
данных, необходимых для выполнения юридических обязательств, например, по учёту
платежей);
</li>
<li>
отозвать согласие на обработку персональных данных, если такое согласие предоставлялось
добровольно;
</li>
<li>
подать жалобу в уполномоченные органы по защите персональных данных, если считает, что
его права нарушены.
</li>
</ul>
<p>8.4. Обязанности Пользователя:</p>
<ul>
<li>предоставлять точную и актуальную информацию;</li>
<li>не использовать Сервис в нарушении законодательства и условий Telegram.</li>
</ul>
<h2 className="text-2xl font-semibold">9. Реклама и использование данных для аналитики</h2>
<p>
9.1. На текущем этапе Разработчик не использует персональные данные для демонстрации
таргетированной рекламы третьих лиц без явного согласия Пользователя.
</p>
<p>
9.2. Разработчик может собирать агрегированную (анонимную) статистику использования
Сервиса для улучшения функционала.
</p>
<h2 className="text-2xl font-semibold">10. Изменения Политики</h2>
<p>
10.1. Разработчик вправе вносить изменения в настоящую Политику. Все изменения публикуются
на этой странице и вступают в силу с момента публикации.
</p>
<h2 className="text-2xl font-semibold">11. Контакты</h2>
<p>
Если у Вас есть вопросы по Политике конфиденциальности или запросы в отношении
персональных данных, пожалуйста, свяжитесь с Разработчиком:
</p>
<ul>
<li>
Telegram:{' '}
<strong>
<a href={env.SUPPORT_TELEGRAM_URL}>{env.SUPPORT_TELEGRAM_URL}</a>
</strong>
</li>
</ul>
<div className="h-10" />
</Container>
</>
);
}

View File

@ -4,6 +4,8 @@ import { z } from 'zod';
export const envSchema = z.object({ export const envSchema = z.object({
__DEV_TELEGRAM_ID: z.string().default(''), __DEV_TELEGRAM_ID: z.string().default(''),
BOT_URL: z.string(), BOT_URL: z.string(),
OFFER_URL: z.string(),
SUPPORT_TELEGRAM_URL: z.string(),
}); });
export const env = envSchema.parse(process.env); export const env = envSchema.parse(process.env);

View File

@ -11,5 +11,7 @@ export default withAuth({
}); });
export const config = { export const config = {
matcher: ['/((?!auth|browser|telegram|unregistered|api|_next/static|_next/image|favicon.ico).*)'], matcher: [
'/((?!auth|browser|telegram|unregistered|privacy|public-offer|api|_next/static|_next/image|favicon.ico).*)',
],
}; };

View File

@ -13,10 +13,13 @@
"BOT_TOKEN", "BOT_TOKEN",
"NEXTAUTH_SECRET", "NEXTAUTH_SECRET",
"BOT_URL", "BOT_URL",
"SUPPORT_TELEGRAM_URL",
"BOT_PROVIDER_TOKEN", "BOT_PROVIDER_TOKEN",
"REDIS_HOST", "REDIS_HOST",
"REDIS_PORT", "REDIS_PORT",
"REDIS_PASSWORD" "REDIS_PASSWORD",
"OFFER_URL",
"PRIVACY_URL"
] ]
}, },
"lint": { "lint": {