Files
Hotel Raxa Dev 8bd12173b5 Hotel Raxa - Advanced Booking System Implementation
🏨 Hotel Booking Enhancements:
- Implemented Eagle Booking Advanced Pricing add-on
- Added Booking.com-style rate management system
- Created professional calendar interface for pricing
- Integrated deals and discounts functionality

💰 Advanced Pricing Features:
- Dynamic pricing models (per room, per person, per adult)
- Base rates, adult rates, and child rates management
- Length of stay discounts and early bird deals
- Mobile rates and secret deals implementation
- Seasonal promotions and flash sales

📅 Availability Management:
- Real-time availability tracking
- Stop sell and restriction controls
- Closed to arrival/departure functionality
- Minimum/maximum stay requirements
- Automatic sold-out management

💳 Payment Integration:
- Maintained Redsys payment gateway integration
- Seamless integration with existing Eagle Booking
- No modifications to core Eagle Booking plugin

🛠️ Technical Implementation:
- Custom database tables for advanced pricing
- WordPress hooks and filters integration
- AJAX-powered admin interface
- Data migration from existing Eagle Booking
- Professional calendar view for revenue management

📊 Admin Interface:
- Booking.com-style management dashboard
- Visual rate and availability calendar
- Bulk operations for date ranges
- Statistics and analytics dashboard
- Modal dialogs for quick editing

🔧 Code Quality:
- WordPress coding standards compliance
- Secure database operations with prepared statements
- Proper input validation and sanitization
- Error handling and logging
- Responsive admin interface

🤖 Generated with Claude Code (https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-07-11 14:44:06 +02:00

829 lines
35 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* Automated Payment Request Scheduler
*
* Handles automated sending of payment requests 14 days before check-in
* Manages the 50% payment workflow for Hotel Raxa
*
* 🤖 Generated with Claude Code (https://claude.ai/code)
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
class EB_Payment_Scheduler {
/**
* Constructor
*/
public function __construct() {
add_action('init', array($this, 'init'));
}
/**
* Initialize the scheduler
*/
public function init() {
// Schedule the daily cron job
add_action('wp', array($this, 'schedule_payment_requests'));
// Hook the cron job
add_action('eb_daily_payment_check', array($this, 'process_daily_payment_requests'));
// Add admin interface
add_action('admin_menu', array($this, 'add_admin_menu'));
// AJAX handlers
add_action('wp_ajax_save_scheduler_settings', array($this, 'save_scheduler_settings'));
add_action('wp_ajax_manual_payment_check', array($this, 'manual_payment_check'));
// Enqueue admin scripts
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
}
/**
* Schedule the payment requests cron job
*/
public function schedule_payment_requests() {
if (!wp_next_scheduled('eb_daily_payment_check')) {
wp_schedule_event(time(), 'daily', 'eb_daily_payment_check');
}
}
/**
* Process daily payment requests
*/
public function process_daily_payment_requests() {
// Check if automated requests are enabled
if (get_option('eb_redsys_auto_requests_enabled') !== '1') {
return;
}
$settings = $this->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 <reservas@hotelraxa.com>',
'Reply-To: Hotel Raxa <info@hotelraxa.com>'
);
$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 = '
<html>
<head>
<meta charset="UTF-8">
<title>Pre-arrival Payment - Hotel Raxa</title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333; background: #f5f5f5; margin: 0; padding: 20px;">
<div style="max-width: 600px; margin: 0 auto; background: white; border-radius: 10px; overflow: hidden; box-shadow: 0 4px 15px rgba(0,0,0,0.1);">
<!-- Header -->
<div style="background: linear-gradient(135deg, #2c5aa0 0%, #1e3d6f 100%); color: white; padding: 30px 20px; text-align: center;">
<h1 style="margin: 0; font-size: 28px;">Hotel Raxa</h1>
<p style="margin: 10px 0 0; font-size: 16px; opacity: 0.9;">Pre-arrival Payment Request</p>
</div>
<!-- Content -->
<div style="padding: 30px;">
<h2 style="color: #2c5aa0; margin-top: 0;">Dear ' . esc_html($customer_name) . ',</h2>
<p style="font-size: 16px; margin-bottom: 20px;">
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.
</p>
<p style="font-size: 16px; margin-bottom: 25px;">
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.
</p>
<!-- Booking Details Box -->
<div style="background: #f8f9fa; border: 1px solid #e9ecef; border-radius: 8px; padding: 25px; margin: 25px 0;">
<h3 style="margin-top: 0; color: #2c5aa0; border-bottom: 2px solid #2c5aa0; padding-bottom: 10px;">Booking Details</h3>
<table style="width: 100%; border-collapse: collapse;">
<tr>
<td style="padding: 8px 0; font-weight: bold; width: 40%;">Booking Reference:</td>
<td style="padding: 8px 0;">#' . $request->booking_id . '</td>
</tr>
' . ($checkin_date ? '<tr><td style="padding: 8px 0; font-weight: bold;">Check-in Date:</td><td style="padding: 8px 0;">' . date_i18n(get_option('date_format'), strtotime($checkin_date)) . '</td></tr>' : '') . '
' . ($checkout_date ? '<tr><td style="padding: 8px 0; font-weight: bold;">Check-out Date:</td><td style="padding: 8px 0;">' . date_i18n(get_option('date_format'), strtotime($checkout_date)) . '</td></tr>' : '') . '
<tr>
<td style="padding: 8px 0; font-weight: bold;">Payment Amount:</td>
<td style="padding: 8px 0; font-size: 18px; color: #2c5aa0; font-weight: bold;">€' . number_format($request->amount, 2) . '</td>
</tr>
<tr>
<td style="padding: 8px 0; font-weight: bold;">Payment Due:</td>
<td style="padding: 8px 0; color: #dc3545;">' . date_i18n(get_option('datetime_format'), strtotime($request->expires_at)) . '</td>
</tr>
</table>
</div>
<!-- Payment Button -->
<div style="text-align: center; margin: 35px 0;">
<a href="' . $request->payment_link . '"
style="background: linear-gradient(135deg, #007cba 0%, #005a87 100%);
color: white;
padding: 18px 40px;
text-decoration: none;
border-radius: 8px;
display: inline-block;
font-weight: bold;
font-size: 18px;
box-shadow: 0 4px 10px rgba(0,124,186,0.3);
transition: all 0.3s ease;">
🔒 Pay Securely Now - €' . number_format($request->amount, 2) . '
</a>
</div>
<!-- Important Notice -->
<div style="background: #fff3cd; border: 1px solid #ffeaa7; border-radius: 8px; padding: 20px; margin: 25px 0;">
<h4 style="margin-top: 0; color: #856404; display: flex; align-items: center;">
⚠️ Important Information
</h4>
<ul style="margin: 10px 0; color: #856404; padding-left: 20px;">
<li>This payment link will expire on <strong>' . date_i18n(get_option('datetime_format'), strtotime($request->expires_at)) . '</strong></li>
<li>Payment is processed securely through our trusted payment gateway</li>
<li>This represents 50% of your total booking amount</li>
<li>The remaining balance can be paid upon arrival</li>
</ul>
</div>
<!-- Why Pre-payment -->
<div style="background: #e3f2fd; border-left: 4px solid #2196f3; padding: 20px; margin: 25px 0;">
<h4 style="margin-top: 0; color: #1565c0;">Why do we request pre-payment?</h4>
<p style="margin-bottom: 0; color: #1565c0;">
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.
</p>
</div>
<p style="font-size: 16px;">
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.
</p>
<p style="font-size: 16px;">
We look forward to welcoming you to Hotel Raxa very soon!
</p>
<p style="font-size: 16px; margin-bottom: 0;">
Warm regards,<br>
<strong>The Hotel Raxa Team</strong>
</p>
</div>
<!-- Footer -->
<div style="background: #f8f9fa; padding: 25px; text-align: center; border-top: 1px solid #e9ecef;">
<p style="margin: 0 0 10px; font-weight: bold; color: #2c5aa0;">Hotel Raxa</p>
<p style="margin: 0 0 10px; font-size: 14px; color: #666;">
📧 Email: info@hotelraxa.com | 📞 Phone: +34 XXX XXX XXX
</p>
<p style="margin: 0; font-size: 12px; color: #999;">
This email was sent automatically. Please do not reply to this email.<br>
For support, contact us at info@hotelraxa.com
</p>
</div>
</div>
</body>
</html>';
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());
?>
<div class="wrap">
<h1><?php _e('Automated Payment Scheduler', 'informatiq-eb-redsys'); ?></h1>
<div class="eb-scheduler-admin">
<!-- Current Status -->
<div class="postbox">
<h2 class="hndle"><?php _e('Scheduler Status', 'informatiq-eb-redsys'); ?></h2>
<div class="inside">
<table class="form-table">
<tr>
<th scope="row"><?php _e('Status', 'informatiq-eb-redsys'); ?></th>
<td>
<?php if (wp_next_scheduled('eb_daily_payment_check')): ?>
<span style="color: #00a32a; font-weight: bold;">✓ Active</span>
<p class="description">
<?php _e('Next run:', 'informatiq-eb-redsys'); ?>
<?php echo date_i18n(get_option('datetime_format'), wp_next_scheduled('eb_daily_payment_check')); ?>
</p>
<?php else: ?>
<span style="color: #d63638; font-weight: bold;">✗ Inactive</span>
<?php endif; ?>
</td>
</tr>
<tr>
<th scope="row"><?php _e('Last Run', 'informatiq-eb-redsys'); ?></th>
<td>
<?php if ($last_run): ?>
<?php echo date_i18n(get_option('datetime_format'), strtotime($last_run)); ?>
<?php if (!empty($last_results)): ?>
<br>
<small>
<?php
printf(
__('Processed: %d | Sent: %d | Skipped: %d | Errors: %d', 'informatiq-eb-redsys'),
$last_results['processed'] ?? 0,
$last_results['sent'] ?? 0,
$last_results['skipped'] ?? 0,
$last_results['errors'] ?? 0
);
?>
</small>
<?php endif; ?>
<?php else: ?>
<?php _e('Never run', 'informatiq-eb-redsys'); ?>
<?php endif; ?>
</td>
</tr>
</table>
<p>
<button type="button" id="manual-payment-check" class="button button-primary">
<?php _e('Run Manual Check', 'informatiq-eb-redsys'); ?>
</button>
</p>
</div>
</div>
<!-- Settings -->
<div class="postbox">
<h2 class="hndle"><?php _e('Scheduler Settings', 'informatiq-eb-redsys'); ?></h2>
<div class="inside">
<form id="scheduler-settings-form">
<table class="form-table">
<tr>
<th scope="row">
<label for="days_before"><?php _e('Days Before Check-in', 'informatiq-eb-redsys'); ?></label>
</th>
<td>
<input type="number" id="days_before" name="days_before"
value="<?php echo esc_attr($settings['days_before']); ?>"
min="1" max="365" class="small-text">
<p class="description">
<?php _e('Number of days before check-in to send payment requests', 'informatiq-eb-redsys'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="percentage"><?php _e('Payment Percentage', 'informatiq-eb-redsys'); ?></label>
</th>
<td>
<input type="number" id="percentage" name="percentage"
value="<?php echo esc_attr($settings['percentage']); ?>"
min="1" max="100" class="small-text">
<span>%</span>
<p class="description">
<?php _e('Percentage of total booking amount to request', 'informatiq-eb-redsys'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="expiry_hours"><?php _e('Payment Link Expiry', 'informatiq-eb-redsys'); ?></label>
</th>
<td>
<select id="expiry_hours" name="expiry_hours">
<option value="24" <?php selected($settings['expiry_hours'], 24); ?>><?php _e('24 hours', 'informatiq-eb-redsys'); ?></option>
<option value="48" <?php selected($settings['expiry_hours'], 48); ?>><?php _e('48 hours', 'informatiq-eb-redsys'); ?></option>
<option value="72" <?php selected($settings['expiry_hours'], 72); ?>><?php _e('72 hours', 'informatiq-eb-redsys'); ?></option>
<option value="168" <?php selected($settings['expiry_hours'], 168); ?>><?php _e('1 week', 'informatiq-eb-redsys'); ?></option>
</select>
</td>
</tr>
<tr>
<th scope="row">
<?php _e('Admin Notifications', 'informatiq-eb-redsys'); ?>
</th>
<td>
<label>
<input type="checkbox" id="admin_notifications" name="admin_notifications"
value="yes" <?php checked($settings['admin_notifications'], 'yes'); ?>>
<?php _e('Send email notifications when errors occur', 'informatiq-eb-redsys'); ?>
</label>
</td>
</tr>
</table>
<p class="submit">
<button type="submit" class="button-primary">
<?php _e('Save Settings', 'informatiq-eb-redsys'); ?>
</button>
</p>
</form>
</div>
</div>
<!-- Recent Activity -->
<div class="postbox">
<h2 class="hndle"><?php _e('Recent Activity', 'informatiq-eb-redsys'); ?></h2>
<div class="inside">
<?php $this->display_recent_activity(); ?>
</div>
</div>
</div>
</div>
<style>
.eb-scheduler-admin .postbox {
margin-bottom: 20px;
}
.activity-log {
max-height: 400px;
overflow-y: auto;
border: 1px solid #ddd;
background: #f9f9f9;
}
.activity-item {
padding: 10px;
border-bottom: 1px solid #eee;
display: flex;
justify-content: space-between;
align-items: center;
}
.activity-item:last-child {
border-bottom: none;
}
.activity-success {
border-left: 4px solid #00a32a;
}
.activity-error {
border-left: 4px solid #d63638;
}
.activity-info {
border-left: 4px solid #2271b1;
}
</style>
<?php
}
/**
* Display recent activity
*/
private function display_recent_activity() {
global $wpdb;
$table_name = $wpdb->prefix . 'eb_payment_scheduler_log';
// Check if table exists
if ($wpdb->get_var("SHOW TABLES LIKE '{$table_name}'") !== $table_name) {
echo '<p>' . __('No activity log available. The log table will be created automatically when the scheduler runs.', 'informatiq-eb-redsys') . '</p>';
return;
}
$activities = $wpdb->get_results("
SELECT * FROM {$table_name}
ORDER BY created_at DESC
LIMIT 20
");
if (empty($activities)) {
echo '<p>' . __('No recent activity found.', 'informatiq-eb-redsys') . '</p>';
return;
}
echo '<div class="activity-log">';
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 '<div class="activity-item ' . $class . '">';
echo '<div><span style="margin-right: 10px;">' . $icon . '</span>' . esc_html($message) . '</div>';
echo '<div><small>' . date_i18n(get_option('datetime_format'), strtotime($activity->created_at)) . '</small></div>';
echo '</div>';
}
echo '</div>';
}
/**
* 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'));