feat: deposit — history page, card info, cancel button, webhook notice, online booking email

- Migration 060: card_last4, card_brand columns in booking_deposits
- Backend: webhook saves card brand/last4 from YooKassa notification
- Backend: GET /api/hotels/:slug/deposits — payment history with guest/room info
- Backend: DELETE /api/hotels/:slug/bookings/:bookingId/deposit — cancel hold_created or paid_cash deposit
- Backend: auto-create YooKassa hold + send deposit email for source='online' bookings
- Frontend: DepositHistoryPage with filters, card display, retention reasons
- Frontend: deposit cancel button for hold_created and paid_cash states
- Frontend: show card last4/brand in hold_confirmed status
- Frontend: release form presets (full return / hold all) + improved UX
- Frontend: webhook setup instructions in DepositSettingsPage
- Frontend: "История платежей" link in deposit settings header

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-06 11:47:51 +03:00
parent 754f03b7fa
commit a4fc4a0972
8 changed files with 481 additions and 33 deletions

View File

@@ -1,6 +1,7 @@
import { useState, useEffect } from 'react'
import { Save, Eye, EyeOff, Loader2, ShieldCheck, Printer } from 'lucide-react'
import { Save, Eye, EyeOff, Loader2, ShieldCheck, Printer, History, Info } from 'lucide-react'
import { QRCodeSVG } from 'qrcode.react'
import { Link } from 'react-router-dom'
import { api, type DepositSettings } from '../lib/api'
import { useAuth } from '../contexts/AuthContext'
import { cn } from '../lib/utils'
@@ -84,14 +85,23 @@ export function DepositSettingsPage() {
return (
<div className="p-4 md:p-6 space-y-6 max-w-2xl">
<div>
<h1 className="text-xl font-bold text-slate-900 dark:text-slate-100 flex items-center gap-2">
<ShieldCheck size={22} className="text-brand-600" />
Депозит
</h1>
<p className="text-sm text-slate-500 dark:text-slate-400 mt-1">
Настройте предоплату или залог при заселении гостей.
</p>
<div className="flex items-start justify-between gap-4 flex-wrap">
<div>
<h1 className="text-xl font-bold text-slate-900 dark:text-slate-100 flex items-center gap-2">
<ShieldCheck size={22} className="text-brand-600" />
Депозит
</h1>
<p className="text-sm text-slate-500 dark:text-slate-400 mt-1">
Настройте предоплату или залог при заселении гостей.
</p>
</div>
<Link
to="/settings/deposit/history"
className="btn-secondary flex items-center gap-1.5 text-sm shrink-0"
>
<History size={15} />
История платежей
</Link>
</div>
{error && (
@@ -174,6 +184,26 @@ export function DepositSettingsPage() {
</div>
</div>
{/* Webhook notice */}
{shopId && (
<div className="border-t border-slate-100 dark:border-slate-700 pt-4">
<div className="rounded-lg bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 p-3 space-y-1.5">
<p className="text-xs font-semibold text-blue-800 dark:text-blue-300 flex items-center gap-1.5">
<Info size={13} /> Настройте webhook в ЮКасса
</p>
<p className="text-xs text-blue-700 dark:text-blue-400">
Чтобы статус депозита обновлялся автоматически после оплаты, добавьте URL уведомлений в личном кабинете ЮКасса (Настройки HTTP-уведомления):
</p>
<code className="block text-xs font-mono bg-blue-100 dark:bg-blue-900/40 text-blue-900 dark:text-blue-200 rounded px-2 py-1 break-all select-all">
https://api.hotelsync.ru/api/webhooks/yookassa
</code>
<p className="text-xs text-blue-600 dark:text-blue-400">
Подписки: <strong>payment.waiting_for_capture</strong>, <strong>payment.succeeded</strong>, <strong>payment.canceled</strong>
</p>
</div>
</div>
)}
{/* QR code section — only when shopId is configured */}
{shopId && (
<div className="border-t border-slate-100 dark:border-slate-700 pt-4">