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) "); ?>

total_preauths ?: 0; ?>

active_preauths ?: 0; ?>

€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; } ?>

€amount, 2); ?>

status); ?>

auth_code); ?>

created_at)); ?>

status === 'active'): ?>

expires_at)); ?>

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();