Подписки и платежи — модуль оплаты через CloudPayments
Провайдер
CloudPayments (api.cloudpayments.ru). Карты МИР, Visa, Mastercard. Рекуррентные платежи.
Архитектура
Фронтенд (hadl-front)
Точки входа
src/views/Subscription.vue— страница выбора и оплаты подпискиsrc/stores/subscription.js— стор планов подпискиsrc/components/Subscription/SubscriptionPlanCard.vue— карточка плана
Поток оплаты
resolvePlanAction()→ генерируетpaymentId(UUID), проверяет нет ли активной подписки → вызываетpay()pay()→ открывает виджет CloudPayments (cp.CloudPayments), настройки рекуррента (recurrent)onComplete(paymentResult)→ приsuccessвызываетPOST /api/subscribeсpaymentId.value- При ошибке 409 (уже активна) — показывает info-уведомление
Sync подписки
При onMounted для web/android вызывается POST /api/sync-subscription — синхронизирует статус подписки с CloudPayments API.
Бэкенд (cryptodiary)
Контроллеры
UserController::subscribe(POST /api/subscribe) — клиентский запрос после успешной оплаты в виджете- Идемпотентность по
uuid(TransactionId / paymentId) - Блокирует повторную покупку при активной подписке (409)
- Создаёт/продлевает
Subscription+ записываетSubscriptionPayment
- Идемпотентность по
UserController::syncSubscription(POST /api/sync-subscription) — синхронизирует статус с CP APICloudPaymentsController::paymentSuccess(POST /api/cloudpayments/payment-success) — webhook Pay от CloudPayments- Создаёт подписку серверно (без зависимости от клиента)
- Защита HMAC через middleware
cloudpayments_hmac - Идемпотентность по
TransactionIdчерезuuidвsubscription_payments - Логирование в канал
cloudpayments(файлstorage/logs/cloudpayments.log)
Модели
Subscription— подписка пользователя (user_id, subscription_plan_id, status_id, starts_at, ends_at, external_id)SubscriptionPayment— запись платежа (subscription_id, amount, platform_id, uuid)SubscriptionPlan— план (PAID_PLANS_IDS = [1, 2], коды:basic-m,basic-y,free,free-forever)SubscriptionStatus— статусы (active,cancelled)User::subscription()—hasOne(Subscription)->latest('updated_at')
Сервисы
CloudPaymentsService— HTTP-клиент к API CloudPaymentsFindUserSubscriptions— поиск подписок пользователя по accountIdUpdateSubscription— обновление рекуррентной подписки
Middleware
VerifyCloudPaymentsHmac(cloudpayments_hmac) — проверка HMAC-SHA256 подписи webhook'ов CloudPaymentsSubscriptionGate(subscription_gate) — ограничение функций по тарифу
Команды
clear:outdated-subscriptions— удаляет подписки сends_at < now() - 2 дня
Конфигурация
config/services.php→cloud_payments.public_id,cloud_payments.api_secret.env→CLOUD_PAYMENTS_PUBLIC_ID,CLOUD_PAYMENTS_API_SECRET- Фронт:
VITE_CLOUD_PAYMENTS_PUBLIC_ID,VITE_CLOUD_PAYMENTS_PASSWORD
Webhook в личном кабинете CloudPayments
- URL:
https://hadl.app/api/cloudpayments/payment-success - Метод: POST
- Формат: CloudPayments
- Тип: Pay (о принятом платеже)
Ключевые нюансы
- Двойная защита: подписка создаётся и клиентом (
subscribe), и вебхуком (paymentSuccess). Оба проверяют uuid/TransactionId на дублирование. SubscriptionPayment.uuidхранит paymentId (от клиента) или TransactionId (от webhook) — обеспечивает идемпотентность.- Рекуррентные платежи: виджет создаёт подписку в CP, последующие списания приходят через Pay webhook.
resolveSubscriptionPlan()в webhook определяет план по сумме платежа (точное совпадение цены или эвристика).