Ask your WordPress questions! Pay money and get answers fast! (more info)

Move Payment Method on WooCommerce Checkout Page

  • SOLVED

I am using the [[LINK href="https://woocommerce.com/products/stripe/"]]Stripe plugin[[/LINK]] for WooCommerce, and I am attempting to move the Payment Methods on the Checkout page. However, I noticed that the javascript is not being called correctly.

I was able to successfully move the Payment Methods above and outside of the <em>#order-review</em> div the on the Checkout page. I followed the instructions that were used to move the Shipping Rates from [[LINK href="https://websitesdepot.com/moving-shipping-rates-woocommerce-checkout"]]here[[/LINK]].

It works well, however, I noticed the following issues:

1. When using an invalid credit card number, the credit card form still submits the payment.
2. The credit card form is visible even if "Use a New Payment Method" is not selected.
3. The input fields of the credit card number and expiration date are not being formatted corrected (e.g., The credit card number displays 4242424242424242 vs. 4242 4242 4242 4242).
4. The css style for the credit card name is not appending to the input field (e.g., The css class "visa" is not appended to the input when entering a credit card number that beings with "4").

I am looking for a solution that does not change the legacy or plugin files directly, but uses an action or filter hook to get the javascript to work correctly.

Here's the code I am using in my <em>functions.php</em>:

function boutique_woo_checkout_payment_split( $payment_fragments ) {
ob_start();
boutique_woo_checkout_payment_template();
$boutique_woo_checkout_payment_template = ob_get_clean();
$payment_fragments['.payment-table'] = $boutique_woo_checkout_payment_template;
return $payment_fragments;
}
add_filter( 'woocommerce_update_order_review_fragments', 'boutique_woo_checkout_payment_split', 10, 1 );

function boutique_woo_checkout_payment_template() {
if ( WC()->cart->needs_payment() ) {
$available_gateways = WC()->payment_gateways()->get_available_payment_gateways();
WC()->payment_gateways()->set_current_gateway( $available_gateways );
} else {
$available_gateways = array();
}
wc_get_template( 'checkout/payment-order-review.php', array(
'checkout' => WC()->checkout(),
'available_gateways' => $available_gateways
) );
}

function boutique_woo_checkout_payment_table() {
echo '<div class="payment-table"></div>';
}
add_action( 'woocommerce_checkout_after_customer_details', 'boutique_woo_checkout_payment_table', 5 );


Here's the code I am using in <em>payment-order-review.php</em>:

<?php
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
?>

<?php if ( WC()->cart->needs_payment() ) : ?>
<ul class="wc_payment_methods payment_methods methods">
<?php
if ( ! empty( $available_gateways ) ) {
foreach ( $available_gateways as $gateway ) {
wc_get_template( 'checkout/payment-method.php', array( 'gateway' => $gateway ) );
}
} else {
echo '<li>' . apply_filters( 'woocommerce_no_available_payment_methods_message', WC()->customer->get_country() ? __( 'Sorry, it seems that there are no available payment methods for your state. Please contact us if you require assistance or wish to make alternate arrangements.', 'woocommerce' ) : __( 'Please fill in your details above to see available payment methods.', 'woocommerce' ) ) . '</li>';
}
?>
</ul>
<?php endif; ?>

Answers (2)

2016-08-18

Reigel Gallarde answers:

can you check if you have a javascript error in your console? or do you have a link for us to see?


thoughtwool comments:

<blockquote>Can you check if you have a javascript error in your console? or do you have a link for us to see?</blockquote>

There are no javascript errors. I am developing locally.


Reigel Gallarde comments:

Okay, so your goal is to move Payment Method above the "YOUR ORDER" location?

Are you using the latest version of woocommerce?


thoughtwool comments:

That's correct, I attached an image that shows the current layout. Yes, I am using the latest version of WooCommerce.


thoughtwool comments:

I just checked which javascript files are being loaded on the checkout page, and it is missing the following two:

1. credit-card-form.min.js
2. tokenization-form.min.js


Reigel Gallarde comments:

here's how I tried to get your screenshot...

remove_action( 'woocommerce_checkout_order_review', 'woocommerce_checkout_payment', 20 );
add_action( 'woocommerce_checkout_after_customer_details', 'woocommerce_checkout_payment', 30 );
add_action( 'woocommerce_checkout_order_review', 'reigel_checkout_payment', 30 );
function reigel_checkout_payment(){
$order_button_text = apply_filters( 'woocommerce_woocommerce_checkout_after_customer_detailsorder_button_text', __( 'Place order', 'woocommerce' ) );
?>
<div class="form-row place-order">
<noscript>
<?php _e( 'Since your browser does not support JavaScript, or it is disabled, please ensure you click the <em>Update Totals</em> button before placing your order. You may be charged more than the amount stated above if you fail to do so.', 'woocommerce' ); ?>
<br/><input type="submit" class="button alt" name="woocommerce_checkout_update_totals" value="<?php esc_attr_e( 'Update totals', 'woocommerce' ); ?>" />
</noscript>

<?php wc_get_template( 'checkout/terms.php' ); ?>

<?php do_action( 'woocommerce_review_order_before_submit' ); ?>

<?php echo apply_filters( 'woocommerce_order_button_html', '<input type="submit" class="button alt" name="woocommerce_checkout_place_order" id="place_order" value="' . esc_attr( $order_button_text ) . '" data-value="' . esc_attr( $order_button_text ) . '" />' ); ?>

<?php do_action( 'woocommerce_review_order_after_submit' ); ?>

<?php wp_nonce_field( 'woocommerce-process_checkout' ); ?>
</div>
<?php
}

add_action( 'wp_footer', 'reigel_print_js', 35 );

function reigel_print_js(){
?>
<style>
#payment .place-order {
display: none;
}
</style>
<script>
jQuery(function($) {
$(document.body).on('change','#payment [name=payment_method]',function (){
if ( $( this ).data( 'order_button_text' ) ) {
$( '.place-order [name=woocommerce_checkout_place_order]' ).val( $( this ).data( 'order_button_text' ) );
} else {
$( '.place-order [name=woocommerce_checkout_place_order]' ).val( $( '#place_order' ).data( 'value' ) );
}
});
});
</script>
<?php
}


the idea was to just change the frontend display... not much changing any code to avoid conflict.
so I move the `woocommerce_checkout_payment` hook before the "YOUR ORDER".
then hide the "Place Order" button, and re-create it down below the form.

let me know if this works for you...


Reigel Gallarde comments:

I forgot to mention that this was tested on a default template of woocommerce...


thoughtwool comments:

This is a simple and effective solution, and it appears to resolve the issues #2-5. The only issue I noticed is that when I use an invalid credit card number, the correct error message is displayed, however, the checkout form still submits the payment.


Reigel Gallarde comments:

can you check if that issue is with the code we did?


thoughtwool comments:

Yes, it is caused by the code. When I remove the code, the credit card form works correctly.


Reigel Gallarde comments:

can you comment this out or remove this line to check?

add_action( 'wp_footer', 'reigel_print_js', 35 );

this line is hiding the original "Place Order" button.

try using the original button found above "YOUR ORDER" to see if the problem is within our created button..
I'm suspecting it is... I will look into a solution if it is...


thoughtwool comments:

I commented out <em>add_action( 'wp_footer', 'reigel_print_js', 35 );</em>, and the checkout form still submits when using an invalid credit card number. This occurs with both the original button and the newly created button.


Reigel Gallarde comments:

Okay... Got it... here's another approach... let's move the table instead...

remove_action( 'woocommerce_checkout_order_review', 'woocommerce_order_review', 10 );
add_action( 'woocommerce_checkout_order_review', 'woocommerce_order_review', 25 );
add_action( 'woocommerce_checkout_order_review', 'reigel_checkout_payment', 30 );
function reigel_checkout_payment(){
$order_button_text = apply_filters( 'woocommerce_woocommerce_checkout_after_customer_detailsorder_button_text', __( 'Place order', 'woocommerce' ) );
?>
<div class="form-row place-order">
<noscript>
<?php _e( 'Since your browser does not support JavaScript, or it is disabled, please ensure you click the <em>Update Totals</em> button before placing your order. You may be charged more than the amount stated above if you fail to do so.', 'woocommerce' ); ?>
<br/><input type="submit" class="button alt" name="woocommerce_checkout_update_totals" value="<?php esc_attr_e( 'Update totals', 'woocommerce' ); ?>" />
</noscript>

<?php wc_get_template( 'checkout/terms.php' ); ?>

<?php do_action( 'woocommerce_review_order_before_submit' ); ?>

<?php echo apply_filters( 'woocommerce_order_button_html', '<input type="submit" class="button alt" name="woocommerce_checkout_place_order" id="place_order" value="' . esc_attr( $order_button_text ) . '" data-value="' . esc_attr( $order_button_text ) . '" />' ); ?>

<?php do_action( 'woocommerce_review_order_after_submit' ); ?>

<?php wp_nonce_field( 'woocommerce-process_checkout' ); ?>
</div>
<?php
}

add_action( 'wp_footer', 'reigel_print_js', 35 );

function reigel_print_js(){
?>
<style>
#payment .place-order {
display: none;
}
</style>
<script>
jQuery(function($) {
$(document.body).on('change','#payment [name=payment_method]',function (){
if ( $( this ).data( 'order_button_text' ) ) {
$( '.place-order [name=woocommerce_checkout_place_order]' ).val( $( this ).data( 'order_button_text' ) );
} else {
$( '.place-order [name=woocommerce_checkout_place_order]' ).val( $( '#place_order' ).data( 'value' ) );
}
});

$('#payment').after($('#order_review_heading'));

});
</script>
<?php
}


thoughtwool comments:

The revised code appears to solve the invalid credit card issue. Just so I have an understanding of what was causing the issue in the first place, can you provide a brief explanation? Is it because the Payments Methods need to be enclosed in the <em>#order_review</em> div?

On a side note, I noticed that since there are 2 input checkboxes for the Terms & Conditions, when the new checkbox is selected, it is actually selecting the original checkbox above. In order to resolve this issue, I simply deleted the original <em><?php wc_get_template( 'checkout/terms.php' ); ?></em> from the <em>payments.php</em> template.


Reigel Gallarde comments:

yes... it's somehow using #order_review div in the js...
then it hits me, the table is just there for display purpose and not doing anything, so why not just move it. :D


thoughtwool comments:

An elegant solution. Thank you.

2016-08-18

Rempty answers:

I suggest you edit the form-checkout.php (copy from woocommerce/template/checkout/ to your theme)

I did a similar modification, i modified the form-checkout.php, and had your problems. the order review is located in this hook
<?php do_action( 'woocommerce_checkout_order_review' ); ?>
You can move this line to any part of the form-checkout.php.

The payment methods are loaded here: do_action( 'woocommerce_checkout_before_order_review' );

But you must check that the do_action( 'woocommerce_checkout_before_order_review' ); is inside the div <div id="order_review" class="woocommerce-checkout-review-order"> (remember that can't have this div id duplicated)
Example:

<div id="order_review" class="woocommerce-checkout-review-order">
<?php do_action( 'woocommerce_checkout_before_order_review' ); ?>
</div>