How to reload checkout page if payment failed in Woocommerce - php

I have a custom payment gateway where I need to reload checkout page if payment failed.
Reason:
When Card details submitted the payment gateway generates a card token which I need to process the payment but we can use the card token only once with a request.
What I need:
Currently, I am just showing the error message and a return when payment is failed.
if($payment_status['status']){
$order->update_status( 'on-hold', __( "ABC Payment Done\n", 'stackoverflow' ) );
wc_reduce_stock_levels($order_id);
WC()->cart->empty_cart();
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order )
);
}else{
wc_add_notice( $payment_status['message'] , 'error' );
return;
}
How can I reload/refresh the page if payment failed? S user can enter the card details again and we can process the payment.
Or any other suggestion?

It depends if you want it sync or async.
Sync way:
header("Refresh:0");
It refreshes your current page. You can specify also the page you want:
header("Refresh:0; url=another_page.php");
Async way:
Generate the content dynamically, clean all changes and tokens.

Fksjii is true.
However, it might be cleaner to have a specified page that generates token code.
With an XHR request you can get it, then submit every data with an XHR and wait for a callback.
However, if you can't edit JS code you might do
if ($failed) {
unset($_REQUEST);
Header("Refresh: 0");
exit;
}
Do not forget to clean $_GET or $_POST data before reloading your page.
It is always very important to exit even after a redirection sent in the headers. Indeed, headers are sent but code continue to execute and it could create some bugs.

You need return the array for example:
if ( $payment_status['status'] ) {
$order->update_status( 'on-hold', __( "ABC Payment Done\n", 'stackoverflow' ) );
wc_reduce_stock_levels($order_id);
WC()->cart->empty_cart();
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order )
);
} else {
$order->update_status( 'failed', __( 'Payment error:', 'woocommerce' ) . $this->get_option( 'error_message' ) );
wc_add_notice( $payment_status['message'] , 'error' );
WC()->cart->empty_cart();
return array(
'result' => 'failure',
'redirect' => WC()->cart->get_checkout_url()
);
}
That code will redirect to the checkout page. Hope help you.

Related

How to Use WP_REDIRECT and Continue Parent Execution

In WordPress, how do you use the WP_REDIRECT() function from a custom plugin, and then pass back execution to the parent function?
When I use WP_REDIRECT, I am noticing that the execution is not passed back to the parent function.
Sharing Code - UPDATE:
How can I hook into the below plugins' do_action( 'otherplugin_new_user_moderation', $customer_id ); hook and use the below redirect function to force the home page to load (from my plugin); and then pass the execution flow back to the parent (other plugin) so that the wc_add_notice is added to the home page (instead of the current page).
function redirect() {
wp_safe_redirect(home_url());
exit();
}
Another Plugin - handles moderation:
do_action( 'otherplugin_created_customer', $customer_id, $new_customer_data );
if ( 'yes' === \get_option( 'otherplugin_moderate_registrations', 'yes' ) ) {
update_user_meta( $customer_id, 'otherplugin_moderation_required', true);
do_action( 'otherplugin_new_user_moderation', $customer_id );
wc_add_notice( __( 'Your registration has been submitted successfully.', 'other-plugin' ) );
} else {
do_action( 'otherplugin_new_user_auto_approved', $customer_id, $password );
wc_add_notice( __( 'Your registration was processed successfully and a password has been sent to your email address.', 'other-plugin' ) );
}
Please let me know if I can clarify anything from my end.
Thank you!

Display a success custom notice after Placing an Order in WooCommerce

I need to check on the checkout page whether payment has been successful or not and display the message: 'Your payment has been successful', and then redirect to the thank you page (which is customized per product by the plugin Woo Product Tools). I've been trying to find hooks on the Woo documentation, but no luck so far. The latest code I have is:
add_action( 'woocommerce_after_checkout_validation', 'message_after_payment' );
function message_after_payment(){
global $woocommerce;
$order = wc_get_order( $order_id );
if ( $order->has_status('processing') ){
wc_add_notice( __("Your payment has been successful", "test"), "success" );
}
}
How can this be achieved?
You can't display a "success" notice on checkout page once you submit an order (place an order)… You can do that in Order Received (thankyou) page. Also in your code, $order_id is not defined…
So the right hook is woocommerce_before_thankyou using wc_print_notice() function instead:
add_action( 'woocommerce_before_thankyou', 'success_message_after_payment' );
function success_message_after_payment( $order_id ){
// Get the WC_Order Object
$order = wc_get_order( $order_id );
if ( $order->has_status('processing') ){
wc_print_notice( __("Your payment has been successful", "woocommerce"), "success" );
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Addition: Display a custom html message instead of a Woocommerce notice
Just replace the code line:
wc_print_notice( __("Your payment has been successful", "woocommerce"), "success" );
with for example this:
echo '<p class='cudtom-message"> . __("Your payment has been successful", "woocommerce"), "success" ) . '</p>';
You can add your own html as you like around the text message.

Handle data received after payment with custom Woocommerce form based gateway

I'm trying to add a custom payment gateway in my Woocommerce website. The flow will be like this:
After the user clicks on "Place order" button, he is redirected to the hosted payment page (HPP) of my payment processor. In order to achieve that, I must send some data (such as the merchant id, a hash, the order amount, etc.) through a hidden form which the payment processor needs to make the redirection to the HPP.
In the HPP, there is a form where the user can introduce his card data.
If everything is ok, the user is redirected to thank you page, otherwise he will be redirected to a fail page, or an error message shows up, I don't care.
This is what I have achieved so far:
When the user clicks on "Place order", a new order with the status "on hold" is created and he is redirected to a page called 'prueba.php'. This page only contains the hidden form with the data the payment processor needs to redirect the user to their gateway. This form is submitted automatically once the page is loaded (I have the feeling that this isn't the safest thing to do since if you open the element inspector, you can see the hidden inputs and their values, but I didn't know any other better ways to make this work). This is what I have in the main file of the plugin:
add_action( 'plugins_loaded', 'custom_init_gateway_class' );
function custom_init_gateway_class() {
class WC_My_Gateway extends WC_Payment_Gateway {
/**
* Constructor
*/
public function __construct() {
$this->id = 'mygateway';
$this->has_fields = true;
$this->method_title = 'My Gateway';
$this->method_description = 'Description of payment gateway';
$this->supports = array(
'products'
);
// Method
$this->init_form_fields();
$this->init_settings();
$this->title = $this->get_option( 'title' );
$this->description = $this->get_option( 'description' );
$this->enabled = $this->get_option( 'enabled' );
add_action( 'woocommerce_update_options_payment_gateways_' . $this->id, array( $this, 'process_admin_options' ) );
}
/**
* Plugin options
*/
public function init_form_fields(){
$this->form_fields = array(
'enabled' => array(
'title' => __( 'Enable/Disable', 'woocommerce' ),
'type' => 'checkbox',
'label' => __( 'Enable Card Payment', 'woocommerce' ),
'default' => 'yes'
),
'title' => array(
'title' => __( 'Title', 'woocommerce' ),
'type' => 'text',
'description' => __( 'This controls the title which the user sees during checkout.', 'woocommerce' ),
'default' => __( 'Credit Payment', 'woocommerce' ),
'desc_tip' => true,
),
'description' => array(
'title' => __( 'Customer Message', 'woocommerce' ),
'type' => 'textarea',
'default' => ''
)
);
}
/*
* Processing the payments
*/
public function process_payment( $order_id ) {
global $woocommerce;
$order = new WC_Order( $order_id );
$order->update_status('on-hold', __( 'Awaiting card payment', 'woocommerce' ));
return array(
'result' => 'success',
'redirect' => 'https://mydomain.example/prueba.php?price='.$order->get_total().'&currency='.$order->get_currency().'&order_id='.$order->get_id()
);
}
}
}
function custom_add_gateway_class( $methods ) {
$methods[] = 'WC_My_Gateway';
return $methods;
}
add_filter( 'woocommerce_payment_gateways', 'custom_add_gateway_class' );
The hidden form in "prueba.php". In the input "MERCHANT_RESPONSE_URL" I must specify which url I want the payment processor to send the data to, after the payment is completed:
<form method="POST" action="https://paymentprocessorgateway.example" name="respuesta" style="display: none;">
<input type="hidden" name="TIMESTAMP" value="<?php echo $timestamp; ?>">
<input type="hidden" name="MERCHANT_ID" value="12345">
<input type="hidden" name="AMOUNT" value="<?php echo $Amount; ?>">
<input type="hidden" name="CURRENCY" value="<?php echo $Currency; ?>">
<input type="hidden" name="SIGNATURE" value="<?php echo $hash; ?>">
<input type="hidden" name="COMMENT1" value="<?php echo $order_id; ?>">
<input type="hidden" name="MERCHANT_RESPONSE_URL" value="https://mydomain.example/response.php">
<input type="submit" value="Click here to buy">
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.4/jquery.min.js"></script>
<script type="text/javascript">
function sendForm() {
$("form").submit();
}
sendForm();
</script>
After this form is sent, the user is redirected to the payment processor gateway where he can write and send his card data, and then the processor sends some POST data to "response.php". In this page, I compare a hash I receive from the payment processor with a hash I generate myself (with some of the other POST values received from the payment processor).
What I'd like to achieve is redirecting the user to the thank you page and change the order status to 'completed' if everything is correct, or showing an error message if it isn't. Also, I would like that the order is created here (if the payment is correct), not when the client clicks on 'Place order' button (I thought that knowing the id of the order, I will be able to change its status wherever I needed, or that I could create a new order anywhere, but I must be doing everything wrong since I don't know very well how woocommerce and OOP PHP works).
I tried writing this in "response.php", but the processor's gateway throws an error that says the transaction went well but it couldn't connect to the url specified in MERCHANT_RESPONSE_URL:
//Generating the hash with data received from processor
$respuesta = $_POST['TIMESTAMP'].".".$_POST['MERCHANT_ID'].".".$_POST['ORDER_ID'].".".$_POST['RESULT'];
$my_hash = sha1(sha1($respuesta);
global $woocommerce;
$order = wc_get_order( $order_id );
if ($my_hash != $_POST['HASH']) { //$_POST['HASH'] is the hash sent by the processor.
wc_add_notice( 'Please try again.', 'error' );
return;
} else {
$order->payment_complete();
$woocommerce->cart->empty_cart();
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order )
);
}
If I delete the woocommerce related variables and use something like this to see if it's working, the alerts are printed correctly:
if ($my_hash != $_POST['HASH']) {
echo '<script>alert("Wrong transaction");</script>';
} else {
echo '<script>alert("Correct transaction");</script>';
}
It's like I can't use woocommerce related variables there. I guess I must attach this to some hook or something, but I don't know which or where, I have the feeling I have been making many mistakes along the way. If someone could throw some light on how to handle the response of the payment processor and integrate it with Woocommerce flow I would be eternally grateful.
First of all. If you're supposed to submit a hidden form, I wouldnt worry about security when it comes to the hidden or not aspect. Anything that is client side can always be viewed/tampered with anyway, so thats not where security measures should be taken imo.
Your last block of code mentions $old_hash, but it is empty? At least, your code block doesn't set it. What part of this code isn't working you say? Did you try logging to something to like an error log in it to see if it is called? (Like error_log('message'); )
You mentioned that HPP sends back a hash to a page of your choice, so I'd say response.php is probably the page where the user is sent after paying.
(Also, it is almost bed time for me, so apologies if I stop responding.)

Apply WooCommerce Coupon code to checkout

I am trying to create a "Product Redeem Page as shown in this tutorial on SitePoint.
Problem is that the product does indeed gets added to the cart and you can proceed to the checkout, but the discount that is associated with the coupon code is not automagically applied. In the coupon code I created the value is set to 100% discount.
You could apply te coupon code again via the "Do you have a coupon code" fly out on the checkout page, but this defeats the whole purpose.
I also did not get this code working to begin with, but I was able to figure out that:
// Check coupon to make determine if its valid or not
if( ! $coupon->id && ! isset( $coupon->id ) ) {
...Rest of code here...
should be:
// Check coupon to make determine if its valid or not
if( ! $coupon->id && ! isset( $coupon_id ) ) {
Please note the second Not isset variable name. Maybe this does work, but is not the proper way of handling things, everybody knows, but me.
Sadly I am out of my comfort zone a.t.m., but I am willing to learn by making mistakes and figuring out how to fix them and learn from people who are way smarter then me and/or way more advanced. In my direct cirlce of friends I have no one that I could aks and get any other answer then: "Huh?!?", so I am giving it a shot here on Stackoverflow.
A link to the tutorial on SitePoint only is probably not appreciated, so here is the complete code I am using:
The Ajax handlers added in functions.php
add_action( 'wp_ajax_spyr_coupon_redeem_handler', 'spyr_coupon_redeem_handler' );
add_action( 'wp_ajax_nopriv_spyr_coupon_redeem_handler', 'spyr_coupon_redeem_handler' );
The coupon login also added to functions.php
function spyr_coupon_redeem_handler() {
// Get the value of the coupon code
$code = $_REQUEST['coupon_code'];
// Check coupon code to make sure is not empty
if( empty( $code ) || !isset( $code ) ) {
// Build our response
$response = array(
'result' => 'error',
'message' => 'Code text field can not be empty.'
);
header( 'Content-Type: application/json' );
echo json_encode( $response );
// Always exit when doing ajax
exit();
}
// Create an instance of WC_Coupon with our code
$coupon = new WC_Coupon( $code );
// Check coupon to make determine if its valid or not
if( ! $coupon->id && ! isset( $coupon_id ) ) {
// Build our response
$response = array(
'result' => 'error',
'message' => 'Invalid code entered. Please try again.'
);
header( 'Content-Type: application/json' );
echo json_encode( $response );
// Always exit when doing ajax
exit();
} else {
// Attempting to add the coupon code as a discount.
WC()->cart->add_discount( $code );
// Coupon must be valid so we must
// populate the cart with the attached products
foreach( $coupon->product_ids as $prod_id ) {
WC()->cart->add_to_cart( $prod_id );
}
// Build our response
$response = array(
'result' => 'success',
'href' => WC()->cart->get_cart_url()
);
header( 'Content-Type: application/json' );
echo json_encode( $response );
// Always exit when doing ajax
exit();
}
}
The jQuery form submission code, enqueued via the registered Ajax handlers in functions.php
jQuery( document ).ready( function() {
jQuery( '#ajax-coupon-redeem input[type="submit"]').click( function( ev ) {
// Get the coupon code
var code = jQuery( 'input#coupon').val();
// We are going to send this for processing
data = {
action: 'spyr_coupon_redeem_handler',
coupon_code: code
}
// Send it over to WordPress.
jQuery.post( woocommerce_params.ajax_url, data, function( returned_data ) {
if( returned_data.result == 'error' ) {
jQuery( 'p.result' ).html( returned_data.message );
} else {
// Hijack the browser and redirect user to cart page
window.location.href = returned_data.href;
}
})
// Prevent the form from submitting
ev.preventDefault();
});
});
Thanks in advance of pointing me in the right direction.
Update: At this moment I got the wanted functionality working.
What needed to be done is to add:
// Let's add the discount to the cart.
global $woocommerce;
WC()->cart->add_discount( $code );
inside the foreach statement. The complete else statement now looks like this:
} else {
// Coupon must be valid so we must
// populate the cart with the attached products
foreach( $coupon->product_ids as $prod_id ) {
WC()->cart->add_to_cart( $prod_id );
// Let's add the discount to the cart.
global $woocommerce;
WC()->cart->add_discount( $code );
}
// Build our response
$response = array(
'result' => 'success',
'href' => WC()->cart->get_cart_url()
);
header( 'Content-Type: application/json' );
echo json_encode( $response );
// Always exit when doing ajax
exit();
I'm still not sure if this is the correct way of handeling this, but it seems to work.
For instance I am calling(?!) the global $woocommerce variable, but below I am using the global(?!) class WC() to add the coupon. Not sure if this is as clean and logical as it gets.
If anybody knows of a better/cleaner way, please let me know! I am happy to learn from you guys and maybe one day I may be able to return the favour, who knows.

Hide Payment Method WooCommerce after Failure of first try

I am trying to hide the payment method once user click place order using payment method lets say 'B' which returns in failure of payment.
public function process_payment( $order_id ) {
global $woocommerce;
$order = new WC_Order( $order_id );
if($this->api->somemethod()){
// Payment complete
$order->payment_complete();
return array(
'result' => 'success',
'redirect' => $this->get_return_url( $order )
);
}
else {
wc_add_notice( pll__('Payment error: You cannot use this payment method, Choose another method '), 'error' );
return;
}
}
I have used following filter
add_filter( 'woocommerce_payment_gateways', 'add_gateway_cb' );
But it only works on page reload. I want something which works with Ajax process payment or at least triggers the payment methods to reload upon failure.
Alternatively:
I can use custom JS on place order click.

Categories