Модуль - маркетплейс с функцией оплаты Юкассы по API

Автор FOxsis1, 11 августа 2026, 11:51:34

« назад - далее »

0 Пользователи и 1 гость просматривают эту тему.

FOxsis1

Монетизация форума RU. Данный модуль предлагает монетизацию форума а именно размещение специалистов на форуме для помощи. Оплата через Юкассу по API.
Скриншоты модуля: https://disk.yandex.ru/d/kHAEwAojdMS1nA

Телеграм: @Mainframe_Lit

Схема работы модуля «Позовите специалиста»
1. Назначение
Модуль превращает форум SMF в маркетплейс услуг: участники становятся специалистами, публикуют услуги, клиенты оформляют и оплачивают заявки через ЮКассу. Всё сопровождается юридически значимым документооборотом с простой электронной подписью (ПЭП).



# Модуль «Позовите специалиста» для SMF
Маркетплейс услуг внутри форума SMF 2.1: участники становятся специалистами,
публикуют услуги, клиенты оформляют и оплачивают заявки через ЮKassa.
Весь процесс сопровождается юридически значимым документооборотом с простой
электронной подписью (ПЭП) по 63-ФЗ.
---
## Возможности
- Каталог специалистов с фильтром по категориям, рейтингом и отзывами.
- Личный кабинет специалиста: самостоятельное заполнение карточки и услуг.
- Заказ услуги с оплатой через ЮKassa и автоматическим созданием закрытой
  темы для общения клиента и специалиста.
- Дополнительные поля формы заказа, настраиваемые под каждую услугу.
- Подсистема из пяти правовых документов с онлайн-редактором.
- Простая электронная подпись (ПЭП): протокол акцепта с фиксацией версии
  документа, SHA-256, IP, времени и кода подписания.
- Модерация карточек специалистов администратором.
- Панель администратора: категории, документы, реквизиты, настройки ЮKassa,
  журнал подписаний, все заявки.
---
## Требования
- SMF 2.1.x
- PHP 7.4+ (проверено на 8.3)
- MySQL / MariaDB (InnoDB, utf8mb4)
- Аккаунт ЮKassa (shopId и секретный ключ) для приёма платежей
---
## Структура файлов
| Файл | Назначение |
|---|---|
| `Sources/CallSpecialist.php` | Ядро: каталог, профиль, заказы, оплата ЮKassa, отзывы, админка |
| `Sources/CallSpecialistCabinet.php` | Личный кабинет специалиста, модерация карточек, схема доп-колонок |
| `Sources/CallSpecialistDocs.php` | Документы: хранение, вывод, ПЭП, журнал, админка документов |
| `Sources/CallSpecialistDocsText.php` | Эталонные HTML-тексты пяти документов |
| `Themes/default/CallSpecialist.template.php` | Шаблоны каталога, профиля, заказа, «Мои заявки», админки |
| `Themes/default/CallSpecialistCabinet.template.php` | Шаблоны кабинета и модерации |
| `Themes/default/CallSpecialistDocs.template.php` | Шаблоны страниц документов и редактора |
| `Languages/CallSpecialist.russian.php` | Строки интерфейса |
| `Patches/index.php.patch.txt` | Инструкция по регистрации экшенов в корневом `index.php` |
| `install.sql` | Таблицы каталога, услуг, заказов, категорий, отзывов |
| `install_documents.sql` | Таблицы документов и журнала подписаний (обычно создаются автоматически) |
---
## Таблицы базы данных
| Таблица | Содержимое |
|---|---|
| `specialists` | Карточки специалистов. Привязка к участнику — `member_id`. Поля `moderated`, `active`, `inn`, `email`, `edo_id`, `contact`, `rating` |
| `specialist_categories` | Категории специалистов (название, иконка) |
| `specialist_services` | Услуги специалиста: цена, длительность, JSON доп-полей формы заказа |
| `specialist_orders` | Заявки: клиент, услуга, специалист, сумма, статус, `topic_id`, id платежа |
| `specialist_reviews` | Отзывы клиентов |
| `specialist_documents` | Документы: `slug`, HTML-текст, версия, флаги `require_accept` / `show_public` |
| `specialist_doc_accepts` | Журнал подписаний ПЭП: пользователь, документ, версия, SHA-256, IP, User-Agent, код, время |
Связи: `members.id_member``specialists.member_id` (1:1);
специалист → `specialist_services` и `specialist_reviews` (1:N);
`specialist_orders` связывает клиента, услугу, специалиста и тему форума;
каждая запись `specialist_doc_accepts` ссылается на пользователя, документ и (при заказе) на заявку.
---
## Установка
1. **Скопируйте файлы** через FTP в соответствующие папки форума:
   `Sources/*``/Sources/`, `Themes/default/*``/Themes/default/`,
   `Languages/*``/Languages/`.
2. **Создайте таблицы каталога.** Выполните `install.sql` в phpMyAdmin,
   заменив префикс `smf_` на свой (см. `$db_prefix` в `Settings.php`).
   Таблицы документов и кабинета создаются автоматически при первом заходе
   в соответствующие разделы.
3. **Зарегистрируйте экшены** в корневом `index.php`. Перед закрывающей `);`
   массива `$actionArray` добавьте строки из `Patches/index.php.patch.txt`:
   ```php
   'call_specialist'         => array('CallSpecialist.php', 'CallSpecialist'),
   'call_specialist_profile' => array('CallSpecialist.php', 'CallSpecialistProfile'),
   'call_specialist_order'   => array('CallSpecialist.php', 'CallSpecialistOrder'),
   'call_specialist_yoo_cb'  => array('CallSpecialist.php', 'CallSpecialistYooCallback'),
   'call_specialist_success' => array('CallSpecialist.php', 'CallSpecialistSuccess'),
   'call_specialist_orders'  => array('CallSpecialist.php', 'CallSpecialistMyOrders'),
   'call_specialist_admin'   => array('CallSpecialist.php', 'CallSpecialistAdmin'),
   'call_specialist_manage'  => array('CallSpecialist.php', 'CallSpecialistManage'),
   'call_specialist_docs'         => array('CallSpecialistDocs.php', 'CallSpecialistDocs'),
   'call_specialist_doc'          => array('CallSpecialistDocs.php', 'CallSpecialistDoc'),
   'call_specialist_docs_admin'   => array('CallSpecialistDocs.php', 'CallSpecialistDocsAdmin'),
   'call_specialist_docs_manage'  => array('CallSpecialistDocs.php', 'CallSpecialistDocsManage'),
   'call_specialist_cabinet'      => array('CallSpecialistCabinet.php', 'CallSpecialistCabinet'),
   'call_specialist_cabinet_save' => array('CallSpecialistCabinet.php', 'CallSpecialistCabinetSave'),
   ```
   Сохраняйте `index.php` в UTF-8 без BOM.
4. **Настройте реквизиты и ЮKassa** в панели администратора (см. ниже).
---
## Страницы (экшены)
| URL | Назначение | Доступ |
|---|---|---|
| `?action=call_specialist` | Каталог специалистов | все |
| `?action=call_specialist_profile;sid=N` | Профиль специалиста | все (карточка на модерации — только владелец/админ) |
| `?action=call_specialist_order` | Оформление заявки (POST) | зарегистрированные |
| `?action=call_specialist_yoo_cb` | Webhook ЮKassa | ЮKassa |
| `?action=call_specialist_success` | Страница после оплаты | зарегистрированные |
| `?action=call_specialist_orders` | Мои заявки (клиент) | зарегистрированные |
| `?action=call_specialist_cabinet` | Кабинет специалиста | зарегистрированные |
| `?action=call_specialist_cabinet;sa=orders` | Заявки клиентов специалиста | специалист |
| `?action=call_specialist_docs` | Публичный список документов | все |
| `?action=call_specialist_doc;slug=public_offer` | Просмотр документа | все |
| `?action=call_specialist_doc;sa=protocol;order=N` | Протокол подписания ПЭП | владелец/админ |
| `?action=call_specialist_admin` | Панель администратора | админ |
| `?action=call_specialist_admin;sa=moderation` | Модерация карточек | админ |
| `?action=call_specialist_docs_admin` | Редактор документов и реквизитов | админ |
---
## Роли и сценарии
### Клиент
Открывает каталог → фильтрует по категориям → заходит в профиль специалиста →
«Заказать» → в модальном окне заполняет поля услуги и **принимает документы
галочками** → «Оплатить» → оплата в ЮKassa → доступ к закрытой теме со специалистом.
### Специалист
Кнопка «🚀 Стать специалистом» в каталоге → форма карточки (имя, категория,
описание, **ИНН, e-mail, идентификатор ЭДО**) + **подпись пяти документов ПЭП**
карточка уходит на модерацию → после одобрения управляет услугами и видит заявки
клиентов в кабинете.
### Администратор
Панель `?action=call_specialist_admin`: категории, все заявки, настройки ЮKassa.
Модерация карточек (`sa=moderation`) с одобрением/отклонением.
Редактор документов и реквизитов (`?action=call_specialist_docs_admin`).
---
## Поток «заказ → оплата → тема»
1. `CallSpecialistOrder()` проверяет принятие обязательных документов
   (`csValidateRequiredAcceptance`; при отказе — `fatal_error`).
2. Создаётся заявка в `specialist_orders` со статусом `pending_payment`.
3. Фиксируется акцепт ПЭП (`csRecordAcceptances`): версия, SHA-256, IP, код.
4. Создаётся платёж в ЮKassa, происходит редирект на `confirmation_url`.
5. После оплаты ЮKassa вызывает webhook → `CallSpecialistYooCallback()`
   проверяет подпись, ставит статус `paid`.
6. Создаётся закрытая тема (`createPrivateTopic`), `topic_id` пишется в заявку.
7. Обе стороны получают уведомление; клиент видит код ПЭП и ссылку на протокол.
Статусы заявки: `pending_payment``paid``in_progress``completed`
(или `cancelled`).
---
## Документы и ПЭП
Пять стандартных документов (slug):
| slug | Документ | Обязателен при заказе |
|---|---|---|
| `public_offer` | Публичная оферта (договор комиссии) | да |
| `privacy_policy` | Политика обработки персональных данных | нет |
| `services_offer` | Публичная оферта на оказание услуг | да |
| `signature_agreement` | Соглашение об использовании ПЭП | да |
| `pd_consent` | Согласие на обработку персональных данных | да |
Тексты хранятся как HTML с плейсхолдерами, которые подставляются из реквизитов:
`{ORG_NAME}`, `{ORG_SHORT}`, `{INN}`, `{KPP}`, `{OGRN}`, `{ADDRESS}`, `{EMAIL}`,
`{PHONE}`, `{SITE}`, `{DIRECTOR}`, `{BANK}`, а также `{VERSION}`, `{UPDATED}`, `{DATE}`.
**Механизм подписи.** При подписании считается `SHA-256(slug|версия|текст)`
подпись привязана к конкретной редакции. В `specialist_doc_accepts` пишутся
пользователь, документ, версия, хэш, IP, User-Agent, время и код вида
`A1B2-C3D4-E5F6-7890`. Правка текста документа не затрагивает ранее выданные
подписи. Протокол доступен клиенту («Мои заявки») и админу (журнал подписаний).
Кнопки в админке документов: «Обновить все тексты из стандартных» перезаписывает
содержимое эталонными текстами (флаги сохраняются); «Добавить недостающие»
досоздаёт отсутствующие документы.
---
## Модерация
Новая или изменённая карточка получает `moderated = 0` и не отображается в
каталоге (фильтр в `getSpecialistsList`). Профиль неодобрённой карточки открыт
только владельцу и администратору. В админ-панели — кнопка «🛡 Модерация» со
счётчиком ожидающих; одобрение ставит `moderated = 1`, отклонение возвращает
карточку на доработку.
Чтобы публиковать карточки без модерации:
```sql
INSERT INTO smf_settings (variable, value) VALUES ('cs_cabinet_automod', '1');
```
---
## Настройки (таблица `settings`)
| Переменная | Назначение |
|---|---|
| `cs_org_name`, `cs_org_short` | Наименование организации |
| `cs_org_inn`, `cs_org_kpp`, `cs_org_ogrn` | Налоговые реквизиты |
| `cs_org_address`, `cs_org_email`, `cs_org_phone`, `cs_org_site` | Контакты |
| `cs_org_director`, `cs_org_bank` | Руководитель и банковские реквизиты |
| `yookassa_shop_id`, `yookassa_secret_key` | Доступы ЮKassa |
| `cs_cabinet_automod` | `1` — карточки публикуются без модерации |
| `cs_docs_installed`, `cs_cabinet_installed` | Служебные флаги установки схемы |
Реквизиты заполняются в «Документы → 🏢 Реквизиты», ключи ЮKassa — в
«Панель → ⚙️ ЮКасса».
---
## Настройка ЮKassa
1. В личном кабинете ЮKassa получите **shopId** и **секретный ключ**.
2. Внесите их в «Панель администратора → ⚙️ ЮКасса».
3. Укажите URL для уведомлений (webhook):
   `https://ваш-форум/index.php?action=call_specialist_yoo_cb`
4. Проверьте оплату тестовой заявкой.
---
## Диагностика
- **«Этот раздел недоступен»** — экшен не зарегистрирован в `index.php`
  (см. шаг 3 установки).
- **«Data too long for column 'url'»** — в лог SMF попал длинный SQL при сбое.
  Расширьте колонку: `ALTER TABLE smf_log_errors MODIFY url TEXT NOT NULL;`
  затем откройте нужную страницу и посмотрите настоящую ошибку в
  «Обслуживание → Журнал ошибок».
- **Кнопка кабинета не видна** — не обновлён `CallSpecialist.template.php`
  или активна другая тема (не `default`).
- **Новые колонки не добавились** — сбросьте флаг схемы:
  `DELETE FROM smf_settings WHERE variable = 'cs_cabinet_installed';`
  и снова откройте кабинет.
---
## Лицензия и поддержка
Модуль разработан под конкретную установку форума. При изменении структуры
таблиц SMF или версии ЮKassa API может потребоваться доработка.






2. Файловая структура
Логика (
Sources/):
CallSpecialist.php — ядро (каталог, заказы, оплата, админка);
CallSpecialistCabinet.php — личный кабинет специалиста и модерация;
CallSpecialistDocs.php — подсистема документов и ПЭП;
CallSpecialistDocsText.php — эталонные тексты пяти документов.
Отображение (
Themes/default/): три
*.template.php — соответственно каталог/заказы/админка, кабинет, документы.
Интеграция: строки экшенов в корневом
index.php; тексты интерфейса в
Languages/.
3. Таблицы базы данных
specialists — карточки специалистов (привязка к участнику через
member_id, поля
moderated,
inn,
email,
edo_id,
active).
specialist_categories — категории.
specialist_services — услуги (цена, продолжительность, дополнительные поля формы заказа в формате JSON).
specialist_orders — заявки (клиент, услуга, сумма, статус, id темы, id платежа).
specialist_reviews — отзывы.
specialist_documents — документы (слаг, HTML-текст, версия, флаги «обязательный/публичный»).
specialist_doc_accepts — журнал подписания ПЭП (кто, что, версия, SHA-256, IP, код, время).
4. Три роли и их сценарии
Гость/клиент открывает каталог (
?action=call_specialist) → фильтрует по категориям → заходит в профиль специалиста → жмёт «Заказать» → в модалке заполняет поля услуги и принимает документы галочками → нажимает «Оплатить».
Специалист (зарегистрированный участник) через кнопку в каталоге входит в кабинет (
?action=call_specialist_cabinet), заполняет карточку (имя, категория, описание, ИНН, e-mail, ЭДО) и подписывает 5 документов ПЭП → карточка уходит на модерацию → после одобрения управляет услугами и видит заявки клиентов.
Администратор (
?action=call_specialist_admin) ведёт категории, модерирует карточки (
sa=moderation), правит документы и реквизиты (
?action=call_specialist_docs_admin), настраивает ЮKassa, отслеживает все заявки.
5. Ключевой поток — оформление и оплата заявки
Выполнил команду
Выполнил команду

Полный поток подтвержден кодом. Вот пошаговая цепочка:
Клиент отправляет форму заказа →
CallSpecialistOrder() сначала проверяет принятие обязательных документов (
csValidateRequiredAcceptance, иначе
fatal_error) → создаёт заявку в
specialist_orders со статусом
pending_paymentфиксирует акцепт ПЭП (
csRecordAcceptances: версия, SHA-256, IP, код подписания) → создаёт платёж в ЮKassa и делает
redirectexit на
confirmation_url.
Клиент платит на стороне ЮKassa → ЮKassa шлёт webhook на
?action=call_specialist_yoo_cb
CallSpecialistYooCallback() проверяет подпись, ставит статус
paidсоздаёт закрытую тему (
createPrivateTopic) для общения клиента и специалиста → сохраняет
topic_id в заявке → уведомляет обе стороны.
Клиент возвращается на
?action=call_specialist_success → видит код ПЭП и ссылку на протокол подписания.
6. Подсистема документов и ПЭП
Пять документов (публичная оферта-комиссия, политика ПДн, оферта на услуги, соглашение ПЭП, согласие на ПДн) хранятся в БД как HTML с плейсхолдерами
{ORG_NAME},
{INN},
{SITE} и т.д. При выводе
csDocRender() подставляет реквизиты из настроек и вычищает опасные теги.
Механизм ПЭП: при подписании вычисляется SHA-256 связки
slug|версия|текст — это привязывает подпись к конкретной редакции. В журнал
specialist_doc_accepts записываются пользователь, документ, версия, хэш, IP, User-Agent, время и код вида
A1B2-C3D4-E5F6-7890. Правка текста документа не приводит к «переподписанию» старых заявок. Протокол доступен клиенту (в разделе «Мои заявки») и администратору (в журнале).
7. Модерация карточек
Новая или измененная карточка получает
moderated=0 и не отображается в каталоге (фильтр в
getSpecialistsList). Профиль неодобренной карточки виден только владельцу и администратору. В админ-панели есть кнопка «🛡 Модерация» со счетчиком; одобрение ставит
moderated=1, отклонение возвращает на доработку. Опция
cs_cabinet_automod=1 отключает модерацию.
8. Схема данных (связи)
Участник форума
members.id_member
specialists.member_id (1:1). Специалист → его
specialist_services (1:N) и
specialist_reviews (1:N). Заявка
specialist_orders связывает клиента (
user_id), услугу (
service_id), специалиста (
specialist_id) и тему форума (
topic_id). Каждый акцепт в
specialist_doc_accepts ссылается на пользователя, документ и (при заказе) на
order_id.