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() {
?>
display_preauth_statistics(); ?>
display_active_preauthorizations(); ?>
display_preauth_history(); ?>
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)
");
?>
€active_amount ?: 0, 2); ?>
€captured_amount ?: 0, 2); ?>
prefix . 'eb_preauthorizations';
$preauths = $wpdb->get_results("
SELECT * FROM {$table_name}
WHERE status = 'active'
ORDER BY created_at DESC
");
if (empty($preauths)) {
echo '' . __('No active preauthorizations found.', 'informatiq-eb-redsys') . '
';
return;
}
?>
#booking_id; ?>
customer_email); ?>
€amount, 2); ?>
auth_code); ?>
expires_at);
$now = time();
$days_left = ceil(($expires - $now) / (24 * 60 * 60));
if ($days_left <= 0) {
echo '' . __('Expired', 'informatiq-eb-redsys') . ' ';
} else {
echo date_i18n(get_option('date_format'), $expires);
echo '' . sprintf(_n('%d day left', '%d days left', $days_left, 'informatiq-eb-redsys'), $days_left) . ' ';
}
?>
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 '' . __('No preauthorization history found.', 'informatiq-eb-redsys') . '
';
return;
}
?>
#booking_id; ?>
customer_email); ?>
€amount, 2); ?>
status); ?>
created_at)); ?>
updated_at ? date_i18n(get_option('datetime_format'), strtotime($preauth->updated_at)) : '-'; ?>
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 '' . __('No preauthorization found for this booking.', 'informatiq-eb-redsys') . '
';
return;
}
?>
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();