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!
Related
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.
Through this function I can change the text on the pay button.
I would like to insert a small image instead.
add_filter( 'woocommerce_available_payment_gateways', 'woocommerce_available_payment_gateways' );
function woocommerce_available_payment_gateways( $available_gateways ) {
if (! is_checkout() ) return $available_gateways; // stop doing anything if we're not on checkout page.
if (array_key_exists('paypal_express',$available_gateways)) {
// Gateway ID for Paypal is 'paypal'.
$available_gateways['paypal_express']->order_button_text = __( 'PAY', 'woocommerce' );
}
return $available_gateways;
}
Thanks in advance for the help!
As in the frontend the button text will be changed by using .text() function of jQuery, adding html like img tag won't work here.
I suggest instead though to you use javascript with jQuery. It should be something like this:
$( document.body ).on( 'payment_method_selected', function(){
var selectedPaymentMethod = $( '.woocommerce-checkout input[name="payment_method"]:checked' ).attr( 'id' );
$( '#place_order' ).find('.payment-icon');
$( '#place_order' ).prepend('<span class="payment-icon '+ selectedPaymentMethod +'"></span>'); // or any element like from font-awesome.
});
$( document.body ).trigger( 'payment_method_selected' ); // this will trigger on page load, act as initialize the icon.
The script above will add a span tag with classes payment-icon and an ID of the selected payment method. You can then use css to add your icon as background in this span.
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);
}
I'm trying to show/hide some elements from my checkout page, based on the chosen shipping method. The page elements I'm trying to show/hide come from another plugin, so I tried to change the display properties for them. I've looked to many thread like:
Show or hide checkout fields based on shipping method in Woocommerce 3
But it's for checkout fields, and I'm not sure how to do it for page elements.
Then based on this answer thread Here's my code so far:
add_action( 'wp_footer', 'conditionally_hidding_order_delivery_date' );
function conditionally_hidding_order_delivery_date(){
// Only on checkout page
if( ! is_checkout() ) return;
// HERE your shipping methods rate ID "Home delivery"
$home_delivery = 'distance_rate_shipping';
?>
<script>
jQuery(function($){
// Choosen shipping method selectors slug
var shipMethod = 'input[name^="shipping_method"]',
shipMethodChecked = shipMethod+':checked';
// Function that shows or hide imput select fields
function showHide( actionToDo='show', selector='' ){
if( actionToDo == 'show' )
$(selector).show( 200, function(){
$(this).addClass("validate-required");
});
else
$(selector).hide( 200, function(){
$(this).removeClass("validate-required");
});
$(selector).removeClass("woocommerce-validated");
$(selector).removeClass("woocommerce-invalid woocommerce-invalid-required-field");
}
// Initialising: Hide if choosen shipping method is "Home delivery"
if( $(shipMethodChecked).val() != '<?php echo $home_delivery; ?>' ) {
showHide('hide','#e_deliverydate_field' );
showHide('hide','#time_slot_field' );
}
// Live event (When shipping method is changed)
$( 'form.checkout' ).on( 'change', shipMethod, function() {
if( $(shipMethodChecked).val() == '<?php echo $home_delivery; ?>' ) {
showHide('show','#e_deliverydate_field' );
showHide('show','#time_slot_field' );
}
else {
showHide('hide','#e_deliverydate_field' );
showHide('hide','#time_slot_field' );
}
});
});
</script>
<?php
}
But it's not working completely (the initializing function is not working.)
Any help is very much appreciated.
Additional edit:
<p class="form-row form-row-wide validate-required" id="e_deliverydate_field" data-priority="" style="display: block;"><label for="e_deliverydate" class="">Date<abbr class="required" title="required">*</abbr></label><span class="woocommerce-input-wrapper"><input class="input-text hasDatepicker" name="e_deliverydate" id="e_deliverydate" placeholder="Choose a Date" value="" style="cursor:text !important;" type="text"></span></p>
My objective is to change the display properties for p element from block to none, and vice versa.
The needed code is something much more simple, than the one used in my other answers, but you need to be sure that the targeted chosen shipping method is 'distance_rate_shipping' as I can't test it.
For initialization problem, see the solution exposed at the end of my answer.
The simplified needed code:
// Embedded jQuery script
add_action( 'wp_footer', 'checkout_delivery_date_script' );
function checkout_delivery_date_script() {
// Only checkout page
if( ! ( is_checkout() && ! is_wc_endpoint_url() ) ) return;
?>
<script type="text/javascript">
jQuery( function($){
var a = 'input[name^="shipping_method"]', b = a+':checked',
c = 'distance_rate_shipping',
d = '#e_deliverydate_field,#time_slot_field';
// Utility function that show or hide the delivery date
function showHideDeliveryDate(){
if( $(b).val() == c )
$(d).show();
else
$(d).hide('fast');
console.log('Chosen shipping method: '+$(b).val()); // <== Just for testing (to be removed)
}
// 1. On start
showHideDeliveryDate();
// 2. On live event "change" of chosen shipping method
$('form.checkout').on('change', a, function(){
showHideDeliveryDate();
});
});
</script>
<?php
}
Code goes in function.php file of your active child theme (or theme). It should work.
The initialization problem:
It's certainly because the plugin you are using that generates the delivery date output is delayed after initialization
The solution can be to add some delay on initialization execution.
So should try to replace this:
// 1. On start
showHideDeliveryDate();
By the following in my code (adjusting the execution delay to something higher or lower than 500):
// 1. On start
setTimeout(function(){
showHideDeliveryDate();
}, 500);
I am wondering if someone can help me with the following code. I am trying to set some conditional html dependent on which payment method the user selects on checkout. On the payment method page I am using the following function to update the payment method information:
function cart_update_script() {
if (is_checkout()) :
?>
<script>
jQuery( function( $ ) {
// woocommerce_params is required to continue, ensure the object exists
if ( typeof woocommerce_params === 'undefined' ) {
return false;
}
$checkout_form = $( 'form.checkout' );
$checkout_form.on( 'change', 'input[name="payment_method"]', function() {
$checkout_form.trigger( 'update' );
});
});
</script>
<?php
endif;
}
add_action( 'wp_footer', 'cart_update_script', 999 );
On the final checkout page I am then getting that selected payment method with this bit of code:
<?php $chosen_gateway = WC()->session->chosen_payment_method; ?>
Then I am using conditional statements like the following:
<?php if ( $chosen_gateway == 'stripe' ) { ?>
//do this
<?php } ?>
My problem is that I am not getting dynamic information when the user changes payment method. Sometimes the code works but sometimes it still thinks a different payment method was selected. Any help would be great!