Problems with wc_add_notice - php

In my checkout form i add google-captcha in function.php. Then i am verifing that captcha is not empty and not false:
function my_custom_checkout_field_process($data, $errors) {
// Check if set, if its not set add an error.
if(count($errors->get_error_messages()) == 0) {
if ( empty( $_REQUEST['g-recaptcha-response'] ) || !(google_recaptcha( $_REQUEST['g-recaptcha-response'] )) ) {
wc_add_notice( __( 'Please enter correct captcha.', 'error' ));
}
}
}
But function wc_add_notice is showing error message only in next page (thankyou.php). Order is sending. But i want thankyou.php is not loading if captcha is false or empty. What should i add to this code? It must function like in the case with required fields. If there is a fault - error message is appeared and user stays on the same page.

I believe you should just remove the if(count($errors->get_error_messages()) == 0) and it should work. You're getting the error on the thank you page because of that if statement.
function my_custom_checkout_field_process($data, $errors) {
if ( empty( $_REQUEST['g-recaptcha-response'] ) || !(google_recaptcha( $_REQUEST['g-recaptcha-response'] )) ) {
throw new Exception( __( 'Please enter correct captcha.', 'error' ));
}
}

Related

How do I change the Woocommerce checkout page error messages?

I run an e-commerce site and need to change my checkout error messages.
Recently I have had a customer say that they made a mistake entering their credit card's expiration date, but they were confused because the error message displayed by woocommerce says "Your card's security code is incorrect." This led them to double and triple check the security code instead of the expiration date which was the real problem.
I would like to find a solution that allows me to change that error message to say, "Your card's security code or expiration date is incorrect."
I would also be okay with a solution that allows me to input a single generic error message such as, "Some information is incorrect or missing." that would be used for all payment info errors.
Somebody please help me.
I have tried implementing the following solutions from other forums into my functions.php file with no luck:
Attempt 1:
function ShowOneError( $fields, $errors ){
// if their is any validation errors
if( !empty( $errors->get_error_codes() ) ) {
// remove all of Error msg
foreach( $errors->get_error_codes() as $code ) {
$errors->remove( $code );
}
// our custom Error msg
$errors->add('validation','There is an error in filed data.');
}
}
add_action('woocommerce_after_checkout_validation','ShowOneError',999,2);
Attempt 2:
// alter the subscriptions error
function my_woocommerce_add_error( $error ) {
if( 'The generic error message' == $error ) {
$error = 'The shiny brand new error message';
}
return $error;
}
add_filter( 'woocommerce_add_error', 'my_woocommerce_add_error' );
Attempt 3:
add_action( 'woocommerce_after_checkout_validation', 'quadlayers', 9999, 2);
function quadlayers( $fields, $errors ){
// in case any validation errors
if( !empty( $errors->get_error_codes() ) ) {
// omit all existing error messages
foreach( $errors->get_error_codes() as $code ) {
$errors->remove( $code );
}
// display custom single error message
$errors->add( 'validation', 'Your Custom Message Goes Here!!!' );
}
}

How To Get Login Errors on Custom Login Form

I have created a custom login form through Elementor but can't output the error properly, breaks my site.
I have made a bit of PHP code and amended it to try to collect the WP Errors from the WP Login form but it's not working correctly.
//add hook to redirect the user back to the elementor login page if the login failed
add_action( 'wp_login_failed', 'elementor_form_login_fail' );
function elementor_form_login_fail( $username ) {
$referrer = $_SERVER['HTTP_REFERER']; // where did the post submission come from?
// if there's a valid referrer, and it's not the default log-in screen
if ( !empty($referrer) && !strstr($referrer,'wp-login') && !strstr($referrer,'wp-admin') ) {
if (is_wp_error($username)) {
//Login failed, find out why...
$error_types = array_keys($username->errors);
//Error type seems to be empty if none of the fields are filled out
$error_type = 'both_empty';
//Otherwise just get the first error (as far as I know there
//will only ever be one)
if (is_array($error_types) && !empty($error_types)) {
$error_type = $error_types[0];
}
wp_redirect(preg_replace('/\?.*/', '', $referrer) . '?login=error&reason=' . $error_type );
exit;
}
}
I can successfully test the user has logged in and return an error code using a Bootstrap Alert, but i want to do it so i can collect the exact error.
I know the WP login form does this but i want this exact error to display at the top of my custom login, but whenever i add my code in i get an error saying this saite is experiencing technical difficulties.
My Custom Login form is locate at https://www.qbeam.co.uk/login.
I looked at the WordPress objects for WP Errors, and also the WP Authenticate Hook as well and i have managed to stop the user from submitting an empty form, i have checked to see if username and password is blank and not send the form and then i return the error code for the form once it has been submitted correctly.
Add the following code to Functions.php
add_action('init', 'prevent_wp_login');
function prevent_wp_login() {
if(isset($_POST['log']) && isset($_POST['pwd']))
if($_POST['log']=='' && $_POST['pwd']=='')
{
$page = 'https://www.qbeam.co.uk/login/?login=error&reason=blank';
// Redirect to the your url
wp_redirect($page);
exit();
}
else if($_POST['log']=='')
{
$page = 'https://www.qbeam.co.uk/login/?login=error&reason=username';
// Redirect to the your url
wp_redirect($page);
exit();
}
else if($_POST['pwd']=='')
{
$page = 'https://www.qbeam.co.uk/login/?login=error&reason=password';
// Redirect to the your url
wp_redirect($page);
exit();
}
}
add_filter('login_redirect', 'qbeam_login_failed_redirect', 10, 3);
function qbeam_login_failed_redirect($user) {
$referrer = $_SERVER['HTTP_REFERER']; // where did the post submission come from?
// if there's a valid referrer, and it's not the default log-in screen
if ( !empty($referrer) && !strstr($referrer,'wp-login') && !strstr($referrer,'wp-admin') ) {
if (is_wp_error($user)) {
//Login failed, find out why...
$error_types = array_keys($user->errors);
//Get the reason why login failed
if (is_array($error_types) && !empty($error_types)) {
$error_type = $error_types[0];
}
wp_redirect(preg_replace('/\?.*/', '', $referrer) . "?login=failed&reason=" . $error_type );
exit;
}
}
}
function qbeam_login_redirect( $redirect_to, $request, $user ) {
return ( is_array( $user->roles ) && in_array( 'administrator', $user->roles ) ) ? admin_url() : site_url() . '/my-account';
}
add_filter( 'login_redirect', 'qbeam_login_redirect', 10, 3 );
add_action('wp_logout','auto_redirect_external_after_logout');
function auto_redirect_external_after_logout(){
wp_redirect( 'https://www.qbeam.co.uk/login/?loggedout=true' );
exit();
}
Then add something similar to this in your login page where your custom login form is.
<?php
if( "error" == $_GET['login'] && "blank" == $_GET['reason'] )
{
echo
"<div class='alert alert-warning alert-dismissible container' style='margin-top:20px; margin-bottom:20px;'>
<a href='#' class='close' data-dismiss='alert' aria-label='close'>×</a>
<strong>Error!</strong> Form Cannot Be Blank, Please Try Again.
</div>";
}
?>
You then do a banner for every error that you may encounter on your custom login form, and then you can use a custom redirect to stop any non logged in users accessing wp-login, you can use a plugin for this or another bit of code in your Functions.php.
Hope this helps someone else later.

Showing failed wp_remote_post on user registration

When registering user, I am hooking into user_register hook to send some call to the external API.
add_action( 'user_register', 'create_user_on_api' );
inside the create_user_on_api function I am making the call (nonces are there, and tons of security checks, but I'm omitting those for brevity) using wp_remote_post()
function create_user_on_api( $user_id ) {
$user_create_response = wp_remote_post( "someurlgoeshere.api", $curl_args );
if ( is_wp_error( $user_create_response ) ) {
throw new Exception( 'wp error' );
} else {
$code = wp_remote_retrieve_response_code( $user_create_response );
$msg = wp_remote_retrieve_response_message( $user_create_response );
$body = wp_remote_retrieve_body( $user_create_response );
if ( $code !== 200 ) {
throw new Exceprion( 'error' );
}
}
}
This is the gist of it.
The problem is following: using any error handles causes white screen of death, error is logged in my debug.log, and I would like to throw a notice on my admin dashboard that, even though api call failed, the user is created in WP, and you'll have to create it manually on the API or try again.
I tried echoing stuff, custom exception handling mentioned here, but so far no luck.
Any idea how to do that? Can I hook into admin notices in any way?
Ok, found a solution. A hacky one but it works.
On api error I am storing a error message in a transient, which has an expiry of 60 seconds and I created a user_notices function that I hook on admin_notices hook. In it I check two things: if I'm on users screen
$current_screen = get_current_screen();
if ( $current_screen->id === 'users' ) {
}
And then inside this I check if the transients are empty, and then assign them to the $error_message array, which I output in a foreach (in case I have multiple error messages)
if ( ! empty( $error_msg_array ) ) {
foreach ( $error_msg_array as $error_msg_key => $error_msg ) {
?>
<div class="error notice">
<p><?php printf( '<strong>Apigee error:</strong> %s', esc_html( $error_msg ) ); ?></p>
</div>
<?php
}
}
And this seems to be working.

Check if WooCommerce address_field_1 contains house number before processing order

Sometimes, in WooCommerce, the customer is required to fill in street name and house number in a single field.
In this case, we want to then validate the billing_address_1 WooCommerce checkout field to check if it contains numbers before processing the order. We have tried a number of methods to get this done but without any luck.
This standard WooCommerce method doesn't work:
add_action('woocommerce_checkout_process', 'custom_checkout_field_check');
function custom_checkout_field_check() {
// Check if set, if its not set add an error.
if ( $_POST['billing_address_1'] && strpbrk($_POST['billing_address_1'], '1234567890') )
wc_add_notice( __( 'Het adresveld moet minimaal een huisnummer bevatten' ), 'error' );
}
These return bool(false) on the checkout page:
var_dump($_POST['billing_address_1'] == true);
var_dump($_POST['billing_address_2'] == true);
var_dump($_POST['billing_postcode'] == true);
var_dump($_POST['billing_email'] == true);
This front-end workaround doesn't work.
document.querySelector("#place_order").addEventListener("click", validateAddressField);
function validateAddressField () {
console.log('Okay dan!');
}
What else can I try to ensure validation takes place before the order is processed?
// Check if address field contains house number otherwise provide error message
add_action( 'woocommerce_after_checkout_validation', 'validate_checkout', 10, 2);
function validate_checkout( $data, $errors ){
if ( ! preg_match('/[0-9]/', $data[ 'billing_address_1' ] ) ){
$errors->add( 'address', 'Sorry, but the address you provided does not contain a house number.' );
}
}
This isn't working correctly in your code: strpbrk($_POST['billing_address_1'], '1234567890').
he PHP function preg_match() is more appropriate here.
So I have make some little changes in your code to make it work as you expect:
add_action('woocommerce_checkout_process', 'address_field_validation', 10, 0);
function address_field_validation() {
// The field value to check
$post_value = $_POST['billing_address_1'];
// If there is no number in this field value, stops the checkout process
// and displays an error message.
if ( $post_value && ! preg_match( '/[0-9]+/', $post_value ) ) {
// The error message
throw new Exception( sprintf( __( 'Het adresveld moet minimaal een huisnummer bevatten', 'woocommerce' ) ) );
}
}
This code is tested and works on WooCommerce versions 2.6.x and 3.0+…
*This code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Reference: WC_Checkout - process_checkout() source code
You can try using hook :- woocommerce_after_checkout_validation
Please refer how to use this hook and sample code here
Let me know if you need anything else...

Do not submit form after wrong code

I am working on a WordPress plugin which adds an auth code to the login form.
This is the process of checking if the auth code is valid:
add_action( 'wp_authenticate', 'authcode_check', 5 );
function authcode_check($username) {
$options = get_option( 'authcode_settings' );
if(!empty($options['code'])) {
global $wpdb;
if ( !username_exists( $username ) ) {
return;
}
$set_code = $options['code'];
$submit_code = $_POST['auth_key'];
if(empty($submit_code)) {
add_filter( 'login_errors', function( $error ) {$error = '<strong>ERROR</strong>: Authentication code cannot be empty.';return $error;} );
return;
} elseif ( ! ( $set_code == $submit_code ) ) {
add_filter( 'login_errors', function( $error ) {$error = '<strong>ERROR</strong>: Authentication code is invalid.';return $error;} );
return;
}
}
}
The problem is; when the user enters their WordPress name and password correctly, but not the auth code, the form still submits and log the user in.
I tried return false but that didn't work.
Is there any way to prevent the form from logging the user in when they have entered a wrong auth code?
Working Example
Updated after chat with #J.Doe
We can hook into the login_form hook, to display the input for the authentication code:
/**
* 'Authentication Code' Input
*/
add_action( 'login_form', function()
{
// Fetch the stored code
$options = get_option( 'authcode_settings' );
// Display code input
if( isset( $options['code'] ) )
printf(
'<p class="login-authenticate">
<label for="auth_key">%s</label>
<input type="text" name="so38551606_auth_key" id="so38551606_auth_key"
class="input" value="" size="20" autocomplete="off" />
</p>',
esc_html__( 'Authentication Code', 'mydomain' )
);
} );
You can hook into the authenticate filter, within the wp_authenticate() function, for the validating part:
/**
* Validate 'Authentication Code' Input
*/
add_filter( 'authenticate', function( $user )
{
// Fetch stored code value
$options = get_option( 'authcode_settings' );
// Nothing to do if there's no stored code value
if( ! isset( $options['code'] ) )
return $user;
// Fetch the user's code input
$submit_code = isset( $_POST['so38551606_auth_key'] )
? $_POST['so38551606_auth_key']
: null;
// Validation's logic
$is_valid_auth_code = ! is_null( $submit_code )
&& ( $options['code'] === $submit_code );
// Valid auth code
if( $is_valid_auth_code )
return $user;
// Add an unvalid auth code error
if( is_wp_error( $user ) )
$user->add(
'invalid_auth_code',
sprintf(
'<strong>%s</strong>: %s',
esc_html__( 'ERROR', 'mydomain' ),
esc_html__( 'Authentication code is invalid.', 'mydomain' )
)
);
// Create a new auth code error
else
$user = new WP_Error(
'invalid_auth_code',
sprintf(
'<strong>%s</strong>: %s',
esc_html__( 'ERROR', 'mydomain' ),
esc_html__( 'Authentication code is invalid.', 'mydomain' )
)
);
return $user;
}, 100 );
Here we use a priority of 100 because we want to run it after the default callbacks of:
add_filter( 'authenticate', 'wp_authenticate_username_password', 20, 3 );
add_filter( 'authenticate', 'wp_authenticate_email_password', 20, 3 );
add_filter( 'authenticate', 'wp_authenticate_spam_check', 99 );
We prefixed the POST variable with so38551606_ to avoid possible name collision.
Output example:
Instead of return false use return as apparently WordPress prefers it that way, look at the existing username code it just returns, not false, not true, just return
Also place the add filter functions before the return statements as a return signifies that the code stops running at that point, nothing below it is executed anymore, so your filters won't appear unless you move them
Can you prove the user isn't being logged in? Or are you just assuming they are being logged in because the error messages aren't being shown?
Anything after the return keyword won't be run. So in your if statements, you have two return false;s if the auth key doesn't match and then you are calling a function afterwards. This function won't be run so the error code won't be displayed.
What you can do however, is move the function above the return keyword. It will then display the error and return false.

Categories