GAP School Module 08 — Conversion Tools Lesson 8.1

Most dealer sites treat all inventory equally: one listing type, one position in the grid, one display format. Priority listing tiers change that. For a monthly fee, a unit gets a badge, a sort boost, and dedicated slots on the homepage. For the dealer, it’s recurring revenue that scales with inventory size. For a consigning seller, it’s a way to accelerate their sale.


The situation

The Anchor build had 166 units in inventory. Some were consignments where the seller had skin in the game and would pay for faster exposure. Some were high-margin units the dealer wanted in front of more buyers. There was no mechanism to differentiate them. Priority tiers created a product layer on top of the inventory system with three tiers ($500/month Standard, $1,000/month Featured, $2,000/month Premium) with progressively more visibility.


What I did

Tier state machine with auto-expiry

Each unit has a tier stored as post meta with an expiry date. The tier drives all downstream behavior. An expired tier auto-reverts to standard on the next read — no scheduled job required:

PHP
function [client]_get_listing_tier( int $unit_id ): string { $tier = get_post_meta( $unit_id, '[client]_listing_tier', true ); $expires = get_post_meta( $unit_id, '[client]_tier_expires', true ); if ( ! $tier || $tier === 'standard' ) { return 'standard'; } // Expired tier reverts to standard on read if ( $expires && strtotime( $expires ) < time() ) { update_post_meta( $unit_id, '[client]_listing_tier', 'standard' ); delete_post_meta( $unit_id, '[client]_tier_expires' ); return 'standard'; } return in_array( $tier, [ 'featured', 'premium' ], true ) ? $tier : 'standard'; }

Sort boost via SQL CASE expression

Featured and premium units appear before standard units in search and browse results. The boost runs in the database via a filter on WP_Query’s ORDER BY — not in PHP after the query returns:

PHP
add_filter( 'posts_join', '[client]_join_tier_meta', 10, 2 ); add_filter( 'posts_orderby', '[client]_apply_tier_sort_boost', 10, 2 ); function [client]_join_tier_meta( string $join, WP_Query $query ): string { if ( $query->get( 'post_type' ) !== '[client]_unit' ) { return $join; } global $wpdb; $join .= " LEFT JOIN {$wpdb->postmeta} pm_tier ON pm_tier.post_id = {$wpdb->posts}.ID AND pm_tier.meta_key = '[client]_listing_tier'"; return $join; } function [client]_apply_tier_sort_boost( string $orderby, WP_Query $query ): string { if ( ! $query->is_main_query() || $query->get( 'post_type' ) !== '[client]_unit' ) { return $orderby; } $boost = "CASE WHEN pm_tier.meta_value = 'premium' THEN 1 WHEN pm_tier.meta_value = 'featured' THEN 2 ELSE 3 END ASC"; return $boost . ( $orderby ? ', ' . $orderby : '' ); }

Visual badge rendering

The tier badge renders in the listing card template. Display logic in the template, business logic in the model:

PHP
function [client]_render_tier_badge( int $unit_id ): void { $tier = [client]_get_listing_tier( $unit_id ); if ( $tier === 'standard' ) { return; } $labels = [ 'featured' => 'Featured', 'premium' => 'Premium' ]; $classes = [ 'featured' => 'tier-badge tier-badge--featured', 'premium' => 'tier-badge tier-badge--premium', ]; printf( '<span class="%s">%s</span>', esc_attr( $classes[ $tier ] ), esc_html( $labels[ $tier ] ) ); }

Homepage premium slots

Premium units get dedicated slots above the main browse grid on the homepage, filled by a separate WP_Query:

PHP
function [client]_get_homepage_premium_units( int $limit = 3 ): array { $query = new WP_Query( [ 'post_type' => '[client]_unit', 'post_status' => 'publish', 'posts_per_page' => $limit, 'meta_query' => [ [ 'key' => '[client]_listing_tier', 'value' => 'premium', ], [ 'key' => '[client]_tier_expires', 'value' => current_time( 'mysql' ), 'compare' => '>=', 'type' => 'DATETIME', ], ], ] ); return $query->posts; }

Why it matters

Listing tiers are a revenue product that doesn’t require additional staff. The inventory is already there. The site is already built. Adding a tier system converts the visibility the site already provides into something with a price attached to it.

The auto-expiry mechanism is what makes tiers financially clean. A tier that requires manual downgrade stays active after the billing period ends unless someone remembers to act. An auto-expiring tier converts back to standard on the next read regardless of whether anyone takes action.


The Anchor build

8 featured + 3 premium slots active at peak. Monthly tier revenue: $2,400 (8 × $500 + 3 × $1,000 minus one 30-day trial discount). The three premium units were consignment listings where sellers paid the fee themselves. The featured units were a mix of dealer-owned high-margin units and consignments where the seller opted up.


Do this, not that

  • Auto-expire tiers from post meta on read. A tier that requires a scheduled job to expire is a tier that fails when cron is broken. Expiry on read is synchronous and requires no background process.
  • Apply sort boost via SQL, not PHP. Sorting 166 units in PHP after fetching them defeats WP_Query’s pagination. The CASE expression in ORDER BY runs in the database where it belongs.
  • Keep badge rendering in the template, not the model. The badge is display logic. The tier value is business logic. They belong in different places.
  • Don’t create a stored “standard” value. Standard is the absence of a tier. A stored “standard” value creates migration complexity when the tier logic changes and means every unit write needs to set it.
When you’re ready to build

The lessons are yours. When you want it built, we’re here.

Every lesson stays free — no account, no paywall, no email gate, ever. But if you’d rather have this system standing on your business than wire all 48 lessons yourself, leave your email. We’ll send you a direct line to a build — and you’ll be first to hear when we add new tools to the curriculum.

None of this gates a single lesson. The curriculum was free before you got here and it stays that way.

We’ll use your email to send you a fast-track to a GAP build and occasional notes on how GAP builds digital sales departments. Lessons stay 100% free — no email required to read any of them. We never share or sell your information. Unsubscribe any time. Privacy policy at gapindustriesllc.com/privacy.html.

Done learning how it’s built? We’ll build it.

You came here to understand the system, and now you do. If you’d rather have it standing on your business than spend the next three months wiring it yourself, GAP Concierge is the same architecture from these lessons — a white-label AI agent that knows your catalog and captures your leads — set up for you, from $97/mo.

See GAP Concierge →