getBookingsForReminder($target_date); if (empty($bookings)) { return; } foreach ($bookings as $booking) { if (self::sendReminderEmail($booking)) { $bookingRepository->markReminderSent($booking->id); } } // Log the operation if (defined('WP_DEBUG') && WP_DEBUG) { } } /** * Send a reminder email to the customer */ private static function sendReminderEmail(object $booking): bool { $customer_email = $booking->contact_email; if (empty($customer_email)) { return false; } $reminder_days = (int) SettingsService::get('booking_reminder_days', 3); $vars = TransactionalEmailTemplateService::variablesFromBooking($booking); $vars['reminder_days'] = (string) $reminder_days; $vars['days_until_trip'] = (string) $reminder_days; $amount_due = (float) $booking->amount_due; $extra = ''; if ($amount_due > 0) { $extra = '

' . esc_html__('Payment reminder', 'yatra') . '

' . '

' . esc_html(sprintf(__('Outstanding balance: %s — please pay before travel.', 'yatra'), yatra_format_price($amount_due))) . '

'; } $extra .= '

' . esc_html__('Preparation checklist', 'yatra') . '

'; $vars['reminder_extra_html'] = $extra; return TransactionalEmailTemplateService::sendIfEnabled( TransactionalEmailTemplateService::TYPE_BOOKING_REMINDER, $customer_email, $vars ); } /** * Expire pending bookings that have passed the expiry time */ public static function expirePendingBookings(): void { $expiry_hours = (int) SettingsService::get('booking_expiry_hours', 24); if ($expiry_hours <= 0) { return; // Expiry disabled } $bookingRepository = self::getBookingRepository(); $tripRepository = self::getTripRepository(); // Calculate the expiry threshold $expiry_threshold = date('Y-m-d H:i:s', strtotime("-{$expiry_hours} hours")); // Get pending bookings that are older than the expiry threshold $expired_bookings = $bookingRepository->getExpiredPendingBookings($expiry_threshold); if (empty($expired_bookings)) { return; } foreach ($expired_bookings as $booking) { // Update booking status to expired/cancelled $bookingRepository->expireBooking( $booking->id, __('Booking expired due to non-payment', 'yatra') ); do_action('yatra_booking_status_changed', (int) $booking->id, 'pending', 'cancelled'); // Get trip title for email $trip = $tripRepository->find($booking->trip_id); // Send expiry notification email self::sendExpiryEmail($booking, $trip); } // Log the operation if (defined('WP_DEBUG') && WP_DEBUG) { } } /** * Send expiry notification email */ private static function sendExpiryEmail(object $booking, ?object $trip): void { $customer_email = $booking->contact_email; $customer_name = trim($booking->contact_first_name . ' ' . $booking->contact_last_name); if (empty($customer_email)) { return; } $expiry_hours = (int) SettingsService::get('booking_expiry_hours', 24); $subject = sprintf( __('[%s] Booking Expired - %s', 'yatra'), get_bloginfo('name'), $booking->reference ); $body = sprintf(__("Dear %s,\n\n", 'yatra'), $customer_name ?: 'Customer'); $body .= __("We regret to inform you that your booking has been automatically cancelled due to non-payment.\n\n", 'yatra'); $body .= "═══════════════════════════════════════\n"; $body .= sprintf(__("Booking Reference: %s\n", 'yatra'), $booking->reference); $body .= sprintf(__("Trip: %s\n", 'yatra'), $trip ? $trip->title : 'N/A'); $body .= "═══════════════════════════════════════\n\n"; $body .= sprintf( __("Bookings that are not paid within %d hours are automatically cancelled to maintain availability for other travelers.\n\n", 'yatra'), $expiry_hours ); $body .= __("If you still wish to book this trip, please visit our website to create a new booking.\n\n", 'yatra'); $body .= __("If you believe this is an error or have already made a payment, please contact us immediately.\n\n", 'yatra'); $body .= sprintf(__("Best regards,\n%s Team\n", 'yatra'), get_bloginfo('name')); $body .= home_url() . "\n"; $headers = ['Content-Type: text/plain; charset=UTF-8']; EmailService::send($customer_email, $subject, $body, $headers); // Also notify admin $admin_email = get_option('admin_email'); $admin_subject = sprintf(__('[%s] Booking Expired - %s', 'yatra'), get_bloginfo('name'), $booking->reference); $admin_body = sprintf(__("Booking %s has been automatically cancelled due to non-payment.\n\n", 'yatra'), $booking->reference); $admin_body .= sprintf(__("Customer: %s (%s)\n", 'yatra'), $customer_name, $customer_email); $admin_body .= sprintf(__("Trip: %s\n", 'yatra'), $trip ? $trip->title : 'N/A'); $admin_body .= sprintf(__("View: %s\n", 'yatra'), admin_url('admin.php?page=yatra&subpage=bookings&action=view&id=' . $booking->id)); EmailService::send($admin_email, $admin_subject, $admin_body, $headers); } }