I have set up a custom Woocommerce off-side payment gateway, the communication from my website to the Gateway works fine. The user gets redirected to the gateway and after the payment he gets returned to the (by Woocommerce) defined return URL.
In the API documentation of the gateway, it says that the payment gateway will add a query string variable to the return url. This variable can have the following values:
response=approved
response=declined
response=error
In the documentation is this exampple:
If backURL = http://www.test.com/return.asp
Then the response will be http://www.test.com/return.asp?response=approved
To process the order I have created the code below (first just for the state of response=approved)
However, the order status does not get updated, when I test it. I hope somebody can point me in the right direction to find the error.
On top of the gateway I have extended query_vars with:
function add_query_vars_filter( $vars ){
$vars[] = "response";
return $vars;
}
add_filter( 'query_vars', 'add_query_vars_filter' );
After sending the user to the gateway I have this function to handle and process the query string in the return URL:
function check_example_response(){
global $woocommerce;
$transaction_id = get_query_var( 'response' );
$order = new WC_Order( $order_id );
// check if payment was successful
if ($transaction_id == 'approved') {
//Update order status
$order->update_status( 'processing', 'Payment received, your order is currently being processed.' );
//Add admin order noote
$order->add_order_note('Payment Via Example Gateway<br />Transaction ID: '.$order);
//Add customer order note
$order->add_order_note('Payment Received.<br />Your order is currently being processed.<br />We will be shipping your order to you soon.<br />Transaction ID: '.$order, 1);
$message = 'Thank you for shopping with us.<br />Your transaction was successful, payment was received.<br />Your order is currently being processed.';
$message_type = 'success';
}
// Reduce stock levels
$order->reduce_order_stock();
// Empty cart
WC()->cart->empty_cart();
}}
I have also registered this function with:
add_action( 'woocommerce_api_wc_example', array( $this, 'check_example_response' ) );
Wordpress also does not give any hints in DEBUG mode, so I am right now quite frustrated and hope somebody can help me.
Thank you!
Related
Im trying to set a value "paid" in my custom table when the payment was successful / the customer gets redirectd to the thankyou page.
Im not sure if its secure enough to insert "paid => 1" just when the buyer reached the thankyou page. Thats why i wanted to check if the order was really paid, completed or processing.
Now i figured some payment methods are slow? When i checkout with Klarna my method works and it inserts paid = 1, but when I choose Paypal my Method will die and return: "WASNT PAID". But when I refresh the thankyou-page like 30 secs later it works. So i came to the conclusion that the order_status isnt set fast enough? Is there a cleaner way than mine?
add_action('woocommerce_thankyou', 'ceb_order_complete', 10, 1);
function ceb_order_complete( $order_id ) {
if ( ! $order_id )
return;
// Getting an instance of the order object
$order = wc_get_order( $order_id );
if($order->is_paid() || $order->has_status('processing') || $order->has_status('completed')) {
global $wpdb;
$payID = WC()->session->get( 'payID' );
if(!empty($payID)) {
if(!$wpdb->update($wpdb->prefix."ceb_registrations", array("paid"=>1), array("payID"=>$payID))) {
die("ERROR IN PAYMENT COMPLETE");
}
}
} else {
die("WASNT PAID");
}
}
"woocommerce_thankyou" hook will run each time you see/refresh thankyou page.
The most suitable way is to process data after payment received. For that you can use "woocommerce_payment_complete".
add_action('woocommerce_payment_complete', 'payment_complete_callback');
function payment_complete_callback($order_id)
{
$order = wc_get_order($order_id);
$user = $order->get_user();
// do what ever you want to do using $order object of the WC_Order class providing many functions
}
Is it possible to set all new orders to on hold without them going to processing first?
I need to capture the funds, but the order needs to go as on-hold rather than processing so that I can make sure the payment is received before triggering the email to our supplier.
I have found some code that allows me to change all new orders to on-hold, however when payment is authorized the order is set to processing.
pending payment --> processing (triggers email) --> on-hold
Screenshot of Order Notes that show this
This is the code that I found:
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_onhold_order' );
function custom_woocommerce_auto_onhold_order( $order_id ) {
global $woocommerce;
if ( !$order_id )
return;
$order = new WC_Order( $order_id );
$order->update_status( 'on-hold' ); //All new orders go to "on-hold"
}
I've been looking for the action which triggers the order status to be set to processing on payment authorization however I cannot find it.
All help is greatly appreciated!
Edit: I've managed to stumble upon a snippet of code that appears to work.
add_filter( 'woocommerce_payment_complete_order_status', 'custom_update_order_status', 10, 2 );
function custom_update_order_status( $order_status, $order_id ) {
return 'on-hold';
}
All orders start with a "pending" status in Woocommerce. That status is set before they go through the payment gateway...
There is no email notifications for "Pending" orders staus
You can try the following, that will set the status "on-hold" on order creation (The update status action comes once the order has been created ans saved in database):
add_action( 'woocommerce_checkout_create_order', 'force_new_order_status', 20, 1 );
function force_new_order_status( $order ) {
if( ! $order->has_status('on-hold') )
$order->set_status( 'on-hold', 'Forced status by a custom script' );
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
When a woocommerce order is created the status of the order is "processing". I need to change the default order-status to "pending".
How can I achieve this?
The default order status is set by the payment method or the payment gateway.
You could try to use this custom hooked function, but it will not work (as this hook is fired before payment methods and payment gateways):
add_action( 'woocommerce_checkout_order_processed', 'changing_order_status_before_payment', 10, 3 );
function changing_order_status_before_payment( $order_id, $posted_data, $order ){
$order->update_status( 'pending' );
}
Apparently each payment method (and payment gateways) are setting the order status (depending on the transaction response for payment gateways)…
For Cash on delivery payment method, this can be tweaked using a dedicated filter hook, see:
Change Cash on delivery default order status to "On Hold" instead of "Processing" in Woocommerce
Now instead you can update the order status using woocommerce_thankyou hook:
add_action( 'woocommerce_thankyou', 'woocommerce_thankyou_change_order_status', 10, 1 );
function woocommerce_thankyou_change_order_status( $order_id ){
if( ! $order_id ) return;
$order = wc_get_order( $order_id );
if( $order->get_status() == 'processing' )
$order->update_status( 'pending' );
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested and works
Note: The hook woocommerce_thankyou is fired each time the order received page is loaded and need to be used with care for that reason...
Now the function above will update the order status only the first time. If customer reload the page, the condition in the IF statement will not match anymore and nothing else will happen.
Related thread: WooCommerce: Auto complete paid Orders (depending on Payment methods)
Nowadays, if the payment gateway that you use properly sets the order status using WC_Order->payment_complete(), you can use the woocommerce_payment_complete_order_status filter.
This is better than using woocommerce_thankyou hook since we are setting the order status immediately, rather than applying it after it has been already set.
function h9dx3_override_order_status($status, $order_id, $order) {
if ($status === 'processing') {
$status = 'pending';
}
return $status;
}
add_filter('woocommerce_payment_complete_order_status', 'h9dx3_override_order_status', 10, 3);
Again, this will only work if the payment gateway uses the proper payment_complete wrapper method rather than setting status directly with set_status. You can just search the gateway code for 'payment_complete(' and 'set_status(' to see what it does.
If you develop a plugin for everyone, you will be better off with using woocommerce_thankyou, or you could use a combined approach and use woocommerce_thankyou as the fallback if the order status was not updated.
The hook woocommerce_thankyou suffers from the problem that you can pay for an order and then close the browser or go somewhere else and therefore never click "Return to Merchant" (which is displayed after paying via paypal) to get to the thankyou page. And then the code will never be executed.
// Rename order status 'Processing' to 'Order Completed' in admin main view - different hook, different value than the other places
add_filter( 'wc_order_statuses', 'wc_renaming_order_status' );
function wc_renaming_order_status( $order_statuses ) {
foreach ( $order_statuses as $key => $status ) {
if ( 'wc-processing' === $key )
$order_statuses['wc-processing'] = _x( 'Order Completed', 'Order status', 'woocommerce' );
}
return $order_statuses;
}
I have a Wordpress setup with WooCommerce using the standard Paypal Gateway. Payments are being received and going through fine. Paypal IPN is being received by the site and marked as "Completed" for an order, but the order status remains unchanged in WooCommerce and still reads as "Processing".
10-04-2016 # 11:04:18 - Received valid response from PayPal
10-04-2016 # 11:04:18 - Found order #1303
10-04-2016 # 11:04:18 - Payment status: completed
Other things entered into Paypal gateway settings:
Paypal API Details
Paypal Identity Token
Clients Paypal login email as the receiver email and paypal email
Payment set to capture
Paypal Return URL:
http://[URL]/checkout/order-received/
Paypal Notification URL:
http://[URL]/?wc-api=WC_Gateway_Paypal
Other Related Woo Plugins installed:
https://woocommerce.com/products/woocommerce-bookings/
https://woocommerce.com/products/smart-coupons/
I'm a little stumped as what else to try, as the IPN is obviously being recieved but for whatever reason the WooCommerce is not updating the order status with this information. There are some PHP Notices regarding unrelated points in other templates but not anything that should be interfering with WooCommerce. Any help or ideas to try would be much appreciated!
I was having the same issue and discovered this is probably normal for WooCommerce since the product is supposed to be shipped out and then set to Completed. You can use a plugin to autocomplete the orders.
WooCommerce Autocomplete Orders
https://wordpress.org/plugins/woocommerce-autocomplete-order/
How To Automatically Set WooCommerce PayPal Orders as Completed
http://biostall.com/how-to-automatically-set-woocommerce-paypal-orders-as-completed/
Note: You have to make sure your product is Virtual and set the Mode to "Paid orders of virtual products only" under WooCommerce > Settings > Extra Options
Thanks for the reply, I did see the auto complete plugin but the client In hand required this to be a manual method. I managed to figure out a method that works for Paypal standard payment, based off the below resource:
http://codecharismatic.com/run-your-own-damn-code-after-paypal-calls-woocommerce-back/
<?php
/**
* Auto Complete Woocommerce 'processing' orders
*/
add_action( 'valid-paypal-standard-ipn-request', 'handle_paypal_ipn_response', 50, 1 );
function handle_paypal_ipn_response( $formdata ) {
if ( !empty( $formdata['invoice'] ) && !empty( $formdata['custom'] ) ) {
if( $formdata['payment_status'] == 'Completed' ) {
// decode data
$order_data = json_decode($formdata['custom'], true);
// get order
$order_id = ($order_data) ? $order_data['order_id'] : '';
$order = new WC_Order( $order_id );
// got something to work with?
if ( $order ) {
if ($order->post->post_status == 'wc-processing'){
// Status success
WC_Gateway_Paypal::log( 'Changing order #' . $order->id . ' status from processing to completed');
$order->update_status( 'completed' );
} else {
// Status fail
WC_Gateway_Paypal::log( 'Status fail, order #' . $order->id . ' status is set to ' . $order->post->post_status . ', not processing');
}
} else {
// Order fail
WC_Gateway_Paypal::log( 'Fail, no order found');
}
} else {
// Payment fail
WC_Gateway_Paypal::log( 'Payment status fail, not completed');
}
}
}
I'm having an issue that's driving me absolutely insane with the payment complete hook because it works perfectly with the PayPal Sandbox, but not when I go live.
I require all new customers to verify the PayPal email used with their first purchase. I believe the problem I'm having is that the hook and function is firing off before WordPress has written the paypal email to the post meta.
I can't post the whole function, but here's what's relevant:
add_action('woocommerce_payment_complete', 'preapproved_api_order', 10, 1);
function preapproved_api_order($order_id){
if ( ! empty( $gateway->id ) && 'paypal' == $gateway->id ) {
$order = new WC_Order( $order_id );
$user_id = $order->get_user_id();
$gateway = wc_get_payment_gateway_by_order( $order );
$paypal_email = get_post_meta( $order_id, 'Payer PayPal address', true );
$error = 'paypal email used for payment '.$paypal_email;
send_test_mail($error);
}
I added some quick error checking and that email does get sent without an email address included, which is why I'm thinking WP hasn't written the email address to the post meta.
If anyone can think of an alternative way to get the paypal email address so that it works with this hook it would be greatly appreciated. Very strange that it works in the sandbox and not live, I'm not sure what can be causing the delay. Thanks in advance.
I couldn't figure out what was causing the delay posting to the post_meta, but I did find the IPN hook that captures all the order and payment details being sent back from PayPal. Just an alternative way for a payment complete hook for PayPal in WooCommerce. Hopefully it helps others experiencing the same issue:
add_action('valid-paypal-standard-ipn-request', 'preapproved_api_order', 10, 1);
function preapproved_api_order($details){
global $woocommerce, $wpdb;
$order_id = $details['item_number1'];
$order = new WC_Order( $order_id );
}