This website uses cookies to allow us to see how the site is used. If you continue to use this site, we assume that you are okay with this. If you want to use the sites without cookies, please see our privacy policy.

How to remove billing address fields from WooCommerce checkout form if there are only virtual products in the cart [WooCommerce Code Snippet]

WooCommerce allows you to sell physical as well as digital / virtual / downloadable products. In case of physical products, we need to collect billing and shipping address because goods need to be delivered to the customers. However in case of digital products, we don’t need to deliver goods and so it does not make sense to collect billing address.

This is actually one of the features my client wanted who has an online store where she sells hard cover books as well as digital copies. For smooth checkout process she requested a feature where users ordering digital copies only should not go through the hassle of filling billing address details.

Here’s the code snippet for the same:


function wooc_custom_checkout_fields( $fields ) {
	if( wooc_cart_has_virtual_product() == true ) { //helper function to check if there are only virtual products in the cart
	return $fields;

function wooc_cart_has_virtual_product() {
	global $woocommerce;
	// By default, no virtual product
	$has_virtual_products = false;
	// Default virtual products number
	$virtual_products = 0;
	// Get all products in cart
	$products = $woocommerce->cart->get_cart();
	// Loop through cart products
	foreach( $products as $product ) {
		// Get product ID and '_virtual' post meta
		$product_id = $product['product_id'];
		$is_virtual = get_post_meta( $product_id, '_virtual', true );
		// Update $has_virtual_product if product is virtual
		if( $is_virtual == 'yes' ) {
			$virtual_products += 1;
	if( count($products) == $virtual_products ) { //match the count with the products in the cart
		$has_virtual_products = true;
	return $has_virtual_products;

How to add metaboxes to your plugin settings page

A handy snippet for plugin developers…

 * Plugin Name: Metaboxes on Plugin Setings Pages
 * Description: How to add metaboxes to your plugin settings page
 * Version:     1.0
 * Author:      malCure
 * Author URI:
 * Text Domain: malcure
 * License:     MIT
 * License URI:
 * Plugin URI:

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly

final class my_metaboxes_init {
	static function get_instance() {
		static $instance = null;
		if ( is_null( $instance ) ) {
			$instance = new self();
			return $instance;

	private function __construct() {

	function init() {
		add_action( 'admin_menu', array( $this, 'settings_menu' ) );
		add_action( 'load-toplevel_page_malcure', array( $this, 'load_meta_boxes' ) );
		add_action( 'load-toplevel_page_malcure', array( $this, 'add_meta_boxes' ) );
		add_action( 'load-toplevel_page_malcure', array( $this, 'add_admin_scripts' ) );

	function settings_menu() {
		add_menu_page( 'My Plugin Settings', 'My Plugin Settings', 'manage_options', 'malcure', array( $this, 'settings_page' ), 'dashicons-chart-pie', 79 );

	function load_meta_boxes() {
		add_action( 'add_meta_boxes', array( $this, 'my_plugin_metaboxes' ) );

	function add_meta_boxes() {
		do_action( 'add_meta_boxes', 'toplevel_page_malcure', '' );

	function add_admin_scripts() {
		wp_enqueue_script( 'jquery' );
		wp_enqueue_script( 'common' );
		wp_enqueue_script( 'wp-lists' );
		wp_enqueue_script( 'postbox' );

	function settings_page() {
		<div class="wrap">
		<h1 id="page_title">My Settings Page Heading</h1>
			<div id="poststuff">
				<div class="metabox-holder columns-2" id="post-body">
					<div class="postbox-container" id="post-body-content">
						<?php do_meta_boxes( 'toplevel_page_malcure', 'main', null ); ?>
					<!-- #postbox-container -->
					<div id="postbox-container-1" class="postbox-container">
								do_meta_boxes( 'toplevel_page_malcure', 'side', null );
		<script type="text/javascript">
		jQuery(document).ready(function($) {
			// close postboxes that should be closed
			// postboxes setup

	function my_plugin_metaboxes() {
		add_meta_box( 'my_first_metabox', 'My First Metabox', array( $this, 'my_first_metabox_html' ), 'toplevel_page_malcure', 'main', 'high' );
		add_meta_box( 'my_second_metabox', 'My Second Metabox', array( $this, 'my_second_metabox_html' ), 'toplevel_page_malcure', 'side', 'high' );

	function my_first_metabox_html() {
		Howdy! I'm your first custom metabox. Now with some WordPress and PHP magic you can customize me as you like.

	function my_second_metabox_html() {
		Meanwhile, I'll sit in the side. I'm your second custom metabox.


function my_metaboxes_init() {
	return my_metaboxes_init::get_instance();