Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/wp-travel/core/ajax/cart.php
+++ b/wp-travel/core/ajax/cart.php
@@ -71,6 +71,22 @@
$response = WP_Travel_Helpers_Cart::add_to_cart( $post_data );
+ if( apply_filters( 'wp_travel_enable_cart_logs', false ) == true ){
+
+ $cookie = wp_unslash($_COOKIE['wp_travel_session']);
+ $parts = explode('||', $cookie);
+
+ wt_cart_log(
+ 'ITEM ADDED TO CART FOR SESSION - ' . $parts[0],
+
+ array(
+ 'session_id' => ! empty($parts[0])
+ ? sanitize_text_field($parts[0])
+ : ''
+ )
+ );
+ }
+
if( $settings['enable_woo_checkout'] == 'yes' ){
global $woocommerce;
$woocommerce->cart->empty_cart();
--- a/wp-travel/inc/booking-functions.php
+++ b/wp-travel/inc/booking-functions.php
@@ -7,7 +7,6 @@
function wptravel_book_now() {
-
// This condition is added to fix Paypal unpaid bookings
if( isset( $_POST['wp_travel_payment_gateway'] ) && $_POST['wp_travel_payment_gateway'] == 'paypal' ){
$booking_post_type = 'pending-booking';
@@ -521,8 +520,35 @@
$affiliate = apply_filters( 'wp_travel_all_booking_data_list_for_slicewp', $booking_id, $user_id );
+ if( apply_filters( 'wp_travel_enable_cart_logs', false ) == true ){
+ $cookie = wp_unslash($_COOKIE['wp_travel_session']);
+ $parts = explode('||', $cookie);
+ wt_cart_log(
+ 'BOOKING SUCCESSFUL',
+ array(
+ 'session_id' => ! empty($parts[0])
+ ? sanitize_text_field($parts[0])
+ : '',
+ 'booking_id' => $booking_id,
+ )
+ );
+ }
+
$wt_cart->clear();
+ if( apply_filters( 'wp_travel_enable_cart_logs', false ) == true ){
+ $cookie = wp_unslash($_COOKIE['wp_travel_session']);
+ $parts = explode('||', $cookie);
+ wt_cart_log(
+ 'CART CLEARED AFTER SUCCESSFUL BOOKING',
+ array(
+ 'session_id' => ! empty($parts[0])
+ ? sanitize_text_field($parts[0])
+ : '',
+ )
+ );
+ }
+
if( get_option( 'wptravel_reserve_date' ) == 'yes' ){
$reserved_booking_dates = array();
--- a/wp-travel/inc/cart-logger.php
+++ b/wp-travel/inc/cart-logger.php
@@ -39,49 +39,49 @@
);
}
-function wt_cart_log( $message, $data = array() ) {
- $uri = $_SERVER['REQUEST_URI'] ?? '';
+function wt_cart_log( $message, $data = array(), $include_device_info = false ) {
- if ( strpos($uri, apply_filters( 'wp_travel_checkout_request_url', 'wp-travel-checkout' ) ) === false ) {
- return;
- }
-
-
$upload_dir = wp_upload_dir();
- $log_dir = $upload_dir['basedir'] . '/wp-travel-logs';
+ $log_dir = $upload_dir['basedir'] . '/wp-travel-logs';
if ( ! file_exists( $log_dir ) ) {
wp_mkdir_p( $log_dir );
}
- $user_id = get_current_user_id();
- $user_email = '';
+ $device_info = '';
- if ( $user_id ) {
- $user = get_userdata( $user_id );
- $user_email = $user ? $user->user_email : '';
- }
+ if ( $include_device_info ) {
- $device_info = array(
- 'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
- 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
- 'device_type' => wt_get_device_type(),
- 'browser' => wt_get_browser_details(),
- 'user_id' => $user_id,
- 'user_email' => $user_email,
- );
+ $user_id = get_current_user_id();
+ $user_email = '';
+
+ if ( $user_id ) {
+ $user = get_userdata( $user_id );
+ $user_email = $user ? $user->user_email : '';
+ }
+
+ $device_info = print_r(
+ array(
+ 'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
+ 'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
+ 'device_type' => wt_get_device_type(),
+ 'browser' => wt_get_browser_details(),
+ 'user_id' => $user_id,
+ 'user_email' => $user_email,
+ ),
+ true
+ );
+ }
$log_file = $log_dir . '/cart-' . date( 'Y-m-d' ) . '.log';
$log = sprintf(
- "[%s]nMESSAGE: %snDEVICE & USER INFO: %snDATA INFO: %snn",
+ "[%s]nMESSAGE: %sn%sDATA INFO: %snn",
current_time( 'mysql' ),
$message,
- print_r( $device_info, true ),
+ $include_device_info ? "DEVICE & USER INFO: {$device_info}n" : '',
print_r( $data, true )
);
file_put_contents( $log_file, $log, FILE_APPEND | LOCK_EX );
-
-
}
--- a/wp-travel/inc/cart/class-checkout.php
+++ b/wp-travel/inc/cart/class-checkout.php
@@ -36,17 +36,12 @@
return;
}
-
- if ( isset($_COOKIE['wp_travel_session']) ) {
-
- $cookie = wp_unslash($_COOKIE['wp_travel_session']);
- $parts = explode('||', $cookie);
-
- }
if( apply_filters( 'wp_travel_enable_cart_logs', false ) == true ){
+ $cookie = wp_unslash($_COOKIE['wp_travel_session']);
+ $parts = explode('||', $cookie);
wt_cart_log(
- 'GET ITEMS CALLED',
+ 'REACHED TO CHECKOUT PAGE',
array(
'session_id' => ! empty($parts[0])
? sanitize_text_field($parts[0])
--- a/wp-travel/inc/gateways/bank-deposit/bank-deposit.php
+++ b/wp-travel/inc/gateways/bank-deposit/bank-deposit.php
@@ -29,10 +29,35 @@
function wptravel_submit_bank_deposit_slip() {
+ if ( ! isset( $_POST['wp_travel_security'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wp_travel_security'] ) ), 'wp_travel_security_action' ) ) {
+ return;
+ }
+
+ $booking_id = absint( $_POST['wp_travel_booking_id'] ?? 0 );
+
+ $user_id = absint(
+ get_post_meta( $booking_id, 'wp_travel_customer_user_id', true )
+ );
+
+ $user = get_userdata( $user_id );
+
+
+ $booking_email = strtolower( trim( $user->user_email ) );
+ $payer_email = strtolower(
+ trim(
+ sanitize_email( wp_unslash( $_POST['payment_email'] ?? '' ) )
+ )
+ );
+
+ if ( empty( $payer_email ) || $booking_email !== $payer_email ) {
+ return;
+ }
+
+
if ( isset( $_POST['complete_partial_payment'] ) && isset( $_POST['wp_travel_payment_gateway'] ) && $_POST['wp_travel_payment_gateway'] == 'bank_deposit' ) {
- $payment_gateway = 'bank_deposit';
- $booking_id = sanitize_text_field( wp_unslash( $_POST['wp_travel_booking_id'] ) );
+
+ $payment_gateway = 'bank_deposit';
$payment_id = get_post_meta( $booking_id, 'wp_travel_payment_id', true );
@@ -46,17 +71,6 @@
if ( isset( $_POST['wp_travel_submit_slip'] ) ) {
- if ( ! isset( $_POST['booking_id'] ) ) {
- return;
- }
-
- if (
- ! isset( $_POST['wp_travel_security'] )
- || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['wp_travel_security'] ) ), 'wp_travel_security_action' )
- ) {
- return;
- }
-
$settings = wptravel_get_settings();
$allowed_files = apply_filters( 'wp_travel_bank_deposit_allowed_files', 'jpg, png, txt, pdf' );
@@ -98,7 +112,7 @@
if ( true === $upload_ok ) {
- $booking_id = absint( $_POST['booking_id'] );
+ $booking_id = absint( $_POST['wp_travel_booking_id'] );
$txn_id = isset( $_POST['wp_travel_bank_deposit_transaction_id'] ) ? sanitize_text_field( $_POST['wp_travel_bank_deposit_transaction_id'] ) : '';
$data = wptravel_booking_data( $booking_id );
@@ -343,10 +357,18 @@
$bank_deposit_fields = wptravel_get_bank_deposit_form_fields($details);
$bank_deposit_fields['booking_id'] = array(
'type' => 'hidden',
- 'name' => 'booking_id',
+ 'name' => 'wp_travel_booking_id',
'id' => 'wp-travel-booking_id',
'default' => $booking_id,
);
+
+ $current_user = wp_get_current_user();
+ $bank_deposit_fields['payment_email'] = array(
+ 'type' => 'hidden',
+ 'name' => 'payment_email',
+ 'id' => 'payment_email',
+ 'default' => $current_user->user_email,
+ );
?>
<div class="wp-travel-bank-deposit-wrap">
<div id="wp-travel-bank-deposit-content" class="wp-travel-popup" >
--- a/wp-travel/inc/gateways/standard-paypal/class-wp-travel-gateway-paypal-request.php
+++ b/wp-travel/inc/gateways/standard-paypal/class-wp-travel-gateway-paypal-request.php
@@ -128,15 +128,7 @@
$args['handling'] = 0;
$args['handling_cart'] = 0;
$args['no_shipping'] = 0;
- // $args['notify_url'] = esc_url(
- // add_query_arg(
- // array(
- // 'wp_travel_listener' => 'IPN',
- // 'partial' => true,
- // ),
- // home_url( 'index.php' )
- // )
- // );
+ $args['notify_url'] = esc_url( add_query_arg( 'wp_travel_listener', 'IPN', home_url( 'index.php' ) ) );
// Cart Item.
$agrs_index = 1;
@@ -147,6 +139,10 @@
$args[ 'amount_' . $agrs_index ] = sanitize_text_field( wp_unslash( $_POST['amount'] ) );
$args[ 'item_number_' . $agrs_index ] = $booking_id;
+ $args['custom'] = wp_json_encode( array(
+ 'booking_id' => $booking_id,
+ 'payment_type' => 'complete_partial',
+ ) );
} elseif ( $items ) { // Normal Payment.
@@ -197,7 +193,7 @@
$args['handling'] = 0;
$args['handling_cart'] = 0;
$args['no_shipping'] = 0;
- // $args['notify_url'] = esc_url( add_query_arg( 'wp_travel_listener', 'IPN', home_url( 'index.php' ) ) );
+ $args['notify_url'] = esc_url( add_query_arg( 'wp_travel_listener', 'IPN', home_url( 'index.php' ) ) );
// Cart Item.
$agrs_index = 1; // Initialize only once
@@ -258,14 +254,17 @@
$args[ 'quantity_1'] = 1;
$args[ 'amount_1' ] = $payment_amount;
$args[ 'item_number_1' ] = $booking_id;
+ $args['custom'] = wp_json_encode( array(
+ 'booking_id' => $booking_id,
+ 'payment_mode' => $payment_mode,
+ 'user_id' => get_current_user_id(),
+ ) );
} else {
return;
}
$args['option_index_0'] = $agrs_index;
- $args['custom'] = $booking_id;
-
return apply_filters( 'wp_travel_paypal_args', $args );
--- a/wp-travel/inc/gateways/standard-paypal/paypal-functions.php
+++ b/wp-travel/inc/gateways/standard-paypal/paypal-functions.php
@@ -30,17 +30,11 @@
*/
function wptravel_listen_paypal_ipn() {
- if ( isset( $_POST['payer_id'] ) && isset( $_POST['txn_id'] ) ) {
- // Get current user's email
- $current_user_email = '';
-
- if ( is_user_logged_in() ) {
- $current_user = wp_get_current_user();
- $current_user_email = $current_user->user_email;
- }
-
- // Pass the current user's email to the action
- do_action( 'wp_travel_verify_paypal_ipn', $current_user_email );
+ if ( isset( $_GET['wp_travel_listener'] )
+ && $_GET['wp_travel_listener'] == 'IPN'
+ || isset( $_GET['test'] )
+ && $_GET['test'] == true ) {
+ do_action( 'wp_travel_verify_paypal_ipn' );
}
}
@@ -55,13 +49,20 @@
*/
function wptravel_paypal_ipn_process( $current_user_email ) {
+ include dirname( __FILE__ ) . '/php-paypal-ipn/IPNListener.php';
+ $listener = new IPNListener();
+
+ $settings = wptravel_get_settings();
- $settings = wptravel_get_settings();
+ $listener->use_sandbox = ( $settings['wt_test_mode'] ) ? true : false;
- $message = null;
+ /**
+ * Check if IPN was successfully processed
+ */
+ if ( $verified = $listener->processIpn() ) {
if ( $_POST['mc_currency'] != $settings['currency'] ) { // @phpcs:ignore
- $message .= "nCurrency does not match those assigned in settingsn";
+ return;
}
/**
@@ -70,12 +71,16 @@
* PayPal transaction id (txn_id) is stored in the database, we check
* that against the txn_id returned.
*/
- $booking_id = isset( $_POST['custom'] ) ? absint( $_POST['custom'] ) : 0;
+ $custom = json_decode( wp_unslash( $_POST['custom'] ?? '' ), true );
+
+ $booking_id = absint( $custom['booking_id'] ?? 0 );
+ $payment_mode = sanitize_text_field( $custom['payment_mode'] ?? '' );
+ $payment_type = sanitize_text_field( $custom['payment_type'] ?? '' );
+ $user_id = absint( $custom['user_id'] ?? 0 );
+
$txn_id = get_post_meta( $booking_id, 'txn_id', true );
if ( empty( $txn_id ) ) {
update_post_meta( $booking_id, 'txn_id', sanitize_text_field( $_POST['txn_id'] ) );
- } else {
- $message .= "nThis payment was already processedn";
}
/**
@@ -84,8 +89,8 @@
* Create a new payment, send customer an email and empty the cart
*/
- if ( ! empty( $_POST['payer_status'] ) && $_POST['payer_status'] == 'VERIFIED' && ! isset( $_GET['partial'] ) ) { // @phpcs:ignore
-
+ if ( ! empty( $_POST['payment_status'] ) && $_POST['payment_status'] == 'Completed' && !$payment_type ) { // @phpcs:ignore
+
// Fixed Paypal booking step
set_post_type( $booking_id, 'itinerary-booking' );
@@ -142,15 +147,8 @@
update_post_meta( $new_payment_id, 'wp_travel_payment_amount', $amount );
-
- if( $_POST['payment_status'] == 'Completed' ){
-
- update_post_meta( $new_payment_id, 'wp_travel_payment_status', 'paid' );
- }else{
-
- update_post_meta( $new_payment_id, 'wp_travel_payment_status', 'pending' );
- }
-
+ update_post_meta( $new_payment_id, 'wp_travel_payment_status', 'paid' );
+
update_post_meta( $new_payment_id, 'wp_travel_payment_mode', 'partial' );
$json = sanitize_text_field( wp_unslash( $_POST['payment_details'] ) );
@@ -160,31 +158,23 @@
update_post_meta( $payment_id, '_paypal_args', wptravel_sanitize_array( $_POST ) );
- if( $_GET['payment'] == 'partial' ){
- if( $_POST['payment_status'] == 'Completed' ){
- update_post_meta( $payment_id, 'wp_travel_payment_status', 'partially_paid' );
- }else{
- update_post_meta( $payment_id, 'wp_travel_payment_status', 'pending' );
- }
- }elseif( $_GET['payment'] == 'full' ){
-
- if( $_POST['payment_status'] == 'Completed' ){
- update_post_meta( $payment_id, 'wp_travel_payment_status', 'paid' );
- }else{
-
- update_post_meta( $payment_id, 'wp_travel_payment_status', 'pending' );
- }
+ if( $payment_mode == 'partial' ){
+
+ update_post_meta( $payment_id, 'wp_travel_payment_status', 'partially_paid' );
+
+ }elseif( $payment_mode == 'full' ){
+
+ update_post_meta( $payment_id, 'wp_travel_payment_status', 'paid' );
+
}else{
- if( $_POST['payment_status'] == 'Completed' ){
- update_post_meta( $payment_id, 'wp_travel_payment_status', 'paid' );
- }else{
- update_post_meta( $payment_id, 'wp_travel_payment_status', 'pending' );
- }
+
+ update_post_meta( $payment_id, 'wp_travel_payment_status', 'pending' );
+
}
update_post_meta( $payment_id, 'wp_travel_payment_mode', 'full' );
- if( $_GET['payment'] == 'partial' ){
+ if( $payment_mode == 'partial' ){
update_post_meta( $payment_id, 'wp_travel_payment_mode', 'partial' );
}
@@ -192,10 +182,9 @@
do_action( 'wp_travel_after_successful_payment', $booking_id );
}
- } elseif( ! empty( $_POST['payer_status'] ) && $_POST['payer_status'] == 'VERIFIED' && isset( $_GET['partial'] ) ) {
+ } elseif( ! empty( $_POST['payment_status'] ) && $_POST['payment_status'] == 'Completed' && $payment_type == 'complete_partial' ) {
$payment_gateway = 'paypal';
- $booking_id = (int)$_GET['booking_id'];
$payment_id = get_post_meta( $booking_id, 'wp_travel_payment_id', true );
@@ -319,11 +308,8 @@
die;
- }else {
-
- $message .= "nPayment status not set to Completedn";
-
- }
+ }
+ }
}
add_action( 'wp_travel_verify_paypal_ipn', 'wptravel_paypal_ipn_process' );
--- a/wp-travel/inc/gateways/standard-paypal/php-paypal-ipn/IPNListener.php
+++ b/wp-travel/inc/gateways/standard-paypal/php-paypal-ipn/IPNListener.php
@@ -55,6 +55,20 @@
public $use_sandbox = false;
/**
+ * Optional explicit path to a CA bundle file for cURL SSL verification.
+ * Left null by default so cURL uses the server's own system CA bundle,
+ * which is properly maintained/updated by the hosting environment. Only
+ * set this if you have a specific reason to override the system bundle
+ * (e.g. a broken local dev environment with no CA store at all) - do NOT
+ * ship a bundled/static cert file here, since PayPal periodically rotates
+ * its certificate chain and a stale bundled file will start failing
+ * verification even though the connection is legitimate.
+ *
+ * @var string|null
+ */
+ public $ca_bundle_path = null;
+
+ /**
* The amount of time, in seconds, to wait for the PayPal server to respond
* before timing out. Default 30 seconds.
*
@@ -98,7 +112,17 @@
curl_setopt( $ch, CURLOPT_SSL_VERIFYPEER, true );
curl_setopt( $ch, CURLOPT_SSL_VERIFYHOST, 2 );
- curl_setopt( $ch, CURLOPT_CAINFO, dirname( __FILE__ ) . '/cert/api_cert_chain.crt' );
+
+ // Only override cURL's CA bundle if explicitly configured. By default
+ // this is left unset so cURL uses the server's own system CA bundle -
+ // previously this was hardcoded to a bundled cert/api_cert_chain.crt
+ // file, which goes stale as PayPal rotates its certificate chain and
+ // causes SSL verification failures ("cURL error: 60 ... unable to get
+ // local issuer certificate") even on healthy servers.
+ if ( ! empty( $this->ca_bundle_path ) ) {
+ curl_setopt( $ch, CURLOPT_CAINFO, $this->ca_bundle_path );
+ }
+
curl_setopt( $ch, CURLOPT_URL, $uri );
curl_setopt( $ch, CURLOPT_POST, true );
curl_setopt( $ch, CURLOPT_POSTFIELDS, $encoded_data );
@@ -367,4 +391,4 @@
}
}
-}
+}
No newline at end of file
--- a/wp-travel/inc/lib/wp-session/class-wp-session.php
+++ b/wp-travel/inc/lib/wp-session/class-wp-session.php
@@ -160,14 +160,39 @@
public function write_data() {
$option_key = "_wp_session_{$this->session_id}";
- if( apply_filters( 'wp_travel_enable_cart_logs', false ) == true ){
+ if ( ! isset( $_COOKIE[ WP_TRAVEL_SESSION_COOKIE ] ) ) {
+
+ $ip = $_SERVER['REMOTE_ADDR'] ?? '';
+
+ $previous_session = get_transient(
+ 'wt_last_session_' . md5( $ip )
+ );
+
+ if ( $previous_session ) {
+
+ wt_cart_log(
+ 'POTENTIAL SESSION RESET',
+ array(
+ 'previous_session' => $previous_session,
+ 'new_session' => $this->session_id,
+ ),
+ true
+ );
+ }
+
+ set_transient(
+ 'wt_last_session_' . md5( $ip ),
+ $this->session_id,
+ DAY_IN_SECONDS
+ );
+
wt_cart_log(
- 'SESSION WRITE',
+ 'NEW SESSION CREATED',
array(
'session_id' => $this->session_id,
'option_key' => $option_key,
- 'container' => $this->container,
- )
+ ),
+ true
);
}
--- a/wp-travel/templates/account/tab-content/bookings.php
+++ b/wp-travel/templates/account/tab-content/bookings.php
@@ -27,8 +27,35 @@
$back_link = $detail_link;
$request_data = WP_Travel::get_sanitize_request();
+
+
if ( $request_data ) { // @phpcs:ignore
+
+ $booking_id = isset( $request_data['detail_id'] ) ? absint( $request_data['detail_id'] ) : 0;
+
+ $user_id = absint(
+ get_post_meta( $booking_id, 'wp_travel_customer_user_id', true )
+ );
+
+ $user = get_userdata( $user_id );
+
+ $booking_email = strtolower( trim( $user->user_email ) );
+
+ $current_user = wp_get_current_user();
+
+ $current_user_email = $current_user->user_email;
+
+ if ( $booking_email !== $current_user_email ) {
+
+ echo '<div class="wp-travel-error-message">';
+ echo esc_html__( 'You have no access to this booking.', 'wp-travel' );
+ echo '</div>';
+
+ return;
+ }
+
wptravel_print_notices();
+
$booking_id = isset( $request_data['detail_id'] ) ? absint( $request_data['detail_id'] ) : 0;
$details = wptravel_booking_data( $booking_id );
$payment_data = wptravel_payment_data( $booking_id );
--- a/wp-travel/wp-travel.php
+++ b/wp-travel/wp-travel.php
@@ -3,7 +3,7 @@
* Plugin Name: WP Travel
* Plugin URI: http://wptravel.io/
* Description: The best choice for a Travel Agency, Tour Operator or Destination Management Company, wanting to manage packages more efficiently & increase sales.
- * Version: 11.8.0
+ * Version: 11.8.1
* Author: WP Travel
* Author URI: http://wptravel.io/
* Requires at least: 6.0.0