From 85b4ae4ee08cf640f9e3a2f2370adf06fbf5b956 Mon Sep 17 00:00:00 2001 From: HotelSync Date: Wed, 8 Apr 2026 02:21:55 +0300 Subject: [PATCH] fix: booking widget payment flow and success message MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - On API error: show error message instead of false success screen - Success message: "Оплата прошла успешно" only when paidViaGateway=true (user actually clicked pay in YooKassa); otherwise "Бронирование принято. Менеджер свяжется с вами" or "Оплата на месте" - Payment screen: simplified to YooKassa-only redirect (removed bank transfer variant) - Removed bankDetails from WidgetSettings entirely (not needed per product decision) Co-Authored-By: Claude Sonnet 4.6 --- src/lib/api.ts | 1 - src/pages/BookingWidgetPage.tsx | 130 ++++++---------------- src/pages/BookingWidgetStandalonePage.tsx | 2 - 3 files changed, 32 insertions(+), 101 deletions(-) diff --git a/src/lib/api.ts b/src/lib/api.ts index 6dc3abf..e2af903 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1680,7 +1680,6 @@ export interface WidgetConfig { allowExtraBeds: boolean allowChildren: boolean hotelName: string - bankDetails: string } } diff --git a/src/pages/BookingWidgetPage.tsx b/src/pages/BookingWidgetPage.tsx index 4836b69..37478e0 100644 --- a/src/pages/BookingWidgetPage.tsx +++ b/src/pages/BookingWidgetPage.tsx @@ -44,7 +44,6 @@ export interface WidgetSettings { showPromo: boolean allowExtraBeds: boolean allowChildren: boolean - bankDetails: string formFields: FormField[] additionalServices: AdditionalService[] } @@ -216,6 +215,7 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme // Hourly service time selections: { serviceId: { date, timeFrom, timeTo } } const [serviceSchedule, setServiceSchedule] = useState>({}) const [bookingError, setBookingError] = useState(null) + const [paidViaGateway, setPaidViaGateway] = useState(false) // Guest lookup const [guestLookupTimer, setGuestLookupTimer] = useState | null>(null) const [guestSuggestion, setGuestSuggestion] = useState<{ first_name: string; last_name: string; middle_name?: string; phone?: string; email?: string } | null>(null) @@ -291,9 +291,6 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme if (result.confirmationUrl) { setConfirmUrl(result.confirmationUrl) setStep('payment') - } else if (settings.bankDetails || needsPayment) { - // YooKassa failed or bank transfer configured — always show payment screen - setStep('payment') } else { setStep('success') } @@ -301,14 +298,14 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme if (err?.status === 403) { setBookingError(err?.error ?? 'Невозможно завершить бронирование. Попробуйте позже или свяжитесь с отелем.') } else { - setStep('success') + setBookingError('Ошибка при создании бронирования. Попробуйте ещё раз или свяжитесь с отелем.') } } finally { setSubmitting(false) } } else { // Preview mode without real slug - if (needsPayment || settings.bankDetails) { + if (needsPayment) { setStep('payment') } else { setStep('success') @@ -318,6 +315,7 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme const handlePay = () => { if (confirmUrl) { + setPaidViaGateway(true) window.location.href = confirmUrl } else { setStep('success') @@ -332,6 +330,8 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme setServiceSchedule({}) setBookingError(null) setGuestSuggestion(null) + setPaidViaGateway(false) + setConfirmUrl(null) } const doGuestLookup = (email?: string, phone?: string) => { @@ -357,9 +357,11 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme

Бронирование принято!

- {needsPayment + {paidViaGateway ? 'Оплата прошла успешно. Подтверждение придёт на email.' - : 'Оплата на месте при заезде. Подтверждение придёт на email.'} + : needsPayment + ? 'Бронирование принято. Менеджер свяжется с вами для оплаты.' + : 'Оплата на месте при заезде. Подтверждение придёт на email.'}

{isCategoryMode ? 'Категория' : 'Номер'}: {selectedName}

@@ -379,11 +381,8 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme ) } - // ── Payment screen ── + // ── Payment screen (YooKassa redirect) ── if (step === 'payment') { - const isYookassa = !!confirmUrl - const hasBankDetails = !isYookassa && !!settings.bankDetails - return (
@@ -397,77 +396,33 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme
- {/* Amount */}
{selectedName} · {nights} ноч. {grandTotal.toLocaleString('ru-RU')} ₽
- - {isYookassa ? ( - /* YooKassa redirect */ -
-
- -
-

- {settings.language === 'ru' ? 'Для оплаты вы будете перенаправлены на страницу ЮKassa' : 'You will be redirected to YooKassa to complete payment'} -

-

🔒 Защищённое соединение · ЮKassa

+
+
+
- ) : hasBankDetails ? ( - /* Bank transfer details */ -
-

- {settings.language === 'ru' ? 'Реквизиты для оплаты' : 'Payment details'} -

-
- {settings.bankDetails} -
-

- {settings.language === 'ru' - ? 'После перевода отель подтвердит бронирование. Укажите в назначении платежа вашу фамилию и даты.' - : 'After transfer, the hotel will confirm your booking. Include your name and dates in the payment reference.'} -

-
- ) : ( - /* No payment configured — pending confirmation */ -
-
- -
-

- {settings.language === 'ru' ? 'Бронирование принято. Менеджер отеля свяжется с вами для подтверждения оплаты.' : 'Booking received. Hotel manager will contact you to confirm payment.'} -

-
- )} +

+ {settings.language === 'ru' + ? 'Для завершения бронирования перейдите к оплате' + : 'Please proceed to payment to confirm your booking'} +

+

🔒 Защищённое соединение · ЮKassa

+
- {isYookassa ? ( - - ) : hasBankDetails ? ( - - ) : ( - - )} +
) @@ -696,11 +651,11 @@ export function WidgetPreview({ settings, slug, realRooms, realCategories, payme style={{ background: settings.primaryColor }} > {submitting && } - {submitting ? 'Отправляем...' : (needsPayment || settings.bankDetails) + {submitting ? 'Отправляем...' : needsPayment ? (settings.language === 'ru' ? `Перейти к оплате · ${grandTotal.toLocaleString('ru-RU')} ₽` : `Proceed to payment · ${grandTotal.toLocaleString('ru-RU')} ₽`) : (settings.language === 'ru' ? `Подтвердить · ${grandTotal.toLocaleString('ru-RU')} ₽` : `Confirm · ${grandTotal.toLocaleString('ru-RU')} ₽`)} - {!needsPayment && !settings.bankDetails && ( + {!needsPayment && (

Оплата на месте при заезде

)}
@@ -1188,7 +1143,6 @@ export function BookingWidgetPage() { showPromo: (hs as any).widgetShowPromo !== undefined ? Boolean((hs as any).widgetShowPromo) : prev.showPromo, allowExtraBeds: (hs as any).widgetExtraBeds !== undefined ? Boolean((hs as any).widgetExtraBeds) : prev.allowExtraBeds, allowChildren: (hs as any).widgetChildren !== undefined ? Boolean((hs as any).widgetChildren) : prev.allowChildren, - bankDetails: String((hs as any).widgetBankDetails ?? prev.bankDetails ?? ''), })) setSettingsReady(true) }).finally(() => setGatewayLoading(false)) @@ -1206,7 +1160,6 @@ export function BookingWidgetPage() { showPromo: true, allowExtraBeds: true, allowChildren: true, - bankDetails: '', formFields: DEFAULT_FORM_FIELDS, additionalServices: DEFAULT_SERVICES, }) @@ -1236,7 +1189,6 @@ export function BookingWidgetPage() { widget_show_promo: s.showPromo, widget_extra_beds: s.allowExtraBeds, widget_children: s.allowChildren, - widget_bank_details: s.bankDetails, }) setSaved(true) setTimeout(() => setSaved(false), 2000) @@ -1576,24 +1528,6 @@ export function BookingWidgetPage() { )}
- {/* Bank transfer details */} -
-
- -

Реквизиты для оплаты переводом

-
-

- Если ЮKassa не подключена, гость увидит эти реквизиты после бронирования. Оставьте пустым если оплата на месте. -

-