WC_Coupon_Data_Store_CPT::check_and_hold_coupon_for_user()publicWC 1.0

Check and records coupon usage tentatively for passed user aliases for short period of time so that counts validation is correct. Returns early if there is no limit per user for the coupon.

Method of the class: WC_Coupon_Data_Store_CPT{}

Hooks from the method

Return

null|false|Int.

Usage

$WC_Coupon_Data_Store_CPT = new WC_Coupon_Data_Store_CPT();
$WC_Coupon_Data_Store_CPT->check_and_hold_coupon_for_user( $coupon, $user_aliases, $user_alias );
$coupon(WC_Coupon) (required)
Coupon object.
$user_aliases(array) (required)
Emails or Ids to check for user.
$user_alias(string) (required)
Email/ID to use as used_by value.

WC_Coupon_Data_Store_CPT::check_and_hold_coupon_for_user() code WC 8.7.0

public function check_and_hold_coupon_for_user( $coupon, $user_aliases, $user_alias ) {
	global $wpdb;
	$limit_per_user = $coupon->get_usage_limit_per_user();
	$held_time      = $this->get_tentative_held_time();

	if ( 0 >= $limit_per_user || 0 >= $held_time ) {
		// This coupon do not have any restriction for usage per customer. No need to check further, lets bail.
		return null;
	}

	if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', true ) ) {
		return null;
	}

	$format = implode( "','", array_fill( 0, count( $user_aliases ), '%s' ) );

	$query_for_usages = $wpdb->prepare(
		"
			SELECT COUNT(*) FROM $wpdb->postmeta
			WHERE {$wpdb->postmeta}.meta_key = '_used_by'
			AND {$wpdb->postmeta}.meta_value IN ('$format')
			AND {$wpdb->postmeta}.post_id = %d
			FOR UPDATE
			",
		array_merge(
			$user_aliases,
			array( $coupon->get_id() )
		)
	); // WPCS: unprepared SQL ok.

	$query_for_tentative_usages = $this->get_tentative_usage_query_for_user( $coupon->get_id(), $user_aliases );
	$db_timestamp               = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM ' . $wpdb->posts . ' LIMIT 1' );

	$coupon_used_by_meta_key    = '_maybe_used_by_' . ( (int) $db_timestamp + $held_time ) . '_' . wp_generate_password( 6, false );
	$insert_statement           = $wpdb->prepare(
		"
		INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value )
		SELECT %d, %s, %s FROM $wpdb->posts
		WHERE ( $query_for_usages ) + ( $query_for_tentative_usages ) < %d
		LIMIT 1",
		$coupon->get_id(),
		$coupon_used_by_meta_key,
		$user_alias,
		$limit_per_user
	); // WPCS: unprepared SQL ok.

	// This query can potentially be deadlocked if a combined index on post_id and meta_key is present and there is
	// high concurrency, in which case DB will abort the query which has done less work to resolve deadlock.
	// We will try up to 3 times before giving up.
	for ( $count = 0; $count < 3; $count++ ) {
		$result = $wpdb->query( $insert_statement ); // WPCS: unprepared SQL ok.
		if ( false !== $result ) {
			// Clear meta cache.
			$this->refresh_coupon_data( $coupon );
			break;
		}
	}

	return $result > 0 ? $coupon_used_by_meta_key : $result;
}