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

850 lines
37 KiB
PHP

<?php
/**
* Redsys Payment Requests Extension
*
* Adds payment request functionality to Redsys gateway
* Allows sending secure payment links to customers
*
* 🤖 Generated with Claude Code (https://claude.ai/code)
*/
// Exit if accessed directly
if (!defined('ABSPATH')) {
exit;
}
class EB_Redsys_Payment_Requests {
/**
* Constructor
*/
public function __construct() {
add_action('init', array($this, 'init'));
}
/**
* Initialize the class
*/
public function init() {
// Add admin menu
add_action('admin_menu', array($this, 'add_admin_menu'));
// AJAX handlers
add_action('wp_ajax_send_payment_request', array($this, 'send_payment_request'));
add_action('wp_ajax_create_payment_link', array($this, 'create_payment_link'));
// Handle payment link processing
add_action('init', array($this, 'handle_payment_link'));
// Add payment request metabox to bookings
add_action('add_meta_boxes', array($this, 'add_booking_metabox'));
// Enqueue scripts
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
// Create database table
register_activation_hook(EB_REDSYS_PLUGIN_FILE, array($this, 'create_database_table'));
// Schedule automatic payment requests
add_action('eb_payment_request_cron', array($this, 'process_scheduled_payment_requests'));
// Add cron schedule
if (!wp_next_scheduled('eb_payment_request_cron')) {
wp_schedule_event(time(), 'daily', 'eb_payment_request_cron');
}
}
/**
* Add admin menu
*/
public function add_admin_menu() {
add_submenu_page(
'eb_bookings',
__('Payment Requests', 'informatiq-eb-redsys'),
__('Payment Requests', 'informatiq-eb-redsys'),
'manage_options',
'eb-payment-requests',
array($this, 'admin_page')
);
}
/**
* Render admin page
*/
public function admin_page() {
?>
<div class="wrap">
<h1><?php _e('Payment Requests', 'informatiq-eb-redsys'); ?></h1>
<div class="eb-payment-requests-admin">
<!-- Send New Payment Request -->
<div class="postbox">
<h2 class="hndle"><?php _e('Send New Payment Request', 'informatiq-eb-redsys'); ?></h2>
<div class="inside">
<form id="payment-request-form">
<table class="form-table">
<tr>
<th scope="row">
<label for="booking_id"><?php _e('Booking ID', 'informatiq-eb-redsys'); ?></label>
</th>
<td>
<input type="number" id="booking_id" name="booking_id" class="regular-text" required>
<button type="button" id="load-booking" class="button"><?php _e('Load Booking', 'informatiq-eb-redsys'); ?></button>
</td>
</tr>
<tr>
<th scope="row">
<label for="customer_email"><?php _e('Customer Email', 'informatiq-eb-redsys'); ?></label>
</th>
<td>
<input type="email" id="customer_email" name="customer_email" class="regular-text" required>
</td>
</tr>
<tr>
<th scope="row">
<label for="amount"><?php _e('Amount (€)', 'informatiq-eb-redsys'); ?></label>
</th>
<td>
<input type="number" id="amount" name="amount" step="0.01" class="regular-text" required>
<p class="description">
<?php _e('Amount to request from customer', 'informatiq-eb-redsys'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="description"><?php _e('Description', 'informatiq-eb-redsys'); ?></label>
</th>
<td>
<textarea id="description" name="description" rows="3" class="large-text"></textarea>
<p class="description">
<?php _e('Optional description for the payment request', 'informatiq-eb-redsys'); ?>
</p>
</td>
</tr>
<tr>
<th scope="row">
<label for="expires_in"><?php _e('Expires In', 'informatiq-eb-redsys'); ?></label>
</th>
<td>
<select id="expires_in" name="expires_in">
<option value="24"><?php _e('24 hours', 'informatiq-eb-redsys'); ?></option>
<option value="72" selected><?php _e('3 days', 'informatiq-eb-redsys'); ?></option>
<option value="168"><?php _e('1 week', 'informatiq-eb-redsys'); ?></option>
<option value="336"><?php _e('2 weeks', 'informatiq-eb-redsys'); ?></option>
</select>
</td>
</tr>
</table>
<p class="submit">
<button type="submit" class="button-primary">
<?php _e('Send Payment Request', 'informatiq-eb-redsys'); ?>
</button>
</p>
</form>
</div>
</div>
<!-- Recent Payment Requests -->
<div class="postbox">
<h2 class="hndle"><?php _e('Recent Payment Requests', 'informatiq-eb-redsys'); ?></h2>
<div class="inside">
<?php $this->display_payment_requests_table(); ?>
</div>
</div>
<!-- Automatic Payment Requests -->
<div class="postbox">
<h2 class="hndle"><?php _e('Automatic Payment Requests', 'informatiq-eb-redsys'); ?></h2>
<div class="inside">
<form id="auto-payment-settings">
<table class="form-table">
<tr>
<th scope="row">
<?php _e('Enable Automatic Requests', 'informatiq-eb-redsys'); ?>
</th>
<td>
<label>
<input type="checkbox" id="auto_requests_enabled" name="auto_requests_enabled"
value="1" <?php checked(get_option('eb_redsys_auto_requests_enabled'), '1'); ?>>
<?php _e('Send payment requests automatically 14 days before check-in', 'informatiq-eb-redsys'); ?>
</label>
</td>
</tr>
<tr>
<th scope="row">
<label for="auto_percentage"><?php _e('Payment Percentage', 'informatiq-eb-redsys'); ?></label>
</th>
<td>
<input type="number" id="auto_percentage" name="auto_percentage"
value="<?php echo esc_attr(get_option('eb_redsys_auto_percentage', '50')); ?>"
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="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(get_option('eb_redsys_days_before', '14')); ?>"
min="1" max="365" class="small-text">
<p class="description">
<?php _e('Number of days before check-in to send the payment request', 'informatiq-eb-redsys'); ?>
</p>
</td>
</tr>
</table>
<p class="submit">
<button type="submit" class="button-primary">
<?php _e('Save Settings', 'informatiq-eb-redsys'); ?>
</button>
</p>
</form>
</div>
</div>
</div>
</div>
<style>
.eb-payment-requests-admin .postbox {
margin-bottom: 20px;
}
.payment-request-status {
padding: 3px 8px;
border-radius: 3px;
font-size: 11px;
font-weight: bold;
text-transform: uppercase;
}
.status-pending {
background: #fff3cd;
color: #856404;
border: 1px solid #ffeaa7;
}
.status-sent {
background: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.status-paid {
background: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.status-expired {
background: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.payment-link {
font-family: monospace;
background: #f8f9fa;
padding: 5px;
border-radius: 3px;
word-break: break-all;
}
</style>
<?php
}
/**
* Display payment requests table
*/
private function display_payment_requests_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'eb_payment_requests';
$requests = $wpdb->get_results(
"SELECT * FROM {$table_name} ORDER BY created_at DESC LIMIT 20"
);
if (empty($requests)) {
echo '<p>' . __('No payment requests found.', 'informatiq-eb-redsys') . '</p>';
return;
}
?>
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th><?php _e('ID', 'informatiq-eb-redsys'); ?></th>
<th><?php _e('Booking', 'informatiq-eb-redsys'); ?></th>
<th><?php _e('Customer', 'informatiq-eb-redsys'); ?></th>
<th><?php _e('Amount', 'informatiq-eb-redsys'); ?></th>
<th><?php _e('Status', 'informatiq-eb-redsys'); ?></th>
<th><?php _e('Created', 'informatiq-eb-redsys'); ?></th>
<th><?php _e('Expires', 'informatiq-eb-redsys'); ?></th>
<th><?php _e('Actions', 'informatiq-eb-redsys'); ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($requests as $request): ?>
<tr>
<td><?php echo $request->id; ?></td>
<td>
<a href="<?php echo admin_url('post.php?post=' . $request->booking_id . '&action=edit'); ?>">
#<?php echo $request->booking_id; ?>
</a>
</td>
<td><?php echo esc_html($request->customer_email); ?></td>
<td>€<?php echo number_format($request->amount, 2); ?></td>
<td>
<span class="payment-request-status status-<?php echo esc_attr($request->status); ?>">
<?php echo ucfirst($request->status); ?>
</span>
</td>
<td><?php echo date_i18n(get_option('date_format'), strtotime($request->created_at)); ?></td>
<td><?php echo date_i18n(get_option('date_format'), strtotime($request->expires_at)); ?></td>
<td>
<?php if ($request->status === 'pending'): ?>
<button class="button button-small resend-request" data-request-id="<?php echo $request->id; ?>">
<?php _e('Resend', 'informatiq-eb-redsys'); ?>
</button>
<?php endif; ?>
<?php if ($request->status !== 'paid'): ?>
<button class="button button-small copy-link" data-link="<?php echo esc_attr($request->payment_link); ?>">
<?php _e('Copy Link', 'informatiq-eb-redsys'); ?>
</button>
<?php endif; ?>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<?php
}
/**
* Send payment request (AJAX handler)
*/
public function send_payment_request() {
check_ajax_referer('payment_request_nonce', 'nonce');
if (!current_user_can('manage_options')) {
wp_die(__('Insufficient permissions', 'informatiq-eb-redsys'));
}
$booking_id = intval($_POST['booking_id']);
$customer_email = sanitize_email($_POST['customer_email']);
$amount = floatval($_POST['amount']);
$description = sanitize_textarea_field($_POST['description']);
$expires_in = intval($_POST['expires_in']);
// Validate inputs
if (!$booking_id || !$customer_email || !$amount) {
wp_send_json_error(__('Missing required fields', 'informatiq-eb-redsys'));
}
// Create payment request
$request_id = $this->create_payment_request(
$booking_id,
$customer_email,
$amount,
$description,
$expires_in
);
if ($request_id) {
// Send email
$sent = $this->send_payment_request_email($request_id);
if ($sent) {
wp_send_json_success(__('Payment request sent successfully', 'informatiq-eb-redsys'));
} else {
wp_send_json_error(__('Payment request created but email failed to send', 'informatiq-eb-redsys'));
}
} else {
wp_send_json_error(__('Failed to create payment request', 'informatiq-eb-redsys'));
}
}
/**
* Create payment request in database
*/
private function create_payment_request($booking_id, $customer_email, $amount, $description, $expires_in) {
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("+{$expires_in} 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
),
array('%d', '%s', '%f', '%s', '%s', '%s', '%s', '%s', '%s')
);
return $result ? $wpdb->insert_id : false;
}
/**
* Send payment request email
*/
private function send_payment_request_email($request_id) {
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;
}
// Get booking details
$booking = get_post($request->booking_id);
$subject = sprintf(
__('Payment Request for Booking #%d - Hotel Raxa', 'informatiq-eb-redsys'),
$request->booking_id
);
$message = $this->get_payment_request_email_template($request, $booking);
$headers = array(
'Content-Type: text/html; charset=UTF-8',
'From: Hotel Raxa <noreply@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 payment request email template
*/
private function get_payment_request_email_template($request, $booking) {
$template = '
<html>
<head>
<meta charset="UTF-8">
<title>Payment Request - Hotel Raxa</title>
</head>
<body style="font-family: Arial, sans-serif; line-height: 1.6; color: #333;">
<div style="max-width: 600px; margin: 0 auto; padding: 20px;">
<div style="background: #2c5aa0; color: white; padding: 20px; text-align: center;">
<h1 style="margin: 0;">Hotel Raxa</h1>
<p style="margin: 10px 0 0;">Payment Request</p>
</div>
<div style="padding: 30px; border: 1px solid #ddd;">
<h2>Dear Guest,</h2>
<p>We hope you are looking forward to your stay at Hotel Raxa. This is a payment request for your upcoming booking.</p>
<div style="background: #f8f9fa; padding: 20px; margin: 20px 0; border-radius: 5px;">
<h3 style="margin-top: 0;">Booking Details</h3>
<p><strong>Booking ID:</strong> #' . $request->booking_id . '</p>
<p><strong>Amount Requested:</strong> €' . number_format($request->amount, 2) . '</p>
' . ($request->description ? '<p><strong>Description:</strong> ' . esc_html($request->description) . '</p>' : '') . '
<p><strong>Payment Due:</strong> ' . date_i18n(get_option('date_format'), strtotime($request->expires_at)) . '</p>
</div>
<div style="text-align: center; margin: 30px 0;">
<a href="' . $request->payment_link . '"
style="background: #007cba; color: white; padding: 15px 30px; text-decoration: none; border-radius: 5px; display: inline-block; font-weight: bold;">
Pay Now - €' . number_format($request->amount, 2) . '
</a>
</div>
<div style="background: #fff3cd; padding: 15px; margin: 20px 0; border-radius: 5px; border-left: 4px solid #ffc107;">
<p style="margin: 0;"><strong>Important:</strong> This payment link will expire on ' . date_i18n(get_option('datetime_format'), strtotime($request->expires_at)) . '. Please complete your payment before this date to secure your reservation.</p>
</div>
<p>If you have any questions about this payment request or your booking, please contact us immediately.</p>
<p>Thank you for choosing Hotel Raxa!</p>
</div>
<div style="background: #f8f9fa; padding: 15px; text-align: center; font-size: 12px; color: #666;">
<p>Hotel Raxa | For questions, please contact: info@hotelraxa.com</p>
<p>This email was sent automatically. Please do not reply to this email.</p>
</div>
</div>
</body>
</html>';
return $template;
}
/**
* Handle payment link processing
*/
public function handle_payment_link() {
if (!isset($_GET['eb_payment_request'])) {
return;
}
$token = sanitize_text_field($_GET['eb_payment_request']);
global $wpdb;
$table_name = $wpdb->prefix . 'eb_payment_requests';
$request = $wpdb->get_row(
$wpdb->prepare("SELECT * FROM {$table_name} WHERE token = %s", $token)
);
if (!$request) {
wp_die(__('Invalid payment request', 'informatiq-eb-redsys'));
}
// Check if expired
if (strtotime($request->expires_at) < time()) {
wp_die(__('This payment request has expired', 'informatiq-eb-redsys'));
}
// Check if already paid
if ($request->status === 'paid') {
wp_die(__('This payment request has already been completed', 'informatiq-eb-redsys'));
}
// Process payment with Redsys
$this->process_payment_request($request);
}
/**
* Process payment request
*/
private function process_payment_request($request) {
// Get Redsys settings
$merchant_code = get_option('eb_redsys_merchant_code');
$terminal = get_option('eb_redsys_terminal');
$encryption_key = get_option('eb_redsys_encryption_key');
$test_mode = get_option('eb_redsys_test_mode') === 'yes';
// Include Redsys API
require_once dirname(__FILE__) . '/../api/class-redsys-api.php';
$redsys = new Redsys_API();
// Generate order number
$order_number = 'PR' . str_pad($request->id, 8, '0', STR_PAD_LEFT);
// Set payment parameters
$redsys->setParameter('DS_MERCHANT_AMOUNT', $request->amount * 100); // Amount in cents
$redsys->setParameter('DS_MERCHANT_ORDER', $order_number);
$redsys->setParameter('DS_MERCHANT_MERCHANTCODE', $merchant_code);
$redsys->setParameter('DS_MERCHANT_CURRENCY', '978'); // EUR
$redsys->setParameter('DS_MERCHANT_TRANSACTIONTYPE', '0'); // Authorization
$redsys->setParameter('DS_MERCHANT_TERMINAL', $terminal);
$redsys->setParameter('DS_MERCHANT_MERCHANTURL', home_url('/?eb-redsys-callback=payment-request'));
$redsys->setParameter('DS_MERCHANT_URLOK', home_url('/?eb_payment_success=' . $request->token));
$redsys->setParameter('DS_MERCHANT_URLKO', home_url('/?eb_payment_error=' . $request->token));
// Generate signature
$signature = $redsys->createMerchantSignature($encryption_key);
// Get Redsys URL
$redsys_url = $test_mode ?
'https://sis-t.redsys.es:25443/sis/realizarPago' :
'https://sis.redsys.es/sis/realizarPago';
// Display payment form
$this->display_payment_form($redsys_url, $redsys, $signature, $request);
}
/**
* Display payment form
*/
private function display_payment_form($redsys_url, $redsys, $signature, $request) {
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title><?php _e('Payment Request - Hotel Raxa', 'informatiq-eb-redsys'); ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body { font-family: Arial, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
.container { max-width: 600px; margin: 0 auto; background: white; padding: 30px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
.header { text-align: center; background: #2c5aa0; color: white; padding: 20px; margin: -30px -30px 30px; border-radius: 10px 10px 0 0; }
.amount { font-size: 2em; color: #2c5aa0; text-align: center; margin: 20px 0; }
.details { background: #f8f9fa; padding: 20px; margin: 20px 0; border-radius: 5px; }
.pay-button { background: #007cba; color: white; padding: 15px 30px; border: none; border-radius: 5px; font-size: 16px; cursor: pointer; width: 100%; }
.pay-button:hover { background: #005a87; }
.security-info { font-size: 12px; color: #666; text-align: center; margin-top: 20px; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>Hotel Raxa</h1>
<p>Secure Payment Request</p>
</div>
<div class="details">
<h3><?php _e('Payment Details', 'informatiq-eb-redsys'); ?></h3>
<p><strong><?php _e('Booking ID:', 'informatiq-eb-redsys'); ?></strong> #<?php echo $request->booking_id; ?></p>
<p><strong><?php _e('Description:', 'informatiq-eb-redsys'); ?></strong> <?php echo esc_html($request->description ?: __('Hotel booking payment', 'informatiq-eb-redsys')); ?></p>
<p><strong><?php _e('Payment expires:', 'informatiq-eb-redsys'); ?></strong> <?php echo date_i18n(get_option('datetime_format'), strtotime($request->expires_at)); ?></p>
</div>
<div class="amount">
€<?php echo number_format($request->amount, 2); ?>
</div>
<form action="<?php echo $redsys_url; ?>" method="post" id="payment-form">
<input type="hidden" name="Ds_SignatureVersion" value="HMAC_SHA256_V1">
<input type="hidden" name="Ds_MerchantParameters" value="<?php echo $redsys->createMerchantParameters(); ?>">
<input type="hidden" name="Ds_Signature" value="<?php echo $signature; ?>">
<button type="submit" class="pay-button">
<?php printf(__('Pay €%.2f Securely', 'informatiq-eb-redsys'), $request->amount); ?>
</button>
</form>
<div class="security-info">
<p><?php _e('This payment is processed securely through Redsys payment gateway.', 'informatiq-eb-redsys'); ?></p>
<p><?php _e('Your payment information is encrypted and secure.', 'informatiq-eb-redsys'); ?></p>
</div>
</div>
<script>
// Auto-submit form after 2 seconds to improve UX
setTimeout(function() {
if (confirm('<?php _e("Redirect to secure payment page?", "informatiq-eb-redsys"); ?>')) {
document.getElementById('payment-form').submit();
}
}, 2000);
</script>
</body>
</html>
<?php
exit;
}
/**
* Create database table for payment requests
*/
public function create_database_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'eb_payment_requests';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE {$table_name} (
id int(11) NOT NULL AUTO_INCREMENT,
booking_id int(11) NOT NULL,
customer_email varchar(255) NOT NULL,
amount decimal(10,2) NOT NULL,
description text,
token varchar(32) NOT NULL,
payment_link text NOT NULL,
status varchar(20) DEFAULT 'pending',
created_at datetime NOT NULL,
sent_at datetime,
expires_at datetime NOT NULL,
paid_at datetime,
redsys_order varchar(20),
redsys_auth_code varchar(20),
PRIMARY KEY (id),
UNIQUE KEY token (token),
KEY booking_id (booking_id),
KEY status (status),
KEY expires_at (expires_at)
) {$charset_collate};";
require_once ABSPATH . 'wp-admin/includes/upgrade.php';
dbDelta($sql);
}
/**
* Process scheduled payment requests
*/
public function process_scheduled_payment_requests() {
if (get_option('eb_redsys_auto_requests_enabled') !== '1') {
return;
}
$days_before = intval(get_option('eb_redsys_days_before', 14));
$percentage = intval(get_option('eb_redsys_auto_percentage', 50));
// Get bookings that need payment requests
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
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'
WHERE p.post_type = 'eagle_booking'
AND p.post_status = 'publish'
AND pm1.meta_value = %s
AND pm4.meta_value = 'pending'
", $target_date));
foreach ($bookings as $booking) {
// Check if payment request already sent
$existing_request = $wpdb->get_var($wpdb->prepare("
SELECT id FROM {$wpdb->prefix}eb_payment_requests
WHERE booking_id = %d AND status != 'failed'
", $booking->ID));
if ($existing_request) {
continue; // Skip if already sent
}
// Calculate amount
$amount = ($booking->total_price * $percentage) / 100;
// Create payment request
$description = sprintf(
__('Payment for your stay at Hotel Raxa (Booking #%d) - %d%% of total amount', 'informatiq-eb-redsys'),
$booking->ID,
$percentage
);
$request_id = $this->create_payment_request(
$booking->ID,
$booking->customer_email,
$amount,
$description,
72 // 3 days expiry
);
if ($request_id) {
$this->send_payment_request_email($request_id);
}
}
}
/**
* Add payment request metabox to booking edit page
*/
public function add_booking_metabox() {
add_meta_box(
'eb-payment-requests',
__('Payment Requests', 'informatiq-eb-redsys'),
array($this, 'booking_metabox_callback'),
'eagle_booking',
'side',
'default'
);
}
/**
* Booking metabox callback
*/
public function booking_metabox_callback($post) {
global $wpdb;
$table_name = $wpdb->prefix . 'eb_payment_requests';
$requests = $wpdb->get_results($wpdb->prepare(
"SELECT * FROM {$table_name} WHERE booking_id = %d ORDER BY created_at DESC",
$post->ID
));
?>
<div class="eb-payment-requests-metabox">
<?php if (empty($requests)): ?>
<p><?php _e('No payment requests sent for this booking.', 'informatiq-eb-redsys'); ?></p>
<?php else: ?>
<?php foreach ($requests as $request): ?>
<div class="payment-request-item" style="border: 1px solid #ddd; padding: 10px; margin: 10px 0; border-radius: 4px;">
<p><strong><?php _e('Amount:', 'informatiq-eb-redsys'); ?></strong> €<?php echo number_format($request->amount, 2); ?></p>
<p><strong><?php _e('Status:', 'informatiq-eb-redsys'); ?></strong>
<span class="payment-request-status status-<?php echo esc_attr($request->status); ?>">
<?php echo ucfirst($request->status); ?>
</span>
</p>
<p><strong><?php _e('Created:', 'informatiq-eb-redsys'); ?></strong> <?php echo date_i18n(get_option('datetime_format'), strtotime($request->created_at)); ?></p>
<p><strong><?php _e('Expires:', 'informatiq-eb-redsys'); ?></strong> <?php echo date_i18n(get_option('datetime_format'), strtotime($request->expires_at)); ?></p>
<?php if ($request->status !== 'paid'): ?>
<button class="button button-small copy-payment-link" data-link="<?php echo esc_attr($request->payment_link); ?>">
<?php _e('Copy Payment Link', 'informatiq-eb-redsys'); ?>
</button>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php endif; ?>
<hr>
<button type="button" class="button" id="send-new-payment-request" data-booking-id="<?php echo $post->ID; ?>">
<?php _e('Send New Payment Request', 'informatiq-eb-redsys'); ?>
</button>
</div>
<?php
}
/**
* Enqueue admin scripts
*/
public function enqueue_admin_scripts($hook) {
if (strpos($hook, 'eb-payment-requests') !== false || $hook === 'post.php') {
wp_enqueue_script(
'eb-payment-requests-admin',
plugin_dir_url(__FILE__) . '../assets/js/payment-requests-admin.js',
array('jquery'),
'1.0.0',
true
);
wp_localize_script('eb-payment-requests-admin', 'ebPaymentRequests', array(
'ajaxurl' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('payment_request_nonce'),
'strings' => array(
'confirmSend' => __('Are you sure you want to send this payment request?', 'informatiq-eb-redsys'),
'linkCopied' => __('Payment link copied to clipboard!', 'informatiq-eb-redsys'),
'copyFailed' => __('Failed to copy link. Please copy manually.', 'informatiq-eb-redsys')
)
));
}
}
}
// Initialize the class
new EB_Redsys_Payment_Requests();