🏨 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>
820 lines
31 KiB
PHP
820 lines
31 KiB
PHP
<?php
|
|
/**
|
|
* Redsys Preauthorization Extension
|
|
*
|
|
* Adds preauthorization functionality to Redsys gateway
|
|
* Allows holding funds without immediate capture
|
|
*
|
|
* 🤖 Generated with Claude Code (https://claude.ai/code)
|
|
*/
|
|
|
|
// Exit if accessed directly
|
|
if (!defined('ABSPATH')) {
|
|
exit;
|
|
}
|
|
|
|
class EB_Redsys_Preauthorization {
|
|
|
|
/**
|
|
* Constructor
|
|
*/
|
|
public function __construct() {
|
|
add_action('init', array($this, 'init'));
|
|
}
|
|
|
|
/**
|
|
* Initialize the class
|
|
*/
|
|
public function init() {
|
|
// Add preauthorization option to payment settings
|
|
add_filter('eb_redsys_payment_settings', array($this, 'add_preauth_settings'));
|
|
|
|
// Modify payment processing for preauthorization
|
|
add_filter('eb_redsys_payment_parameters', array($this, 'modify_payment_parameters'), 10, 2);
|
|
|
|
// Add preauthorization management to admin
|
|
add_action('admin_menu', array($this, 'add_admin_menu'));
|
|
|
|
// Add preauthorization metabox to bookings
|
|
add_action('add_meta_boxes', array($this, 'add_preauth_metabox'));
|
|
|
|
// AJAX handlers for preauthorization actions
|
|
add_action('wp_ajax_capture_preauth', array($this, 'capture_preauthorization'));
|
|
add_action('wp_ajax_cancel_preauth', array($this, 'cancel_preauthorization'));
|
|
|
|
// Handle preauthorization notifications
|
|
add_action('eb_redsys_payment_notification', array($this, 'handle_preauth_notification'), 10, 2);
|
|
|
|
// Create database table for preauthorizations
|
|
register_activation_hook(EB_REDSYS_PLUGIN_FILE, array($this, 'create_preauth_table'));
|
|
|
|
// Enqueue admin scripts
|
|
add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_scripts'));
|
|
}
|
|
|
|
/**
|
|
* Add preauthorization settings to payment gateway
|
|
*/
|
|
public function add_preauth_settings($settings) {
|
|
$preauth_settings = array(
|
|
'preauth_enabled' => array(
|
|
'title' => __('Enable Preauthorization', 'informatiq-eb-redsys'),
|
|
'type' => 'checkbox',
|
|
'description' => __('Enable preauthorization mode for reservations (hold funds without immediate capture)', 'informatiq-eb-redsys'),
|
|
'default' => 'no'
|
|
),
|
|
'preauth_amount_type' => array(
|
|
'title' => __('Preauthorization Amount', 'informatiq-eb-redsys'),
|
|
'type' => 'select',
|
|
'description' => __('Choose how much to preauthorize', 'informatiq-eb-redsys'),
|
|
'default' => 'full',
|
|
'options' => array(
|
|
'full' => __('Full booking amount', 'informatiq-eb-redsys'),
|
|
'deposit' => __('Deposit amount only', 'informatiq-eb-redsys'),
|
|
'fixed' => __('Fixed amount', 'informatiq-eb-redsys')
|
|
)
|
|
),
|
|
'preauth_fixed_amount' => array(
|
|
'title' => __('Fixed Preauthorization Amount (€)', 'informatiq-eb-redsys'),
|
|
'type' => 'number',
|
|
'description' => __('Fixed amount to preauthorize (only used if "Fixed amount" is selected above)', 'informatiq-eb-redsys'),
|
|
'default' => '50',
|
|
'custom_attributes' => array(
|
|
'step' => '0.01',
|
|
'min' => '0.01'
|
|
)
|
|
),
|
|
'preauth_auto_capture' => array(
|
|
'title' => __('Auto-capture Preauthorizations', 'informatiq-eb-redsys'),
|
|
'type' => 'checkbox',
|
|
'description' => __('Automatically capture preauthorizations 24 hours before check-in', 'informatiq-eb-redsys'),
|
|
'default' => 'yes'
|
|
),
|
|
'preauth_expiry_days' => array(
|
|
'title' => __('Preauthorization Expiry (Days)', 'informatiq-eb-redsys'),
|
|
'type' => 'number',
|
|
'description' => __('Number of days before preauthorization expires (max 7 days for most cards)', 'informatiq-eb-redsys'),
|
|
'default' => '7',
|
|
'custom_attributes' => array(
|
|
'min' => '1',
|
|
'max' => '7'
|
|
)
|
|
)
|
|
);
|
|
|
|
return array_merge($settings, $preauth_settings);
|
|
}
|
|
|
|
/**
|
|
* Modify payment parameters for preauthorization
|
|
*/
|
|
public function modify_payment_parameters($parameters, $booking_data) {
|
|
if (get_option('eb_redsys_preauth_enabled') !== 'yes') {
|
|
return $parameters;
|
|
}
|
|
|
|
// Change transaction type to preauthorization
|
|
$parameters['DS_MERCHANT_TRANSACTIONTYPE'] = '1'; // Preauthorization
|
|
|
|
// Calculate preauthorization amount
|
|
$preauth_amount = $this->calculate_preauth_amount($booking_data);
|
|
$parameters['DS_MERCHANT_AMOUNT'] = $preauth_amount * 100; // Convert to cents
|
|
|
|
// Add preauthorization identifier
|
|
$parameters['DS_MERCHANT_PRODUCTDESCRIPTION'] = 'Preauth: ' . $parameters['DS_MERCHANT_PRODUCTDESCRIPTION'];
|
|
|
|
return $parameters;
|
|
}
|
|
|
|
/**
|
|
* Calculate preauthorization amount
|
|
*/
|
|
private function calculate_preauth_amount($booking_data) {
|
|
$amount_type = get_option('eb_redsys_preauth_amount_type', 'full');
|
|
$total_amount = $booking_data['total_price'];
|
|
|
|
switch ($amount_type) {
|
|
case 'full':
|
|
return $total_amount;
|
|
|
|
case 'deposit':
|
|
$deposit_percentage = get_option('eb_deposit_percentage', 30);
|
|
return ($total_amount * $deposit_percentage) / 100;
|
|
|
|
case 'fixed':
|
|
return floatval(get_option('eb_redsys_preauth_fixed_amount', 50));
|
|
|
|
default:
|
|
return $total_amount;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Add admin menu
|
|
*/
|
|
public function add_admin_menu() {
|
|
add_submenu_page(
|
|
'eb_bookings',
|
|
__('Preauthorizations', 'informatiq-eb-redsys'),
|
|
__('Preauthorizations', 'informatiq-eb-redsys'),
|
|
'manage_options',
|
|
'eb-preauthorizations',
|
|
array($this, 'admin_page')
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Render admin page
|
|
*/
|
|
public function admin_page() {
|
|
?>
|
|
<div class="wrap">
|
|
<h1><?php _e('Preauthorizations Management', 'informatiq-eb-redsys'); ?></h1>
|
|
|
|
<div class="eb-preauth-admin">
|
|
<!-- Preauthorization Statistics -->
|
|
<div class="postbox">
|
|
<h2 class="hndle"><?php _e('Preauthorization Statistics', 'informatiq-eb-redsys'); ?></h2>
|
|
<div class="inside">
|
|
<?php $this->display_preauth_statistics(); ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Active Preauthorizations -->
|
|
<div class="postbox">
|
|
<h2 class="hndle"><?php _e('Active Preauthorizations', 'informatiq-eb-redsys'); ?></h2>
|
|
<div class="inside">
|
|
<?php $this->display_active_preauthorizations(); ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Preauthorization History -->
|
|
<div class="postbox">
|
|
<h2 class="hndle"><?php _e('Preauthorization History', 'informatiq-eb-redsys'); ?></h2>
|
|
<div class="inside">
|
|
<?php $this->display_preauth_history(); ?>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<style>
|
|
.eb-preauth-admin .postbox {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.preauth-stats {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
|
gap: 20px;
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.preauth-stat-box {
|
|
background: #f8f9fa;
|
|
padding: 20px;
|
|
text-align: center;
|
|
border-radius: 5px;
|
|
border: 1px solid #e1e5e9;
|
|
}
|
|
|
|
.preauth-stat-box h3 {
|
|
margin: 0 0 10px;
|
|
font-size: 2em;
|
|
color: #2271b1;
|
|
}
|
|
|
|
.preauth-status {
|
|
padding: 3px 8px;
|
|
border-radius: 3px;
|
|
font-size: 11px;
|
|
font-weight: bold;
|
|
text-transform: uppercase;
|
|
}
|
|
|
|
.status-active {
|
|
background: #d4edda;
|
|
color: #155724;
|
|
}
|
|
|
|
.status-captured {
|
|
background: #d1ecf1;
|
|
color: #0c5460;
|
|
}
|
|
|
|
.status-cancelled {
|
|
background: #f8d7da;
|
|
color: #721c24;
|
|
}
|
|
|
|
.status-expired {
|
|
background: #e2e3e5;
|
|
color: #383d41;
|
|
}
|
|
|
|
.preauth-actions {
|
|
display: flex;
|
|
gap: 5px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
.preauth-actions .button {
|
|
font-size: 11px;
|
|
padding: 2px 8px;
|
|
height: auto;
|
|
}
|
|
</style>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Display preauthorization statistics
|
|
*/
|
|
private function display_preauth_statistics() {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'eb_preauthorizations';
|
|
|
|
$stats = $wpdb->get_row("
|
|
SELECT
|
|
COUNT(*) as total_preauths,
|
|
SUM(CASE WHEN status = 'active' THEN 1 ELSE 0 END) as active_preauths,
|
|
SUM(CASE WHEN status = 'captured' THEN 1 ELSE 0 END) as captured_preauths,
|
|
SUM(CASE WHEN status = 'cancelled' THEN 1 ELSE 0 END) as cancelled_preauths,
|
|
SUM(CASE WHEN status = 'active' THEN amount ELSE 0 END) as active_amount,
|
|
SUM(CASE WHEN status = 'captured' THEN amount ELSE 0 END) as captured_amount
|
|
FROM {$table_name}
|
|
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 30 DAY)
|
|
");
|
|
|
|
?>
|
|
<div class="preauth-stats">
|
|
<div class="preauth-stat-box">
|
|
<h3><?php echo $stats->total_preauths ?: 0; ?></h3>
|
|
<p><?php _e('Total Preauthorizations (30 days)', 'informatiq-eb-redsys'); ?></p>
|
|
</div>
|
|
<div class="preauth-stat-box">
|
|
<h3><?php echo $stats->active_preauths ?: 0; ?></h3>
|
|
<p><?php _e('Active Preauthorizations', 'informatiq-eb-redsys'); ?></p>
|
|
</div>
|
|
<div class="preauth-stat-box">
|
|
<h3>€<?php echo number_format($stats->active_amount ?: 0, 2); ?></h3>
|
|
<p><?php _e('Active Amount', 'informatiq-eb-redsys'); ?></p>
|
|
</div>
|
|
<div class="preauth-stat-box">
|
|
<h3>€<?php echo number_format($stats->captured_amount ?: 0, 2); ?></h3>
|
|
<p><?php _e('Captured Amount (30 days)', 'informatiq-eb-redsys'); ?></p>
|
|
</div>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Display active preauthorizations
|
|
*/
|
|
private function display_active_preauthorizations() {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'eb_preauthorizations';
|
|
$preauths = $wpdb->get_results("
|
|
SELECT * FROM {$table_name}
|
|
WHERE status = 'active'
|
|
ORDER BY created_at DESC
|
|
");
|
|
|
|
if (empty($preauths)) {
|
|
echo '<p>' . __('No active preauthorizations found.', 'informatiq-eb-redsys') . '</p>';
|
|
return;
|
|
}
|
|
|
|
?>
|
|
<table class="wp-list-table widefat fixed striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php _e('Booking ID', 'informatiq-eb-redsys'); ?></th>
|
|
<th><?php _e('Customer', 'informatiq-eb-redsys'); ?></th>
|
|
<th><?php _e('Amount', 'informatiq-eb-redsys'); ?></th>
|
|
<th><?php _e('Auth Code', '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 ($preauths as $preauth): ?>
|
|
<tr>
|
|
<td>
|
|
<a href="<?php echo admin_url('post.php?post=' . $preauth->booking_id . '&action=edit'); ?>">
|
|
#<?php echo $preauth->booking_id; ?>
|
|
</a>
|
|
</td>
|
|
<td><?php echo esc_html($preauth->customer_email); ?></td>
|
|
<td>€<?php echo number_format($preauth->amount, 2); ?></td>
|
|
<td><code><?php echo esc_html($preauth->auth_code); ?></code></td>
|
|
<td>
|
|
<?php
|
|
$expires = strtotime($preauth->expires_at);
|
|
$now = time();
|
|
$days_left = ceil(($expires - $now) / (24 * 60 * 60));
|
|
|
|
if ($days_left <= 0) {
|
|
echo '<span style="color: #d63638;">' . __('Expired', 'informatiq-eb-redsys') . '</span>';
|
|
} else {
|
|
echo date_i18n(get_option('date_format'), $expires);
|
|
echo '<br><small>' . sprintf(_n('%d day left', '%d days left', $days_left, 'informatiq-eb-redsys'), $days_left) . '</small>';
|
|
}
|
|
?>
|
|
</td>
|
|
<td>
|
|
<div class="preauth-actions">
|
|
<button class="button button-primary button-small capture-preauth"
|
|
data-preauth-id="<?php echo $preauth->id; ?>"
|
|
data-booking-id="<?php echo $preauth->booking_id; ?>"
|
|
data-amount="<?php echo $preauth->amount; ?>">
|
|
<?php _e('Capture', 'informatiq-eb-redsys'); ?>
|
|
</button>
|
|
<button class="button button-secondary button-small cancel-preauth"
|
|
data-preauth-id="<?php echo $preauth->id; ?>"
|
|
data-booking-id="<?php echo $preauth->booking_id; ?>">
|
|
<?php _e('Cancel', 'informatiq-eb-redsys'); ?>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Display preauthorization history
|
|
*/
|
|
private function display_preauth_history() {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'eb_preauthorizations';
|
|
$preauths = $wpdb->get_results("
|
|
SELECT * FROM {$table_name}
|
|
WHERE status != 'active'
|
|
ORDER BY created_at DESC
|
|
LIMIT 20
|
|
");
|
|
|
|
if (empty($preauths)) {
|
|
echo '<p>' . __('No preauthorization history found.', 'informatiq-eb-redsys') . '</p>';
|
|
return;
|
|
}
|
|
|
|
?>
|
|
<table class="wp-list-table widefat fixed striped">
|
|
<thead>
|
|
<tr>
|
|
<th><?php _e('Booking ID', '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('Updated', 'informatiq-eb-redsys'); ?></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($preauths as $preauth): ?>
|
|
<tr>
|
|
<td>
|
|
<a href="<?php echo admin_url('post.php?post=' . $preauth->booking_id . '&action=edit'); ?>">
|
|
#<?php echo $preauth->booking_id; ?>
|
|
</a>
|
|
</td>
|
|
<td><?php echo esc_html($preauth->customer_email); ?></td>
|
|
<td>€<?php echo number_format($preauth->amount, 2); ?></td>
|
|
<td>
|
|
<span class="preauth-status status-<?php echo esc_attr($preauth->status); ?>">
|
|
<?php echo ucfirst($preauth->status); ?>
|
|
</span>
|
|
</td>
|
|
<td><?php echo date_i18n(get_option('datetime_format'), strtotime($preauth->created_at)); ?></td>
|
|
<td><?php echo $preauth->updated_at ? date_i18n(get_option('datetime_format'), strtotime($preauth->updated_at)) : '-'; ?></td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Capture preauthorization (AJAX handler)
|
|
*/
|
|
public function capture_preauthorization() {
|
|
check_ajax_referer('preauth_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_die(__('Insufficient permissions', 'informatiq-eb-redsys'));
|
|
}
|
|
|
|
$preauth_id = intval($_POST['preauth_id']);
|
|
$booking_id = intval($_POST['booking_id']);
|
|
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'eb_preauthorizations';
|
|
|
|
$preauth = $wpdb->get_row($wpdb->prepare(
|
|
"SELECT * FROM {$table_name} WHERE id = %d AND status = 'active'",
|
|
$preauth_id
|
|
));
|
|
|
|
if (!$preauth) {
|
|
wp_send_json_error(__('Preauthorization not found or not active', 'informatiq-eb-redsys'));
|
|
}
|
|
|
|
// Process capture with Redsys
|
|
$result = $this->process_capture($preauth);
|
|
|
|
if ($result['success']) {
|
|
wp_send_json_success(__('Preauthorization captured successfully', 'informatiq-eb-redsys'));
|
|
} else {
|
|
wp_send_json_error($result['error']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Cancel preauthorization (AJAX handler)
|
|
*/
|
|
public function cancel_preauthorization() {
|
|
check_ajax_referer('preauth_nonce', 'nonce');
|
|
|
|
if (!current_user_can('manage_options')) {
|
|
wp_die(__('Insufficient permissions', 'informatiq-eb-redsys'));
|
|
}
|
|
|
|
$preauth_id = intval($_POST['preauth_id']);
|
|
$booking_id = intval($_POST['booking_id']);
|
|
|
|
global $wpdb;
|
|
$table_name = $wpdb->prefix . 'eb_preauthorizations';
|
|
|
|
$preauth = $wpdb->get_row($wpdb->prepare(
|
|
"SELECT * FROM {$table_name} WHERE id = %d AND status = 'active'",
|
|
$preauth_id
|
|
));
|
|
|
|
if (!$preauth) {
|
|
wp_send_json_error(__('Preauthorization not found or not active', 'informatiq-eb-redsys'));
|
|
}
|
|
|
|
// Process cancellation with Redsys
|
|
$result = $this->process_cancellation($preauth);
|
|
|
|
if ($result['success']) {
|
|
wp_send_json_success(__('Preauthorization cancelled successfully', 'informatiq-eb-redsys'));
|
|
} else {
|
|
wp_send_json_error($result['error']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process capture with Redsys
|
|
*/
|
|
private function process_capture($preauth) {
|
|
// Include Redsys API
|
|
require_once dirname(__FILE__) . '/../api/class-redsys-api.php';
|
|
|
|
$redsys = new Redsys_API();
|
|
|
|
// Set capture parameters
|
|
$redsys->setParameter('DS_MERCHANT_AMOUNT', $preauth->amount * 100);
|
|
$redsys->setParameter('DS_MERCHANT_ORDER', $preauth->order_number);
|
|
$redsys->setParameter('DS_MERCHANT_MERCHANTCODE', get_option('eb_redsys_merchant_code'));
|
|
$redsys->setParameter('DS_MERCHANT_CURRENCY', '978');
|
|
$redsys->setParameter('DS_MERCHANT_TRANSACTIONTYPE', '2'); // Capture
|
|
$redsys->setParameter('DS_MERCHANT_TERMINAL', get_option('eb_redsys_terminal'));
|
|
$redsys->setParameter('DS_MERCHANT_AUTHCODE', $preauth->auth_code);
|
|
|
|
// Generate signature
|
|
$signature = $redsys->createMerchantSignature(get_option('eb_redsys_encryption_key'));
|
|
|
|
// Send request to Redsys
|
|
$result = $this->send_redsys_request($redsys, $signature);
|
|
|
|
if ($result['success']) {
|
|
// Update preauthorization status
|
|
global $wpdb;
|
|
$wpdb->update(
|
|
$wpdb->prefix . 'eb_preauthorizations',
|
|
array(
|
|
'status' => 'captured',
|
|
'captured_at' => current_time('mysql'),
|
|
'updated_at' => current_time('mysql')
|
|
),
|
|
array('id' => $preauth->id),
|
|
array('%s', '%s', '%s'),
|
|
array('%d')
|
|
);
|
|
|
|
// Update booking payment status
|
|
update_post_meta($preauth->booking_id, '_eb_payment_status', 'completed');
|
|
|
|
return array('success' => true);
|
|
} else {
|
|
return array('success' => false, 'error' => $result['error']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Process cancellation with Redsys
|
|
*/
|
|
private function process_cancellation($preauth) {
|
|
// Include Redsys API
|
|
require_once dirname(__FILE__) . '/../api/class-redsys-api.php';
|
|
|
|
$redsys = new Redsys_API();
|
|
|
|
// Set cancellation parameters
|
|
$redsys->setParameter('DS_MERCHANT_AMOUNT', $preauth->amount * 100);
|
|
$redsys->setParameter('DS_MERCHANT_ORDER', $preauth->order_number);
|
|
$redsys->setParameter('DS_MERCHANT_MERCHANTCODE', get_option('eb_redsys_merchant_code'));
|
|
$redsys->setParameter('DS_MERCHANT_CURRENCY', '978');
|
|
$redsys->setParameter('DS_MERCHANT_TRANSACTIONTYPE', '9'); // Cancellation
|
|
$redsys->setParameter('DS_MERCHANT_TERMINAL', get_option('eb_redsys_terminal'));
|
|
$redsys->setParameter('DS_MERCHANT_AUTHCODE', $preauth->auth_code);
|
|
|
|
// Generate signature
|
|
$signature = $redsys->createMerchantSignature(get_option('eb_redsys_encryption_key'));
|
|
|
|
// Send request to Redsys
|
|
$result = $this->send_redsys_request($redsys, $signature);
|
|
|
|
if ($result['success']) {
|
|
// Update preauthorization status
|
|
global $wpdb;
|
|
$wpdb->update(
|
|
$wpdb->prefix . 'eb_preauthorizations',
|
|
array(
|
|
'status' => 'cancelled',
|
|
'cancelled_at' => current_time('mysql'),
|
|
'updated_at' => current_time('mysql')
|
|
),
|
|
array('id' => $preauth->id),
|
|
array('%s', '%s', '%s'),
|
|
array('%d')
|
|
);
|
|
|
|
// Update booking payment status
|
|
update_post_meta($preauth->booking_id, '_eb_payment_status', 'cancelled');
|
|
|
|
return array('success' => true);
|
|
} else {
|
|
return array('success' => false, 'error' => $result['error']);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send request to Redsys
|
|
*/
|
|
private function send_redsys_request($redsys, $signature) {
|
|
$test_mode = get_option('eb_redsys_test_mode') === 'yes';
|
|
$url = $test_mode ?
|
|
'https://sis-t.redsys.es:25443/sis/operaciones' :
|
|
'https://sis.redsys.es/sis/operaciones';
|
|
|
|
$post_data = array(
|
|
'Ds_SignatureVersion' => 'HMAC_SHA256_V1',
|
|
'Ds_MerchantParameters' => $redsys->createMerchantParameters(),
|
|
'Ds_Signature' => $signature
|
|
);
|
|
|
|
$response = wp_remote_post($url, array(
|
|
'timeout' => 45,
|
|
'body' => $post_data
|
|
));
|
|
|
|
if (is_wp_error($response)) {
|
|
return array('success' => false, 'error' => $response->get_error_message());
|
|
}
|
|
|
|
$body = wp_remote_retrieve_body($response);
|
|
$xml = simplexml_load_string($body);
|
|
|
|
if ($xml && isset($xml->OPERACION->Ds_Response)) {
|
|
$response_code = (string) $xml->OPERACION->Ds_Response;
|
|
|
|
if ($response_code >= 0 && $response_code <= 99) {
|
|
return array('success' => true, 'response_code' => $response_code);
|
|
} else {
|
|
return array('success' => false, 'error' => 'Response code: ' . $response_code);
|
|
}
|
|
}
|
|
|
|
return array('success' => false, 'error' => 'Invalid response from Redsys');
|
|
}
|
|
|
|
/**
|
|
* Handle preauthorization notification
|
|
*/
|
|
public function handle_preauth_notification($notification_data, $booking_id) {
|
|
if ($notification_data['Ds_TransactionType'] !== '1') {
|
|
return; // Not a preauthorization
|
|
}
|
|
|
|
$response_code = intval($notification_data['Ds_Response']);
|
|
|
|
if ($response_code >= 0 && $response_code <= 99) {
|
|
// Successful preauthorization
|
|
$this->save_preauthorization_data($booking_id, $notification_data);
|
|
} else {
|
|
// Failed preauthorization
|
|
update_post_meta($booking_id, '_eb_payment_status', 'failed');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Save preauthorization data
|
|
*/
|
|
private function save_preauthorization_data($booking_id, $notification_data) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'eb_preauthorizations';
|
|
|
|
$expiry_days = intval(get_option('eb_redsys_preauth_expiry_days', 7));
|
|
$expires_at = date('Y-m-d H:i:s', strtotime("+{$expiry_days} days"));
|
|
|
|
$wpdb->insert(
|
|
$table_name,
|
|
array(
|
|
'booking_id' => $booking_id,
|
|
'customer_email' => get_post_meta($booking_id, '_eb_customer_email', true),
|
|
'amount' => $notification_data['Ds_Amount'] / 100,
|
|
'order_number' => $notification_data['Ds_Order'],
|
|
'auth_code' => $notification_data['Ds_AuthorisationCode'],
|
|
'status' => 'active',
|
|
'created_at' => current_time('mysql'),
|
|
'expires_at' => $expires_at
|
|
),
|
|
array('%d', '%s', '%f', '%s', '%s', '%s', '%s', '%s')
|
|
);
|
|
|
|
// Update booking payment status
|
|
update_post_meta($booking_id, '_eb_payment_status', 'preauthorized');
|
|
}
|
|
|
|
/**
|
|
* Add preauthorization metabox to booking
|
|
*/
|
|
public function add_preauth_metabox() {
|
|
add_meta_box(
|
|
'eb-preauthorization',
|
|
__('Preauthorization', 'informatiq-eb-redsys'),
|
|
array($this, 'preauth_metabox_callback'),
|
|
'eagle_booking',
|
|
'side',
|
|
'default'
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Preauthorization metabox callback
|
|
*/
|
|
public function preauth_metabox_callback($post) {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'eb_preauthorizations';
|
|
$preauth = $wpdb->get_row($wpdb->prepare(
|
|
"SELECT * FROM {$table_name} WHERE booking_id = %d ORDER BY created_at DESC LIMIT 1",
|
|
$post->ID
|
|
));
|
|
|
|
if (!$preauth) {
|
|
echo '<p>' . __('No preauthorization found for this booking.', 'informatiq-eb-redsys') . '</p>';
|
|
return;
|
|
}
|
|
|
|
?>
|
|
<div class="eb-preauth-metabox">
|
|
<p><strong><?php _e('Amount:', 'informatiq-eb-redsys'); ?></strong> €<?php echo number_format($preauth->amount, 2); ?></p>
|
|
<p><strong><?php _e('Status:', 'informatiq-eb-redsys'); ?></strong>
|
|
<span class="preauth-status status-<?php echo esc_attr($preauth->status); ?>">
|
|
<?php echo ucfirst($preauth->status); ?>
|
|
</span>
|
|
</p>
|
|
<p><strong><?php _e('Auth Code:', 'informatiq-eb-redsys'); ?></strong> <code><?php echo esc_html($preauth->auth_code); ?></code></p>
|
|
<p><strong><?php _e('Created:', 'informatiq-eb-redsys'); ?></strong> <?php echo date_i18n(get_option('datetime_format'), strtotime($preauth->created_at)); ?></p>
|
|
|
|
<?php if ($preauth->status === 'active'): ?>
|
|
<p><strong><?php _e('Expires:', 'informatiq-eb-redsys'); ?></strong> <?php echo date_i18n(get_option('datetime_format'), strtotime($preauth->expires_at)); ?></p>
|
|
|
|
<div class="preauth-actions">
|
|
<button class="button button-primary capture-preauth"
|
|
data-preauth-id="<?php echo $preauth->id; ?>"
|
|
data-booking-id="<?php echo $post->ID; ?>">
|
|
<?php _e('Capture Payment', 'informatiq-eb-redsys'); ?>
|
|
</button>
|
|
<button class="button button-secondary cancel-preauth"
|
|
data-preauth-id="<?php echo $preauth->id; ?>"
|
|
data-booking-id="<?php echo $post->ID; ?>">
|
|
<?php _e('Cancel Preauth', 'informatiq-eb-redsys'); ?>
|
|
</button>
|
|
</div>
|
|
<?php endif; ?>
|
|
</div>
|
|
<?php
|
|
}
|
|
|
|
/**
|
|
* Create preauthorization database table
|
|
*/
|
|
public function create_preauth_table() {
|
|
global $wpdb;
|
|
|
|
$table_name = $wpdb->prefix . 'eb_preauthorizations';
|
|
|
|
$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,
|
|
order_number varchar(20) NOT NULL,
|
|
auth_code varchar(20) NOT NULL,
|
|
status varchar(20) DEFAULT 'active',
|
|
created_at datetime NOT NULL,
|
|
expires_at datetime NOT NULL,
|
|
captured_at datetime,
|
|
cancelled_at datetime,
|
|
updated_at datetime,
|
|
PRIMARY KEY (id),
|
|
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);
|
|
}
|
|
|
|
/**
|
|
* Enqueue admin scripts
|
|
*/
|
|
public function enqueue_admin_scripts($hook) {
|
|
if (strpos($hook, 'eb-preauthorizations') !== false || $hook === 'post.php') {
|
|
wp_enqueue_script(
|
|
'eb-preauth-admin',
|
|
plugin_dir_url(__FILE__) . '../assets/js/preauth-admin.js',
|
|
array('jquery'),
|
|
'1.0.0',
|
|
true
|
|
);
|
|
|
|
wp_localize_script('eb-preauth-admin', 'ebPreauth', array(
|
|
'ajaxurl' => admin_url('admin-ajax.php'),
|
|
'nonce' => wp_create_nonce('preauth_nonce'),
|
|
'strings' => array(
|
|
'confirmCapture' => __('Are you sure you want to capture this preauthorization?', 'informatiq-eb-redsys'),
|
|
'confirmCancel' => __('Are you sure you want to cancel this preauthorization?', 'informatiq-eb-redsys')
|
|
)
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Initialize the class
|
|
new EB_Redsys_Preauthorization();
|