get_scheduler_settings(); $results = array( 'processed' => 0, 'sent' => 0, 'skipped' => 0, 'errors' => 0 ); // Get bookings that need payment requests $bookings = $this->get_bookings_for_payment_requests($settings['days_before']); foreach ($bookings as $booking) { $results['processed']++; try { // Check if payment request already sent if ($this->payment_request_already_sent($booking->ID)) { $results['skipped']++; continue; } // Calculate payment amount $amount = $this->calculate_payment_amount($booking, $settings['percentage']); // Create description $description = sprintf( __('Pre-arrival payment for your stay at Hotel Raxa (Booking #%d)', 'informatiq-eb-redsys'), $booking->ID ); // Create payment request $request_id = $this->create_automated_payment_request( $booking->ID, $booking->customer_email, $amount, $description, $settings['expiry_hours'] ); if ($request_id) { // Send email $sent = $this->send_automated_payment_email($request_id, $booking); if ($sent) { $results['sent']++; // Log the activity $this->log_payment_request_activity($booking->ID, 'sent', $amount); } else { $results['errors']++; $this->log_payment_request_activity($booking->ID, 'email_failed', $amount); } } else { $results['errors']++; $this->log_payment_request_activity($booking->ID, 'creation_failed', $amount); } } catch (Exception $e) { $results['errors']++; $this->log_payment_request_activity($booking->ID, 'error', 0, $e->getMessage()); } } // Update last run statistics update_option('eb_scheduler_last_run', current_time('mysql')); update_option('eb_scheduler_last_results', $results); // Send admin notification if there were errors if ($results['errors'] > 0) { $this->send_admin_error_notification($results); } return $results; } /** * Get scheduler settings */ private function get_scheduler_settings() { return array( 'days_before' => intval(get_option('eb_scheduler_days_before', 14)), 'percentage' => intval(get_option('eb_scheduler_percentage', 50)), 'expiry_hours' => intval(get_option('eb_scheduler_expiry_hours', 72)), 'email_template' => get_option('eb_scheduler_email_template', 'default'), 'admin_notifications' => get_option('eb_scheduler_admin_notifications', 'yes') ); } /** * Get bookings that need payment requests */ private function get_bookings_for_payment_requests($days_before) { global $wpdb; $target_date = date('Y-m-d', strtotime("+{$days_before} days")); $bookings = $wpdb->get_results($wpdb->prepare(" SELECT p.ID, p.post_title, pm1.meta_value as checkin_date, pm2.meta_value as customer_email, pm3.meta_value as total_price, pm4.meta_value as payment_status, pm5.meta_value as customer_name FROM {$wpdb->posts} p LEFT JOIN {$wpdb->postmeta} pm1 ON p.ID = pm1.post_id AND pm1.meta_key = '_eb_checkin_date' LEFT JOIN {$wpdb->postmeta} pm2 ON p.ID = pm2.post_id AND pm2.meta_key = '_eb_customer_email' LEFT JOIN {$wpdb->postmeta} pm3 ON p.ID = pm3.post_id AND pm3.meta_key = '_eb_total_price' LEFT JOIN {$wpdb->postmeta} pm4 ON p.ID = pm4.post_id AND pm4.meta_key = '_eb_payment_status' LEFT JOIN {$wpdb->postmeta} pm5 ON p.ID = pm5.post_id AND pm5.meta_key = '_eb_customer_name' WHERE p.post_type = 'eagle_booking' AND p.post_status = 'publish' AND pm1.meta_value = %s AND (pm4.meta_value = 'pending' OR pm4.meta_value = 'preauthorized') AND pm2.meta_value != '' AND pm3.meta_value > 0 ", $target_date)); return $bookings; } /** * Check if payment request already sent for booking */ private function payment_request_already_sent($booking_id) { global $wpdb; $table_name = $wpdb->prefix . 'eb_payment_requests'; $existing = $wpdb->get_var($wpdb->prepare(" SELECT id FROM {$table_name} WHERE booking_id = %d AND status IN ('pending', 'sent', 'paid') AND created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY) ", $booking_id)); return (bool) $existing; } /** * Calculate payment amount */ private function calculate_payment_amount($booking, $percentage) { $total_price = floatval($booking->total_price); return round(($total_price * $percentage) / 100, 2); } /** * Create automated payment request */ private function create_automated_payment_request($booking_id, $customer_email, $amount, $description, $expiry_hours) { global $wpdb; $table_name = $wpdb->prefix . 'eb_payment_requests'; // Generate unique token $token = wp_generate_password(32, false); // Create payment link $payment_link = home_url('/?eb_payment_request=' . $token); // Calculate expiry date $expires_at = date('Y-m-d H:i:s', strtotime("+{$expiry_hours} hours")); $result = $wpdb->insert( $table_name, array( 'booking_id' => $booking_id, 'customer_email' => $customer_email, 'amount' => $amount, 'description' => $description, 'token' => $token, 'payment_link' => $payment_link, 'status' => 'pending', 'created_at' => current_time('mysql'), 'expires_at' => $expires_at, 'automated' => 1 ), array('%d', '%s', '%f', '%s', '%s', '%s', '%s', '%s', '%s', '%d') ); return $result ? $wpdb->insert_id : false; } /** * Send automated payment email */ private function send_automated_payment_email($request_id, $booking) { global $wpdb; $table_name = $wpdb->prefix . 'eb_payment_requests'; $request = $wpdb->get_row( $wpdb->prepare("SELECT * FROM {$table_name} WHERE id = %d", $request_id) ); if (!$request) { return false; } $subject = sprintf( __('Pre-arrival Payment Required - Hotel Raxa Booking #%d', 'informatiq-eb-redsys'), $request->booking_id ); $message = $this->get_automated_payment_email_template($request, $booking); $headers = array( 'Content-Type: text/html; charset=UTF-8', 'From: Hotel Raxa ', 'Reply-To: Hotel Raxa ' ); $sent = wp_mail($request->customer_email, $subject, $message, $headers); if ($sent) { // Update status $wpdb->update( $table_name, array('status' => 'sent', 'sent_at' => current_time('mysql')), array('id' => $request_id), array('%s', '%s'), array('%d') ); } return $sent; } /** * Get automated payment email template */ private function get_automated_payment_email_template($request, $booking) { $checkin_date = get_post_meta($booking->ID, '_eb_checkin_date', true); $checkout_date = get_post_meta($booking->ID, '_eb_checkout_date', true); $customer_name = $booking->customer_name ?: 'Valued Guest'; $template = ' Pre-arrival Payment - Hotel Raxa

Hotel Raxa

Pre-arrival Payment Request

Dear ' . esc_html($customer_name) . ',

Thank you for choosing Hotel Raxa for your upcoming stay! We are excited to welcome you and ensure you have an exceptional experience with us.

To secure your reservation and streamline your check-in process, we kindly request a pre-arrival payment. This helps us guarantee your room and prepare for your arrival.

Booking Details

' . ($checkin_date ? '' : '') . ' ' . ($checkout_date ? '' : '') . '
Booking Reference: #' . $request->booking_id . '
Check-in Date:' . date_i18n(get_option('date_format'), strtotime($checkin_date)) . '
Check-out Date:' . date_i18n(get_option('date_format'), strtotime($checkout_date)) . '
Payment Amount: €' . number_format($request->amount, 2) . '
Payment Due: ' . date_i18n(get_option('datetime_format'), strtotime($request->expires_at)) . '
🔒 Pay Securely Now - €' . number_format($request->amount, 2) . '

⚠️ Important Information

  • This payment link will expire on ' . date_i18n(get_option('datetime_format'), strtotime($request->expires_at)) . '
  • Payment is processed securely through our trusted payment gateway
  • This represents 50% of your total booking amount
  • The remaining balance can be paid upon arrival

Why do we request pre-payment?

Pre-payment helps us guarantee your reservation, prepare your room according to your preferences, and ensures a smooth, fast check-in process when you arrive. It also helps us provide you with the highest level of personalized service during your stay.

If you have any questions about this payment or your reservation, please do not hesitate to contact us. Our team is here to assist you and ensure your stay exceeds your expectations.

We look forward to welcoming you to Hotel Raxa very soon!

Warm regards,
The Hotel Raxa Team

Hotel Raxa

📧 Email: info@hotelraxa.com | 📞 Phone: +34 XXX XXX XXX

This email was sent automatically. Please do not reply to this email.
For support, contact us at info@hotelraxa.com

'; return $template; } /** * Log payment request activity */ private function log_payment_request_activity($booking_id, $action, $amount, $error_message = '') { global $wpdb; $table_name = $wpdb->prefix . 'eb_payment_scheduler_log'; $wpdb->insert( $table_name, array( 'booking_id' => $booking_id, 'action' => $action, 'amount' => $amount, 'error_message' => $error_message, 'created_at' => current_time('mysql') ), array('%d', '%s', '%f', '%s', '%s') ); } /** * Send admin error notification */ private function send_admin_error_notification($results) { if (get_option('eb_scheduler_admin_notifications') !== 'yes') { return; } $admin_email = get_option('admin_email'); $site_name = get_bloginfo('name'); $subject = sprintf( __('Payment Scheduler Errors - %s', 'informatiq-eb-redsys'), $site_name ); $message = sprintf( __('The automated payment request scheduler encountered errors:\n\nProcessed: %d bookings\nSent: %d requests\nSkipped: %d bookings\nErrors: %d\n\nPlease check the scheduler log for details.', 'informatiq-eb-redsys'), $results['processed'], $results['sent'], $results['skipped'], $results['errors'] ); wp_mail($admin_email, $subject, $message); } /** * Add admin menu */ public function add_admin_menu() { add_submenu_page( 'eb_bookings', __('Payment Scheduler', 'informatiq-eb-redsys'), __('Payment Scheduler', 'informatiq-eb-redsys'), 'manage_options', 'eb-payment-scheduler', array($this, 'admin_page') ); } /** * Render admin page */ public function admin_page() { $settings = $this->get_scheduler_settings(); $last_run = get_option('eb_scheduler_last_run'); $last_results = get_option('eb_scheduler_last_results', array()); ?>

✓ Active

✗ Inactive

%

display_recent_activity(); ?>
prefix . 'eb_payment_scheduler_log'; // Check if table exists if ($wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") !== $table_name) { echo '

' . __('No activity log available. The log table will be created automatically when the scheduler runs.', 'informatiq-eb-redsys') . '

'; return; } $activities = $wpdb->get_results(" SELECT * FROM {$table_name} ORDER BY created_at DESC LIMIT 20 "); if (empty($activities)) { echo '

' . __('No recent activity found.', 'informatiq-eb-redsys') . '

'; return; } echo '
'; foreach ($activities as $activity) { $class = ''; $icon = ''; switch ($activity->action) { case 'sent': $class = 'activity-success'; $icon = '✓'; $message = sprintf(__('Payment request sent for booking #%d (€%.2f)', 'informatiq-eb-redsys'), $activity->booking_id, $activity->amount); break; case 'email_failed': $class = 'activity-error'; $icon = '✗'; $message = sprintf(__('Email failed for booking #%d', 'informatiq-eb-redsys'), $activity->booking_id); break; case 'creation_failed': $class = 'activity-error'; $icon = '✗'; $message = sprintf(__('Payment request creation failed for booking #%d', 'informatiq-eb-redsys'), $activity->booking_id); break; case 'error': $class = 'activity-error'; $icon = '✗'; $message = sprintf(__('Error processing booking #%d: %s', 'informatiq-eb-redsys'), $activity->booking_id, $activity->error_message); break; default: $class = 'activity-info'; $icon = 'ℹ'; $message = sprintf(__('Action "%s" for booking #%d', 'informatiq-eb-redsys'), $activity->action, $activity->booking_id); } echo '
'; echo '
' . $icon . '' . esc_html($message) . '
'; echo '
' . date_i18n(get_option('datetime_format'), strtotime($activity->created_at)) . '
'; echo '
'; } echo '
'; } /** * Save scheduler settings (AJAX handler) */ public function save_scheduler_settings() { check_ajax_referer('scheduler_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_die(__('Insufficient permissions', 'informatiq-eb-redsys')); } $settings = array( 'eb_scheduler_days_before' => intval($_POST['days_before']), 'eb_scheduler_percentage' => intval($_POST['percentage']), 'eb_scheduler_expiry_hours' => intval($_POST['expiry_hours']), 'eb_scheduler_admin_notifications' => isset($_POST['admin_notifications']) ? 'yes' : 'no' ); foreach ($settings as $key => $value) { update_option($key, $value); } wp_send_json_success(__('Settings saved successfully', 'informatiq-eb-redsys')); } /** * Manual payment check (AJAX handler) */ public function manual_payment_check() { check_ajax_referer('scheduler_nonce', 'nonce'); if (!current_user_can('manage_options')) { wp_die(__('Insufficient permissions', 'informatiq-eb-redsys')); } $results = $this->process_daily_payment_requests(); wp_send_json_success($results); } /** * Enqueue admin scripts */ public function enqueue_admin_scripts($hook) { if (strpos($hook, 'eb-payment-scheduler') === false) { return; } wp_enqueue_script( 'eb-scheduler-admin', plugin_dir_url(__FILE__) . '../assets/js/scheduler-admin.js', array('jquery'), '1.0.0', true ); wp_localize_script('eb-scheduler-admin', 'ebScheduler', array( 'ajaxurl' => admin_url('admin-ajax.php'), 'nonce' => wp_create_nonce('scheduler_nonce'), 'strings' => array( 'confirmManualRun' => __('Are you sure you want to run a manual payment check?', 'informatiq-eb-redsys'), 'processing' => __('Processing...', 'informatiq-eb-redsys'), 'completed' => __('Manual check completed!', 'informatiq-eb-redsys') ) )); } /** * Create database table for scheduler log */ public static function create_scheduler_table() { global $wpdb; $table_name = $wpdb->prefix . 'eb_payment_scheduler_log'; $charset_collate = $wpdb->get_charset_collate(); $sql = "CREATE TABLE {$table_name} ( id int(11) NOT NULL AUTO_INCREMENT, booking_id int(11) NOT NULL, action varchar(50) NOT NULL, amount decimal(10,2) DEFAULT 0, error_message text, created_at datetime NOT NULL, PRIMARY KEY (id), KEY booking_id (booking_id), KEY action (action), KEY created_at (created_at) ) {$charset_collate};"; require_once ABSPATH . 'wp-admin/includes/upgrade.php'; dbDelta($sql); } } // Initialize the scheduler new EB_Payment_Scheduler(); // Create table on activation register_activation_hook(EB_REDSYS_FILE, array('EB_Payment_Scheduler', 'create_scheduler_table'));