🏨 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>
850 lines
37 KiB
PHP
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();
|