🏨 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>
829 lines
35 KiB
PHP
829 lines
35 KiB
PHP
<?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'));
|