How to use wp_mail in CPT page

I revised your code to address some issues, but there is the possibility that the issue is not your code.

First, you are returning too early. Your original code returns on success before you send the email. I moved this to the end so that it sets a value (success or error) at the end based on whether you have a post ID or not, then returns. That way you get your mail functions as well.

It’s unlikely that you need both wp_mail() and mail(). If you’re doing that because you think wp_mail() is not working, that’s really not going to help. wp_mail() uses the phpMailer library If you haven’t done any customization, phpMailer is going to use mail() – so these are ultimately the same unless this was run without WP in which case you’d get errors because functions like wp_insert_post() would be undefined.

Also, the documentation for wp_send_json() indicates that you do not need wp_die() as it handles that for you in the function.

Here’s your code with those changes:

public function submit_contact() {

    if (! DOING_AJAX || ! check_ajax_referer('contact-nonce', 'nonce') ) {
        return $this->return_json('error');
    }

    $name    = sanitize_text_field( $_POST['name'] );
    $phone   = sanitize_text_field( $_POST['phone'] );
    $email   = sanitize_email( $_POST['email'] );
    $message = sanitize_textarea_field( $_POST['message'] );

    $data = array(

        'name'     => $name,
        'package'  => $package,
        'phone'    => $phone,
        'email'    => $email,
        'date'     => $date,
        'date'     => $message,
        'approved' => 0,
        'featured' => 0,
    );

    $args = array(
        'post_title'   => $name,
        'post_content' => $message,
        'post_author'  => 1,
        'post_status'  => 'publish',
        'post_type'    => 'contact',
        'meta_input'   => array(
            '_zon_contact_key' => $data
        )
    );

    $postID = wp_insert_post( $args );

    if ( $postID ) {
        wp_mail( '[email protected]', 'The subject', 'The message' );
    }

    $return_val = ( $postID ) ? 'success' : 'error';
    return $return_val;
}

public function return_json( $status ) {

    $return = array(
        'status' => $status
    );

    wp_send_json($return);

}

If you’re still not getting emails, I would do some troubleshooting on the email side of things. If the post is inserted and no email, your code is working and it’s likely an issue with the mail send, not your code.

If you’re on shared hosting, you need to check into rules for sending email via scripts. WP will send from “[email protected]” by default. Some hosts require the “from” address be a real email. So check into that. Also, you’ve got this code set up for testing with a simple subject and message body. There is the possibility that the sending host is rejecting because it thinks it’s spam based on the body text being so short.

Note: I updated and clarified this section

My suspicion is that if you put in an email logger, you’ll may find that the wp_mail() is running (and logging) as if the mail is sent, but that you still don’t receive it. And if that’s the case, it’s because your host is rejecting the send.

It’s easy to avoid all of these things by setting up to send through SMTP. That would avoid most issues. There are a lot of ways to do that, using a plugin or using code. The Internet abounds with examples – this is just one of them.

If you can’t use SMTP, then you may need to troubleshoot other possible email issues with WP’s default headers and things like that. Here’s an article with more information on that kind of troubleshooting.

UPDATE #2 – Address Email Not Being Received

Based on your update, your issue is email not being received. IMO, that’s likely due to the way you’re setting the headers. As a result, your email is getting rejected.

As I stated in the original answer, you’re better off setting up to send through an SMTP account. That will help you avoid a lot of potential problems.

Regardless of whether you do that or not, you need to fix your header issue. Don’t mess with headers unless you absolutely need to. I’d recommend you take that out completely.

If you must leave it in, you need to fix the following:

  1. Unless you’re changing something, don’t declare it.
  2. $current_user is undefined.
  3. Pass headers as array values.
  4. “From” value in the header is malformed.

Unless you’re changing something, don’t declare it. wp_mail() sets any defaults that are not declared. I would suggest you take out the mime type declaration and the content type for sure.

$current_user is undefined in the code that you posted. Add the following to make sure it’s defined:

$current_user = wp_get_current_user();

Pass headers as array values. wp_mail() takes either a string or array, but if it’s a string, it gets exploded into an array, so you may as well start with an array.

“From” value in the header is malformed the way you have it. You must also have a “from” name in addition to the address. It needs to be in the following format:

"From: The From Name <[email protected]>"

If you don’t have the “name”, substitute the email address:

$headers[] = 'From: ' . $current_user->user_email . '<' . $current_user->user_email . '>';

So with those things addressed, here’s what I recommend:

    if ($postID) {
        $current_user = wp_get_current_user();
        $headers[] = 'From: ' . $current_user->user_email . '<' . $current_user->user_email . '>';
        $to = $email;
        $title = "Test email from";
        $body = "Hello " .  $name . " Your ZonPackage " . $package . " Booking Confirmed ." ;
        $subject ="Zon Package Booking";
        wp_mail( $to, $subject, $body, $headers );
    }

Keep in mind that may not solve your problem entirely. If your “from” address doesn’t have a domain that matches the sending domain, you may still look “spammy” and get rejected. Also, be sure to check your spam folder to see if your messages are being received but are flagged as spam.