diff --git a/public_html/wp-content/plugins/camptix/camptix.php b/public_html/wp-content/plugins/camptix/camptix.php index 290c3c118a..955b0797e8 100644 --- a/public_html/wp-content/plugins/camptix/camptix.php +++ b/public_html/wp-content/plugins/camptix/camptix.php @@ -6838,6 +6838,21 @@ function get_ticket_title( $post_id ) { */ function get_remaining_tickets( $post_id, $via_reservation = false ) { $remaining = 0; + $apcu_key = 'remaining_tickets:' . get_current_blog_id() . ':' . $post_id; + if ( function_exists( 'apcu_enabled' ) && apcu_enabled() && ! $via_reservation ) { + do { + if ( isset( $fetch_success ) ) { + // If we failed to fetch the value, wait 50ms before trying again. + usleep( 50000 ); + } + $remaining = (int) apcu_fetch( $apcu_key, $fetch_success ); + } while ( ! $fetch_success && ! apcu_add( $apcu_key . ':lock', 1, 5 ) ); + + if ( $fetch_success ) { + return $remaining; + } + } + if ( $this->is_ticket_valid_for_display( $post_id ) ) { $quantity = intval( get_post_meta( $post_id, 'tix_quantity', true ) ); $remaining = $quantity - $this->get_purchased_tickets_count( $post_id ); @@ -6856,9 +6871,23 @@ function get_remaining_tickets( $post_id, $via_reservation = false ) { $remaining -= $reserved_tickets; } - return apply_filters( 'camptix_get_remaining_tickets', $remaining, $post_id, $via_reservation, $quantity, $reservations ); + // Can't have less than 0 remaining tickets. + $remaining = max( $remaining, 0 ); + + if ( function_exists( 'apcu_enabled' ) && apcu_enabled() && ! $via_reservation ) { + apcu_store( $apcu_key, $remaining, 10 * MINUTE_IN_SECONDS ); + } + + return $remaining; } + /** + * Fetch the number of purchased tickets for a ticket. + * + * @param int $post_id The ticket post ID + * @param string $via_reservation Optional. The reservation token. + * @return int + */ function get_purchased_tickets_count( $post_id, $via_reservation = false ) { $purchased = 0; @@ -7172,13 +7201,13 @@ function form_checkout() { if ( ! is_email( $receipt_email ) ) $this->error_flags['no_receipt_email'] = true; + $this->verify_order( $this->order, true /* reserve tickets */ ); + // If there's at least one error, don't proceed with checkout. if ( $this->error_flags ) { return $this->form_attendee_info(); } - $this->verify_order( $this->order ); - $reservation_quantity = 0; if ( isset( $this->reservation ) && $this->reservation ) $reservation_quantity = $this->reservation['quantity']; @@ -7274,6 +7303,7 @@ function form_checkout() { */ $result = $payment_method_obj->payment_checkout( $payment_token ); if ( self::PAYMENT_STATUS_FAILED == $result ) { + // TODO: Release ticket? Otherwise this will just be reserved until the cache timeout. return $this->form_attendee_info(); } @@ -7287,7 +7317,7 @@ function form_checkout() { /** * Verify an order */ - function verify_order( &$order = array() ) { + function verify_order( &$order = array(), $reserve_tickets = false ) { $tickets_objects = get_posts( array( 'post_type' => 'tix_ticket', 'post_status' => 'publish', @@ -7387,6 +7417,23 @@ function verify_order( &$order = array() ) { $this->error_flag( 'tickets_excess' ); } + // Triple check - make sure we actually have a ticket to sell them. + if ( + function_exists( 'apcu_enabled' ) && apcu_enabled() && + $reserve_tickets && + empty( $this->error_flags[ 'tickets_excess' ] ) && + $item['quantity'] && + ! $via_reservation + ) { + // The key used in get_remaining_tickets() above. + $apcu_key = 'remaining_tickets:' . get_current_blog_id() . ':' . $ticket->ID; + $ticket->tix_remaining = apcu_dec( $apcu_key, $item['quantity'], $success ); + if ( ! $success || $ticket->tix_remaining < 0 ) { + $item['quantity'] = max( 0, min( $ticket->tix_remaining, $ticket->tix_remaining ) ); + $this->error_flag( 'tickets_excess' ); + } + } + // Track coupons usage quantity. if ( $ticket->tix_coupon_applied ) { $coupon_used += $item['quantity'];