Hoppa till innehållet
Snabblösning

Dölj priser och köpknapp för utloggade kunder i WooCommerce

Laptop på ett skrivbord som visar produktsidan för en gul soffa i en webbutik
Foto: Igor Miske / Unsplash

Jag döljer priser för utloggade besökare med ett litet eget plugin. woocommerce_get_price_html byter pris-HTML:en mot länken ”Logga in för pris”. woocommerce_is_purchasable gör varan oköpbar för utloggade. Det är köpbarheten som stoppar köpet, inte det dolda priset. Den gäller både den klassiska varukorgen och Store API, som blockkassan bygger på.

Ett dolt pris i HTML är inte ett hemligt pris. Store API svarar utan inloggning och skickar med priset i JSON. Längre ner visar jag var läckorna finns.

Koden för produktsida och produktlistor

Koden ligger i ett eget plugin, till exempel wp-content/plugins/wpu-hide-prices/wpu-hide-prices.php. Jag lägger den inte i temat, eftersom prisskyddet ska finnas kvar om någon byter tema.

<?php
/**
 * Plugin Name: WPU Hide Prices For Guests
 * Description: Shows "Logga in för pris" instead of prices and blocks purchases for logged-out visitors.
 * Version: 1.0.0
 * Requires Plugins: woocommerce
 */

defined( 'ABSPATH' ) || exit;

/**
 * Send the customer back to the product after login,
 * so they land where they started.
 */
function wpu_login_for_price_html( $product = null ) {
	$redirect = ( $product instanceof WC_Product ) ? $product->get_permalink() : home_url( '/' );

	return sprintf(
		'<a class="wpu-login-for-price" href="%s">%s</a>',
		esc_url( wp_login_url( $redirect ) ),
		esc_html__( 'Logga in för pris', 'wpu-hide-prices' )
	);
}

/**
 * Late priority so other plugins that rewrite the price HTML
 * cannot put the price back after us.
 */
function wpu_hide_price_html( $price_html, $product ) {
	if ( is_user_logged_in() ) {
		return $price_html;
	}
	return wpu_login_for_price_html( $product );
}
add_filter( 'woocommerce_get_price_html', 'wpu_hide_price_html', 999, 2 );

/**
 * Hiding the price is cosmetic. A non-purchasable product is what actually
 * stops both the classic cart and the Store API cart from accepting it.
 */
function wpu_guest_cannot_purchase( $purchasable ) {
	if ( is_user_logged_in() ) {
		return $purchasable;
	}
	return false;
}
add_filter( 'woocommerce_is_purchasable', 'wpu_guest_cannot_purchase', 999 );
// Variations have their own filter. Covering both avoids relying on internals.
add_filter( 'woocommerce_variation_is_purchasable', 'wpu_guest_cannot_purchase', 999 );

På produktsidan och i klassiska produktlistor ersätter länken priset. För enkla produkter visar WooCommerce ingen köpknapp när varan inte är köpbar. Variabla produkter skriver ut köpformuläret ändå. Den utloggade besökaren ser då attributväljarna, en inaktiv köpknapp och meddelandet ”Tyvärr, denna produkt är inte tillgänglig. Välj en annan kombination.” För en B2B-kund är det missvisande. Externa produkter visar alltid sin knapp.

Därför tar jag bort hela köpformuläret för utloggade. Snutten gäller klassiska produktmallar, inte blockteman, och ligger i samma plugin.

<?php
/**
 * Classic product templates only, not block themes.
 * Variable and external products print their add-to-cart form even when
 * nothing is purchasable, so guests would see a disabled button and a
 * misleading notice. Removing the whole form avoids that.
 */
function wpu_remove_add_to_cart_for_guests() {
	if ( is_user_logged_in() ) {
		return;
	}
	remove_action( 'woocommerce_single_product_summary', 'woocommerce_template_single_add_to_cart', 30 );
}
add_action( 'woocommerce_before_single_product', 'wpu_remove_add_to_cart_for_guests' );

Blockbaserade listor och produktblock

Köpbarheten slår igenom även i block. Produktblock, produktlistor i Store API och korsförsäljning i varukorgen anropar metoden WC_Product_Variable::has_purchasable_variations(). Varje variation går då igenom is_purchasable(), och där träffar filtret.

Priset beror på blocket. Product Collection och produktmallar i blockteman visar länken, eftersom blocket Product Price renderar priset med get_price_html() på servern. Det äldre blocket All Products bygger priset från prices i Store API och visar beloppet. Använder butiken All Products bör du byta till Product Collection.

Kassan

Gäller blockkassan och den klassiska kassan. Båda kassorna ritas ut på kassasidan. Därför skickar samma kontroll i template_redirect utloggade till inloggningen, oavsett vilken kassa sidan använder. Jag lägger snutten i samma plugin.

<?php
/**
 * Runs on the checkout page itself, which hosts both the Checkout block
 * and the classic checkout, so one check covers both.
 */
function wpu_redirect_guest_from_checkout() {
	if ( is_user_logged_in() || ! function_exists( 'is_checkout' ) || ! is_checkout() ) {
		return;
	}

	wp_safe_redirect( wp_login_url( wc_get_checkout_url() ) );
	exit;
}
add_action( 'template_redirect', 'wpu_redirect_guest_from_checkout' );

Omdirigeringen skyddar bara sidladdningen. Blockkassan skickar beställningen via Store API, och där är det köpbarheten som stoppar nya varor. Store API svarar då med felkoden woocommerce_rest_product_not_purchasable. Varor som redan ligger i varukorgen tar WooCommerce bort när varukorgssessionen laddas, i både blockkassan och den klassiska kassan.

Där priset fortfarande läcker

Store API är oautentiserat. GET /wp-json/wc/store/v1/products och /wc/store/v1/products/:id returnerar ett prices-objekt med price, regular_price, sale_price och price_range, i minsta valutaenhet. Den som öppnar adressen i webbläsaren ser alltså beloppet i minsta enheten enligt currency_minor_unit. Det blir öre om butiken visar två decimaler och hela kronor om den visar noll.

Jag avråder från att bara tömma prices-fältet. Produktlistan går att filtrera med min_price och max_price och sortera med orderby=price. Med några frågor går priset då att ringa in. Store API:s egen utökningsmekanism, woocommerce_store_api_register_endpoint_data(), lägger till data under extensions och är inte gjord för att ta bort fält.

Produktsidans strukturerade data läcker också. WooCommerce skriver ut JSON-LD med offers, och beloppet där kommer från get_price(), inte från den filtrerade pris-HTML:en. Utloggade ser alltså priset i sidkällan trots filtren. Jag tar bort offers för utloggade via filtret woocommerce_structured_data_product i samma plugin.

<?php
/**
 * The product JSON-LD takes the amount from get_price(), not from the
 * filtered price HTML, so guests would still find it in the page source.
 */
function wpu_remove_offers_for_guests( $markup, $product ) {
	if ( is_user_logged_in() ) {
		return $markup;
	}
	unset( $markup['offers'] );
	return $markup;
}
add_filter( 'woocommerce_structured_data_product', 'wpu_remove_offers_for_guests', 10, 2 );

Google kan då varna för saknad offers-data i Search Console. Det är kostnaden för att beloppet inte syns. Gå också igenom eventuella produktflöden till Google och prisjämförelsetjänster, eftersom de läser priset på annat sätt än temat.

Alla fyra delarna finns färdiga i ett plugin:

Moms för företagskunder

Svenska B2B-kunder vill oftast se priser exklusive moms. Loggar bara företagskunder in kan du ställa in butiken att visa priser exklusive moms under WooCommerce, Inställningar, Moms. Blandar butiken privat- och företagskunder räcker inte butiksinställningen. Visning per kundroll kräver mer än så. Standardvisningen ska då vara inklusive moms, eftersom prisinformationsreglerna kräver det för privatkunder.

Fallgropar

Den vanligaste missen är öppen registrering. Får vem som helst skapa ett konto blir ”Logga in för pris” bara ett extra klick. I en B2B-butik stänger jag registreringen och skapar konton manuellt eller via en godkännandeprocess.

Helsidescache är nästa risk. De flesta cacheplugin och webbhotellens cache hoppar över inloggade. En felkonfigurerad cache kan ändå visa en inloggad besökares sida för alla. Jag kontrollerar därför i ett inkognitofönster efter varje cacheändring.

Omdirigeringen träffar alla sidor som räknas som kassa. Skickar du betalningslänkar till kunder utan konto stoppar den även dem.

Plugin eller egen kod

Koden ovan räcker när alla inloggade ska se samma pris. Har du kundspecifika prislistor, rabatter per företag eller godkännande av nya konton passar ett B2B-plugin bättre. Den logiken blir snabbt större än filtren här.

Vanliga frågor

Räcker det att dölja priset med CSS?

Nej. Priset finns kvar i HTML:en och i Store API:s JSON-svar, och varan går fortfarande att köpa. Använd woocommerce_is_purchasable så att varan inte går att lägga i varukorgen för utloggade.

Döljs priset även i Store API?

Nej. Store API svarar utan inloggning och returnerar prices med price, regular_price, sale_price och price_range. Filtren i inlägget döljer bara pris-HTML:en och stoppar köp. Priset går också att ringa in via min_price, max_price och orderby=price.

Fungerar koden med både blockkassan och den klassiska kassan?

Ja. Köpbarhetsfiltret stoppar nya varor i både den klassiska varukorgen och Store API, som blockkassan använder. Omdirigeringen på kassasidan gäller båda kassorna, eftersom båda ritas ut på samma sida.

Källor

Testad med

  • WordPress 7.1.3
  • WooCommerce 11.2.1
  • PHP 8.4

Senast testad 12 oktober 2026

Fungerar inte koden hos dig, eller hittade du ett fel? Berätta

Hjälpte det här inlägget?

Läs vidare

Alla inlägg