I have made a simple webshop with woocommerce, with three payment methods. iDeal and by direct bank transfer and on account. The order ID is created based on the payment method. for example, if payment is made with iDEAL, the order id becomes ID190100; if payment is made on account, the order id becomes RK190100. I get this working with the plugin
"Sequential Order Numbers for WooCommerce" from BeRocket but these are already created before the payment is complete. The order ID must only be finalized once the payment has been made. Now orders that have not yet paid, and may not be paying, will receive a fixed order ID. So is it possible to create a temporary order id and when the order is completed change the order id based on payment method?
Woocommerce by default will use the post ID of the order for the order ID. This is evident when viewing the WC_Order::get_order_number() method. If you want to use a custom order number to display, you'll need to add a filter on woocommerce_order_number to load in a different value.
An example script would be:
add_action( 'woocommerce_order_status_completed', 'wc_change_order_id' );
function wc_change_order_id( $order_id ) {
$order = wc_get_order( $order_id );
$method = $order->get_payment_method(); // need check this
if ( $method === 'account' ) {
$number = 'ID' . $order->get_id();
$order->update_meta_data('_new_order_number', $number );
}
}
add_filter('woocommerce_order_number', function($default_order_number, \WC_Order $order) {
//Load in our meta value. Return it, if it's not empty.
$order_number = $order->get_meta('_new_order_number');
if(!empty($order_number)) {
return $order_number;
}
// use whatever the previous value was, if a plugin modified it already.
return $default_order_number;
},10,2);
Try this. It's very quick example. Hope help.
add_action( 'woocommerce_order_status_completed', 'wc_change_order_id' );
function wc_change_order_id( $order_id ) {
$order = wc_get_order( $order_id );
$method = $order->get_payment_method(); // need check this
if ( $method === 'account' ) {
$number = 'ID' . $order->get_id();
$order->set_id( $number );
$order->save();
}
}
Related
I want a code that the password generated by woocommerce will be taken from the phone field on the checkout page when the user fills in the details for the billing
I tried the following code and it did not work
I would love a code that works. thanks.
<?php
//Use user phone number as auto-generated password
function wcs_filter_password_phone_number( $args ) {
$args['user_pass'] = $args['dbem_phone'];
return $args;
}
add_filter( 'woocommerce_new_customer_data', 'wcs_filter_password_phone_number' );
It's not really secure setting the password based on the customer's phone number. Anyone could find a way in to be honest.
Having said that, the task is interesting, and based on WooCommerce: Update Order Field Value After a Successful Order it is possible indeed to alter an order/user field after a successful checkout.
You could hook into "woocommerce_thankyou" as per the article, or even into "woocommerce_payment_complete" if you wanted to do that in the background and only for paid/completed orders.
Also, you should look into https://developer.wordpress.org/reference/functions/wp_set_password/ which is the WP function to set a new password programmatically.
Here's my attempt:
add_action( 'woocommerce_thankyou', 'bbloomer_alter_password_after_order' );
function bbloomer_alter_password_after_order( $order_id ) {
$order = wc_get_order( $order_id );
$phone = $order->get_billing_phone();
$user_id = $order->get_user_id();
if ( $phone && $user_id ) {
wp_set_password( $phone, $user_id );
}
}
I've been looking around to find a way to change our woocommerce orders status based on the order notes. We have a card processor that adds order notes when an order is approved, but it puts the order on hold because the card is only authorized not charged. We charge the card later once the order has been shipped, but we need the order to go into "processing" so we can export the order into our system so we can actually process the order (we don't process through woocommerce).
I found this code however, I'm wondering if this can be modified to pull what is in the order notes since our processor adds whether or not the card has been approved.
function mysite_woocommerce_payment_complete( $order_id ) {
error_log( "Payment has been received for order $order_id" );
}
add_action( 'woocommerce_payment_complete', 'mysite_woocommerce_payment_complete', 10, 1 );
Here is a Example which switch on hold order status with processing. you can use it or if need to add some more condition, please mention here or update it accordingly. Thanks
function switch_hold_status_to_processing ($order_id) {
$order = new WC_Order( $order_id );
$order->update_status('processing');
}
add_action('woocommerce_order_status_on-hold', 'switch_hold_status_to_processing');
You can use wc_get_order_notes($args) function to get the order notes in the specific order. It returns an array of order notes. Then you can loop through the array to find the order note you need. Then use an if statement to verify the content of the order note and update the status.
function mysite_woocommerce_payment_complete( $order_id ) {
$order_notes = wc_get_order_notes([
'order_id' => $order_id,
]);
foreach ($order_notes as $order_note){
if ($order_note->content == "Order verified"){
$order = new WC_Order( $order_id );
$order->update_status('processing');
break;
}
}
}
add_action( 'woocommerce_payment_complete', 'mysite_woocommerce_payment_complete', 10, 1 );
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
}
I'm using woocommerce subscriptions and I'm writing a plugin to update an external system, if the user upgrade or downgrade his subscription it shows as a new order with its ID, but I can't get the subscription ID (which it's constant) related to the order ID, I check the documentation but I couldn't find a solution.
I was able to achieve like this.
$subscriptions = wcs_get_subscriptions_for_order($order_id, array( 'order_type' => 'any' ));
foreach( $subscriptions as $subscription_id => $subscription_obj )
{
$current_subs_id = $subscription_obj->get_id(); // This is current subscription id
$parent_id = $subscription_obj->get_parent_id(); // This is subscription parent id
}
You can access this data by hooking into the processed_subscription_payment action provided by WooCommerce Subscriptions:
add_action( 'processed_subscription_payment', 'se43079522_process_subscription', 10, 2 );
function se43079522_process_subscription($user_id, $subscription_key) {
// here you have access to the $subscription_key (ID) and the $user_id associated
}
Take a look at this link: https://docs.woocommerce.com/document/subscriptions/develop/action-reference/ for more information on the available actions with this plugin.
OR, you can do this manually like so:
Read comments for walk-through
global $woocommerce;
// Get the order ID and save as variable
$order_id = [ORDER_ID];
// Get the order object
$order = new WC_Order( $order_id );
// Loop through the subscription order
foreach ( WC_Subscriptions_Order::get_recurring_items( $order ) as $order_item ) {
// Get the subscription key
$subscription_key = WC_Subscriptions_Manager::get_subscription_key( $order->id, WC_Subscriptions_Order::get_items_product_id( $order_item ) );
}
// This is your subscription key (ID)
echo $subscription_key;
Get Subscription ID from Order. You can use wcs_get_subscriptions_for_order woocommerce function to get an object with the subscription related to that order and get the subscription ID from there.
$subscriptions = wcs_get_subscriptions_for_order($order_id);
var_dump($subscriptions);
With WooCommerce, I have the following hook in my function.php after the new order is submitted:
add_action( 'woocommerce_new_order', 'create_job_openings');
function create_job_openings($order_id) {
$order = new WC_Order($order_id);
$items = $order->get_items();
foreach ($order->get_items() as $key => $item) {
$product_name = $item['name'];
var_dump($product_name);
}
}
The above code is not giving me any output i'e it is not entering inside the foreach loop that's why var_dump() not giving me any output, but if I mention the order_id specifically like create_job_openings($order_id=517) it works, even I tried echo $order_id before foreach loop, it is giving me the order_id, then why it is not entering the foreach loop?
note: when I try var_dump($items); before foreach loop its giving me
array(0) {
}
Why it is not able to get the product details even if there are products in it after new order is made?
Update 2 — The working solution (using an email notification hook)
The problem is when using email notification hook can fire an action 2 times, for example when a new order is made (notification for the Shop manager and notification for the customer).
You want to use this "New Order" event for Orders that are in "processing" status.
To avoid your action to be fired 2 times using New order notification WooCommerce event, we use 'customer_processing_order' instead of 'new_order' email ID (notification event).
Here we don't need to get the $order object, as we got it as an argument in this hooked function.
So here is your final functional code:
add_action( 'woocommerce_email_before_order_table', 'custom_action_on_completed_customer_email_notification', 10, 4 );
function custom_action_on_completed_customer_email_notification( $order, $sent_to_admin, $plain_text, $email ) {
if( 'customer_processing_order' == $email->id ){ // for processing order status customer notification…
foreach ($order->get_items() as $item_id => $item_values) {
$product_name = $item_values['name'];
echo $product_name;
break; // (optional) stop loop to first item
}
}
}
This is the validated and working answer to this question
Related Working Answers:
Sending an SMS for specific email notifications and order statuses
Avoid repetitive emails notification on some auto completed orders
Change the user role on purchase for specific products when order status is completed
Update 1 (hook alternative)
Trying using woocommerce_thankyou hook, that is fired on review order after order has been processed:
add_action( 'woocommerce_thankyou', 'create_job_openings', 10, 1 );
function create_job_openings( $order_id ) {
if ( ! $order_id )
return;
$order = wc_get_order( $order_id );
foreach ($order->get_items() as $item_id => $item_values) {
$product_name = $item_values['name'];
var_dump($product_name);
break; // (optional) stop loop to first item
}
}
(Not working for the OP)
You should try this instead wc_get_order() function this way and your code will be:
add_action( 'woocommerce_new_order', 'create_job_openings', 10, 1);
function create_job_openings($order_id) {
$order = wc_get_order( $order_id );
$order_items = $order->get_items();
foreach ($order_items as $item_id => $item_values) {
$product_name = $item_values['name'];
var_dump($product_name);
break; // (optional) stops on first item
}
}
You can have a look to
How to get WooCommerce order details where a lot of things are explained…
(Not working for the OP)
you can use save_post action when post is added in wordpress. for more visit: Link
function wc_order_add_action($post_id, $post, $update)
{
$post_type = get_post_type($post_id);
// If this isn't a 'shop_order' post, don't update it.
if ("shop_order" != $post_type) return;
$order = wc_get_order($post_id);
foreach($order -> get_items() as $key => $item)
{
$product_name = $item['name'];
var_dump($product_name);
}
}
add_action('save_post', 'wc_order_add_action');
To get the items and product details from the new order, you simply need to request two parameters for woocommerce_new_order. The second passed parameter will be the new order object. Example:
add_action( 'woocommerce_new_order', 'so41605256_new_order', 10, 2 );
function so41605256_new_order( $order_id, $order ) {
// Directly access items from $order variable, do not use wc_get_order...
foreach ($order->get_items() as $item_id => $item) {
//...
}
}
This works for both programmatically created and user created orders, regardless of WC email configuration.
Reference: create and update methods in WC_Order_Data_Store_CPT core class. Kudos to this post which helped me discover this.
Faced the same issue. I fixed it by changing the hook to woocommerce_checkout_order_processed. It works perfectly for both instant payment and cash on delivery since it runs as soon as an order is placed. Note that, this will run before payment completion. So, it doesn't have any relation with payment.
User clicks on proceed to checkout button and then this hook fires.