Hooking After Validation but Before Order Create in Woocommerce Checkout - php

I am trying to create a step in checkout to confirm your order. I'm thinking when the place order button is clicked AND the checkout fields are valid I could run some JS to show a modal or whatever.
Is there a JS trigger/event similar to checkout_place_order that runs after validation? For example, I can use the following but it happens before validation. Maybe there is a way to trigger validation from inside there and display my modal based off that?
var checkout_form = $('form.checkout');
checkout_form.on('checkout_place_order', function () {
// do your custom stuff
return true; // continue to validation and place order
return false; // doesn't validate or place order
});
There is also the woocommerce_after_checkout_validation hook but I am not sure how to utilize it to achieve what I'm after.
I am open to ideas...

I was able to figure this out finally, Its more of a workaround since I don't think there is a clear way to do this.
As soon as the "Place Order" button is clicked, we use the checkout_place_order event to place a hidden field with a value set to 1.
var checkout_form = $('form.checkout');
checkout_form.on('checkout_place_order', function () {
if ($('#confirm-order-flag').length == 0) {
checkout_form.append('<input type="hidden" id="confirm-order-flag" name="confirm-order-flag" value="1">');
}
return true;
});
Next, we use the hook woocommerce_after_checkout_validation to check our hidden input and if the value is 1 add in error (This stops the order from going through).
function add_fake_error($posted) {
if ($_POST['confirm-order-flag'] == "1") {
wc_add_notice( __( "custom_notice", 'fake_error' ), 'error');
}
}
add_action('woocommerce_after_checkout_validation', 'add_fake_error');
Last, we use the checkout_error event to determine if there was a real validation or if if there is only 1 error, the error we added. If there is only 1 error it means validation passed so we can show our modal (or whatever you need to do).
$(document.body).on('checkout_error', function () {
var error_count = $('.woocommerce-error li').length;
if (error_count == 1) { // Validation Passed (Just the Fake Error I Created Exists)
// Show Confirmation Modal or Whatever
}else{ // Validation Failed (Real Errors Exists, Remove the Fake One)
$('.woocommerce-error li').each(function(){
var error_text = $(this).text();
if (error_text == 'custom_notice'){
$(this).css('display', 'none');
}
});
}
});
Inside my modal I have a confirm button that sets our hidden field value to nothing and clicks the place order button again. This time the order will go through because we are checking for the hidden input value of 1.
$('#confirm-order-button').click(function () {
$('#confirm-order-flag').val('');
$('#place_order').trigger('click');
});

As far as I know, there is no hooks in between validation and order creation process, that will allow you to interact with customer, making some actions.
Using jQuery and Sweet Alert component (SWAL 2), here is an example of code that will disable the "Place Order" button displaying a Sweet Alert with confirmation buttons. It's not perfect, but it answers partially your question.
Once customer will confirm, the "Place Order" button will be enabled back and it will be triggered by the code… If the customer use the cancel button, Checkout review order will be refreshed (Ajax).
The code:
add_action( 'wp_footer', 'checkout_place_order_script' );
function checkout_place_order_script() {
// Only checkout page
if( is_checkout() && ! is_wc_endpoint_url() ):
// jQuery code start below
?>
<script src="https://unpkg.com/sweetalert2#8.8.1/dist/sweetalert2.all.min.js"></script>
<script src="https://unpkg.com/promise-polyfill#8.1.0/dist/polyfill.min.js"></script>
jQuery( function($){
var fc = 'form.checkout',
pl = 'button[type="submit"][name="woocommerce_checkout_place_order"]';
$(fc).on( 'click', pl, function(e){
e.preventDefault(); // Disable "Place Order" button
// Sweet alert 2
swal({
title: 'Are you sure?',
text: "You are about proceed the order",
type: 'success',
showCancelButton: true,
confirmButtonColor: '#3085d6',
cancelButtonColor: '#d33',
confirmButtonText: "Yes let's go!"
}).then((result) => {
if (result.value) {
$(fc).off(); // Enable back "Place Order button
$(pl).trigger('click'); // Trigger submit
} else {
$('body').trigger('update_checkout'); // Refresh "Checkout review"
}
});
});
});
</script>
<?php
endif;
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.

I'm late to the party, but wanted to share a variation of the answers above
jQuery(document).ready(function($){
/* on submit */
$('form#FORMID').submit( function(e) {
/* stop submit */
e.preventDefault();
/* count validation errors */
var error_count = $('.woocommerce-error li').length;
/* see if terms and conditions are accepted */
var terms = $('input#terms').is(':checked');
/* if there are no validation errors and terms are accepted*/
if( error_count == 0 && terms ) {
/* trigger confirmation dialogue */
if( confirm('Are you sure?') ){
/* resume default submit */
$(this).unbind('submit').submit();
} else {
/* do nothing */
e.stopPropagation();
}
}
});
});

I got a fast and simple JS decision for myself in this case (there were woocommerce and stripe forms). That is based on preventing the checkout button from submit but still makes forms verifications.
// making the wrap click event on dinamic element
$('body').on('click', 'button#place_order_wrap', function(event) {
// main interval where all things will be
var validatoins = setInterval(function(){ happen
// checking for errors
if(no_errors==0){
// making the setTimeout() function with limited time like 200ms
// to limit checkout function for just make verification
setTimeout(function(){
// triggering original button
$('button#place_order').click();
// if there some errors, stop the interval, return false
if(($('element').find('ul.woocommerce_error').length!==0)||($('element').find('.woocommerce-invalid-required-field').length!==0)){
clearInterval(validatoins);
return false;
}else{
no_errors=1;
}
}, 200);
}
if(no_errors==1){
// same error checking
if($('#step5').find('ul.woocommerce_error').length!=0||($('#step5').find('.woocommerce-invalid-required-field').length!==0)){
// if there some errors, stop the interval, return false
clearInterval(validatoins);
return false;
}
setTimeout(function(){
// if no errors
if(($('#step5').find('ul.woocommerce_error').length==0)&&($('#step5').find('.woocommerce-invalid-required-field').length==0)){
// do something, mark that finished
return false;
}
}, 1000);
// if something finished
if() {
setTimeout(function(){
// trigger original checkout click
$('button#place_order').click();
clearInterval(validatoins);
}, 1000);
}
}
}, 1000);
}

Related

WooCommerce updating the shipping method depending on the billing address field changes

In my online store, there are two standard shipping methods - Flat Rate and Free Delivery. I added a plugin for distance delivery.
Thus, when a customer fills in the City and Address fields when placing an order, new shipping methods must be added. But new deliveries are not visible until I select Flat Rate or Free Delivery.
As I understand it, I do not have automatic updating of shipping methods depending on the filling of the fields.
I found and edited this code:
add_action('wp_footer', 'woocommerce_custom_update_checkout', 50);
function woocommerce_custom_update_checkout() {
if (is_checkout()) {
?>
<script type="text/javascript">
jQuery( document ).ready(function( $ ) {
$('#billing_address_1').click(function(){
jQuery('body').trigger('update_checkout', { update_shipping_method: true });
});
});
</script>
<?php
}
}
But until I click on the filled field a second time, the delivery method is not updated.
I want to connect with AJAX. How can I edit the code so that the result of using AJAX is visible immediately without clicking on the filled field again?
Currently you have to click on the billing_address_1 field in order to trigger the event listener and update your fields, because your code says so!
There are multiple ways to solve the issue. For example, instead of listening for a click event, you could add a different event listener.
To start off, you could listen for an on change event. This will happen when the value of the address field has been changed and the user clicked/tabbed out of the billing_address_1 field:
add_action('wp_footer', 'woocommerce_custom_update_checkout', 50);
function woocommerce_custom_update_checkout()
{
if (is_checkout()) {
?>
<script type="text/javascript">
jQuery(document).ready($ => {
$('#billing_address_1').on('change', () => {
$('body').trigger('update_checkout', {
update_shipping_method: true
});
});
});
</script>
<?php
}
}
Another event listener you could use here is input event listener. This will happen every time the value of billing_address_1 field is being changed. This will fire off even with a press of the space key, backspace key etc.
add_action('wp_footer', 'woocommerce_custom_update_checkout', 50);
function woocommerce_custom_update_checkout()
{
if (is_checkout()) {
?>
<script type="text/javascript">
jQuery(document).ready($ => {
$('#billing_address_1').on('input', () => {
$('body').trigger('update_checkout', {
update_shipping_method: true
});
});
});
</script>
<?php
}
}
Another event that could be helpful here is on blur event. This event will fire off when the user clicks/tabs out of the billing_address_1 field. The difference between this event and on change event is that when you listen for this event, update will happen even when the value of the billing_address_1 field has not been changed.
add_action('wp_footer', 'woocommerce_custom_update_checkout', 50);
function woocommerce_custom_update_checkout()
{
if (is_checkout()) {
?>
<script type="text/javascript">
jQuery(document).ready($ => {
$('#billing_address_1').on('blur', () => {
$('body').trigger('update_checkout', {
update_shipping_method: true
});
});
});
</script>
<?php
}
}
Now depending on how you'd like to structure your code and the logic behind it, you could use these events at the same time:
add_action('wp_footer', 'woocommerce_custom_update_checkout', 50);
function woocommerce_custom_update_checkout()
{
if (is_checkout()) {
?>
<script type="text/javascript">
jQuery(document).ready($ => {
$('#billing_address_1').on('change input blur', () => {
$('body').trigger('update_checkout', {
update_shipping_method: true
});
});
});
</script>
<?php
}
}
How can I edit the code so that the result of using AJAX is visible immediately without clicking on the filled field again?
I think the last solution is what you're looking for! Using all of those event listeners together will make sure that your shipping method is getting updated constantly.

Yii2 - warning message before submit

I have a form with two fields "a" and "b" when in action update if the "b" field changes the "beforeSubmit" event a Modal Bootstrap alert is sent to the user without any button OK or CANCEL, only information during 5 seconds, after this time automatically save if the the "b" field were actually changed, if not change save whitout alert modal windows.
How do I send this condition from the controller to view where I have the javascript?
maybe with ajax? but how?
Controller.php
public function actionUpdate()
{
$model = new Faqs();
if ($model->load(Yii::$app->request->post()) && $model->save()) {
if ($model->oldAttributes["b"] != $model->b){
sleep(5);
}
return $this->redirect(['view', 'id' => $model->id]);
} else {
return $this->render('update', [
'model' => $model,
]);
}
}
_form.php
$('#form').on('beforeSubmit', function(e) {
if(old_B_attribute != current_B_attribute){ //example
$('#modal').modal('show');
}
});
You want to prompt the user if the attribute values were actually changed before submitting the form.
How I would go for this
Create a separate action in my controller actionAttributeDirty() which would validate if the selected attribute was actually changed.
Then, use a normal Html::button() rather than a Html::submitButton() for the form.
Add a hidden field to hold the current records id in the form.
Bind click event to the button which will send an ajax call to actionAttributeDirty() with the id of the current record.
Then use the success function to display the modal window and use setTimeout with $.yiiActiveForm('submitForm') to trigger the form submission after 5 seconds.
So in the similar order given above,
actionAttributeDirty
public function actionAttributeDirty()
{
Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
$id = Yii::$app->request->post('id');
$model = Faqs::findOne($id);
$response = ['changed' => false];
$isValidRequest = Yii::$app->request->isAjax && Yii::$app->request->isPost;
if ($isValidRequest && $model->load(Yii::$app->request->post())) {
//get the name of the fields from the dirty attributes
$changedAttributes = array_keys($model->getDirtyAttributes());
//if the attribute name exists in the dirty attributes
if (!empty($changedAttributes) && in_array("b", $changedAttributes)) {
$response['changed'] = true;
}
}
return $response;
}
Your form should have the following buttons along with other fields,
$form = \yii\widgets\ActiveForm::begin(['id' => 'form']);
echo \yii\helpers\Html::hiddenInput('id', $model->id);
echo \yii\helper\Html::button('save', ['id' => 'save-now']);
\yii\widgets\ActiveForm::end();
click Event for the Button
Add the following on the top of your view where you have the form.
Note: change the url of the ajax call '/site/attribute-dirty' accordingly where you copy the actionAttributeDirty() i assume you copy it inside the site controller.
$js = <<< JS
$('#save-now').on('click', function(e) {
e.preventDefault();
let form = $("#form");
$.ajax({
url:'/site/attribute-dirty',
method:'post',
data:form.serialize(),
}).done(function(response){
if(response.changed){
$('#modal').modal('show');
setTimeout(function(){form.yiiActiveForm('submitForm');}
, 5000);
}else{
form.yiiActiveForm('submitForm');
}
}).fail(function(response){
console.log(response.responseText);
});
});
JS;
$this->registerJs($js, \yii\web\View::POS_READY);
EDIT
Pressing Enter button will not submit the form anyhow as there is no submit button, If you want Enter button to submit the form you should add the following along with the script too on the top of the view which will trigger the click event of the save-now button whenever the Enter button is pressed within any input.
$("#form").on('keypress','input',function(e){
e.preventDefault();
if(e.keyCode===13){
$("#save-now").trigger('click');
}
});
your request can not be done on the client side by beforeSubmit.Because you have to decide on the server side.
On the client side you can use
$(document).on("beforeValidate", "form", function(event, messages, deferreds) {
// #code
// console.log('BEFORE VALIDATE TEST');
}).on("afterValidate", "form", function(event, messages, errorAttributes) {
// console.log('AFTER VALIDATE TEST');
//#code
});
Then decide in the rules method.
On the server side, you can also decide on the following events:(For what you want)
 beforeValidate, afterValidate,beforeSave,afterSave,...
if you want show confirmation modal i use as below. you can change as your needs show or hide submit after x seconds
$(function() {
submitting = false;
});
$("form").on("beforeSubmit", function (event, messages, errorAttributes) {
if (typeof(errorAttributes) === "undefined" || errorAttributes.length === 0) {
$('#modal-confirm').modal('show');
return submitting;
}
});
var submit = function() {
submitting = true;
$("form").yiiActiveForm('submitForm');
}
in modal submit
<button type="button" onclick="submit();">Confirm</button>

Woocommerce Payment Method Detection in Checkout Page

I am using woocommerce plugin and braintree extension of woocommerce for payment. I have enabled both card and paypal payment of woocommerce braintree to checkout. I am trying to figure out how to know which payment gateway the user selects before user actually checkouts and pays. Any hooks under woocommerce or braintree to find either credit card radio button or paypal payment radio button is checked for payment.
However i know we can detect the gateway used for the particular order after successful payment but i want the selected gateway information before payment completes within checkout page. Any Help?
You can detect chosen Payment Method with some basic JavaScript on checkout page and run your custom code with PHP by hooking into woocommerce_checkout_update_order_review action.
First,
you also should place JS code on checkout page, checkout template or in header/footer of your theme, so you can detect when the user has changed payment method option and run your own code after that.
JS code:
jQuery(document).ready( function() {
jQuery( "#payment_method_bacs" ).on( "click", function() {
jQuery( 'body' ).trigger( 'update_checkout' );
});
jQuery( "#payment_method_paypal" ).on( "click", function() {
jQuery(document.body).trigger("update_checkout");
});
jQuery( "#payment_method_stripe" ).on( "click", function() {
jQuery(document.body).trigger("update_checkout");
});
});
Notice that for each payment method you have active you should add 'Click' event. It gives you option to fine tune when your custom code is triggered.
To prevent click event to run ONLY ONCE you should add next block of JS code below first one.
jQuery( document ).ajaxStop(function() {
jQuery( "#payment_method_bacs" ).on( "click", function() {
jQuery(document.body).trigger("update_checkout");
});
jQuery( "#payment_method_paypal" ).on( "click", function() {
jQuery(document.body).trigger("update_checkout");
});
jQuery( "#payment_method_stripe" ).on( "click", function() {
jQuery(document.body).trigger("update_checkout");
});
});
It's the same code only that is triggered after ajax.
In both JS blocks of code add your payment options that you are actually use.
After that you put your custom PHP code that hooks into checkout like this:
if ( ! function_exists( 'name_of_your_function' ) ) :
function name_of_your_function( $posted_data) {
// Your code goes here
}
endif;
add_action('woocommerce_checkout_update_order_review', 'name_of_your_function');
This code can be placed in functions.php.
Here is complete PHP code that detects and run when specific payment option is chosen on checkout page:
function name_of_your_function( $posted_data) {
global $woocommerce;
// Parsing posted data on checkout
$post = array();
$vars = explode('&', $posted_data);
foreach ($vars as $k => $value){
$v = explode('=', urldecode($value));
$post[$v[0]] = $v[1];
}
// Here we collect payment method
$payment_method = $post['payment_method'];
// Run code custom code for each specific payment option selected
if ($payment_method == "paypal") {
// Your code goes here
}
elseif ($payment_method == "bacs") {
// Your code goes here
}
elseif ($payment_method == "stripe") {
// Your code goes here
}
}
add_action('woocommerce_checkout_update_order_review', 'name_of_your_function');
I hope this helps!
This is a very powerful option to run all your custom logic on the checkout page!

PHP calling a function before session destory or garabge collection

Im attempting to log the user activity in my database when they login and logout of the site. This works fine, but when they close the page via a tab or browser, there is no way to run the logout query, so according to the records the user never logs out of the site.
Method 1:
I have tried onbeforeunload, but this does not seem to trigger before the page closes.
Method 2:
I would try the method of using an ajax keepalive token sent to the php, but this would need to run every 1 min and could cause high traffic load.
Method 3:
I was hoping an alterantive would be to set the session.gc.maxlifetime to 1 min and add a call to the logout query via a destroy session callback.
Which would be the best method, or is there a better method of achieving this?
Is there a way to trigger a custom function(query) before php destroy session or php garbage collection has taken place?
UPDATE
I have taken the advise of everyone and decided to attempt 'method 1'. So far this is my attempt but its still not working perfectly:
var isClosePage = true;
//detect f5 and backspace page navigation
$(document).on('keypress', function(e)
{
if (e.keyCode == 116)
{
alert('f5');
isClosePage = false;
}
if (e.keyCode == 8)
{
alert('backspace');
isClosePage = false;
}
});
//detect back and forward buttons
$(window).bind('statechange',function()
{
alert('back');
isClosePage = false;
});
//detect page button press
$('html').on('mouseenter', function()
{
console.log('mouse has enetered!');
isClosePage = false;
});
//detect browser buttons press
$('html').on('mouseleave', function()
{
console.log('mouse has left!');
isClosePage = true;
});
//make ajax call (logout) to server if above events not triggered
$(window).on('beforeunload', function(e)
{
if(isClosePage)
{
$.ajax(
{
url:'php/function/active-user.php?logout=ajax',
dataType: 'jsonp',
crossDomain: true,
async: false
});
//return 'some default message';
}
else
{
isClosePage = true;
}
});
Can anyone suggest any improvments to how I can make this work well?
When you speak of Method 1, you're doing it client-side, right?
The interface is window.onbeforeunload, and this is a usage example:
window.onbeforeunload = function(e) {
// call server script to log the user activity to database ...
};
The event window.onbeforeunload IS triggered before page is unloaded (tab/window/browser closed).
I would like to extend MarcoS's answer a little bit. window.onbeforeunload should work but this isn't the preferred way to set a handler for events. Since this is a property other scripts attaching to this event will overwrite your listener and your function will never get called.
The preferred way of registering to this event is as follows:
window.addEventListener('beforeunload', function (evt) {
}, false);
You could also attach a event listener to unload
window.addEventListener('unload', function (evt) {
}, false);
Hope this helps

jQuery is not redirecting after AJAX call

I'm validating a form using jQuery and against a database using PHP-MySQL. The AJAX call is successful but the current page(A) is not redirected to page(B) after successful validation. But if i refresh the page, the page(B) is loaded. That is, the call is successful but jQuery is not redirecting.
<script language="javascript">
$(document).ready(function()
{
$("#login_form").submit(function()
{
//remove all the class add the messagebox classes and start fading
$("#msg").text('Checking....').fadeIn(1000);
//check the username exists or not from ajax
$.post("signin_check.php",{ loginid:$('#username').val(),pswd:$('#password').val() }, function(data)
{
if(data=='yes') //if correct login detail
{
$("#msg").fadeTo(200,0.1,function() //start fading the messagebox
{
//add message and change the class of the box and start fading
$(this).text('Logging in.....').fadeTo(900,1,
function(){
//redirect to secure page
document.location = "/pawn/selectdomain.php";
});
});
}
else
{
$("#msg").fadeTo(200,0.1,function() //start fading the messagebox
{
//add message and change the class of the box and start fading
$(this).text('Incorrect login details !!').fadeTo(900,1);
});
}
});
return false; //not to post the form physically
});
//now call the ajax also focus move from
$("#password").blur(function()
{
$("#login_form").trigger('submit');
});
});
</script>
I also tried replacing document.location with window.location and window.location.href and window.location.replace(....) but even they dont work. Someone please help.
the problem must be one of these 2 or both:
1) The data that ajax returns is not equal to 'yes'
2)ajax is not successful and it has error
please check that if the ajax is successful and then return value is equal to 'yes'

Categories