Prevent PO Boxes in WooCommerce Shipping Addresses

In some cases PO Boxes are not allowed when shipping certain products or when using specific carriers.  The code below disables PO Boxes for all shipping addresses.  You can add additional logic if you only need to disallow PO Boxes in certain cases.

Create a function for checking if a string is a PO Box. This uses a regular expression that should catch most of the possible formats that a customer would use, but of course there’s always some edge case that may not be caught.

function myprefix_is_po_box($s) {
  return preg_match("/^\s*((P(OST)?.?\s*(O(FF(ICE)?)?)?.?\s+(B(IN|OX))?)|B(IN|OX))/i", $s);
}

Now create a function that adds extra validation to the WooCommerce registration form. In the example below, it is showing an error message specific to a winery since alcohol is not able to be sent to a PO Box.  This function can be expanded to include any other custom validations you want to add for the registration form, such as custom password restrictions or required fields.

function myprefix_validate_extra_register_fields( $username, $email, $validation_errors ) {
  $errors = array();
  
  foreach(array('shipping_address_1', 'shipping_address_2') as $fld) {
    if (myprefix_is_po_box($_POST[$fld])) {
      $errors[] = 'A PO Box address was detected in the shipping address. Wine cannot be shipped to PO Boxes.';
    }
  }
  
  if ( !empty ($errors) ) {
    $validation_errors->add( 'custom_field_errors', 'Please correct the following issues:<ul><li>' . implode('</li><li>', $errors) . '</li></ul>' );
  }
}
add_action( 'woocommerce_register_post', 'myprefix_validate_extra_register_fields', 10, 3 );

The last thing is to create a function to perform the same validations when a user edits their shipping address for their account.

function myprefix_woocommerce_after_save_address_validation($user_id, $load_address, $address) {
  if (isset($_POST['action']) && $_POST['action'] == 'edit_address') {
    if (isset($_POST['shipping_address_1']) && myprefix_is_po_box($_POST['shipping_address_1'])) {
      wc_add_notice('A PO Box address was detected in the shipping address. Wine cannot be shipped to PO Boxes.', 'error');
    }
    if (isset($_POST['shipping_address_2']) && myprefix_is_po_box($_POST['shipping_address_2'])) {
      wc_add_notice('A PO Box address was detected in the shipping address. Wine cannot be shipped to PO Boxes.', 'error');
    }
  }
}
add_action( 'woocommerce_after_save_address_validation', 'myprefix_woocommerce_after_save_address_validation', 10, 3);

That is all that is required to prevent adding a PO Box to a customer’s address.  Additional validations will be necessary during the checkout process if your shipping service doesn’t already perform those validations.  If you’re shipping with FedEx, for example, it will automatically fail validation as long as you have properly indicated that your order contains alcohol.  If you need to do custom validation during checkout you can use the woocommerce_checkout_process action hook.