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'];