Woocommerce - update_order_review become 502 after add_discount programmatically - wordpress

I need some help. I want to implement, add existing coupon to cart when some conditions are fulfilled. I've got nothing wrong with the condition. But, when the function are going to add discount to coupon, the wc-ajax=update_order_review become 502.
However, if I do another things like changing address or something that can re-run update_order_review, it become 200. update_order_review successfully running and coupon is added to cart.
This is my function code so far, and I've copied some code from stackoverflow forum too
add_action('woocommerce_before_calculate_totals', 'discount_based_on_weight_threshold');
function discount_based_on_weight_threshold( $cart ) {
if ( ! defined( 'DOING_AJAX' ) )
return;
// Your settings
$coupon_code = 'freeongkir8krpx'; // Coupon code
// Initializing variables
$applied_coupons = $cart->get_applied_coupons();
$coupon_code = sanitize_text_field( $coupon_code );
$methods = WC()->session->get( 'chosen_shipping_methods' );
$method = isset( $methods[ 0 ] ) ? $methods[ 0 ] : false;
$kupon = new WC_Coupon('freeongkir10k');
if($method !== 'jne_shipping_test'){
if($cart->get_subtotal() <= $kupon->get_minimum_amount()){
$user = wp_get_current_user();
error_log( 'CHECK USER ROLES');
error_log( print_r($user->roles, TRUE) );
if(in_array( 'administrator', (array) $user->roles )){
// Applying coupon
if( ! in_array($coupon_code, $applied_coupons) ){
$cart->add_discount( $coupon_code );
wc_clear_notices();
}
}
} else {
if( in_array($coupon_code, $applied_coupons) ){
$cart->remove_coupon( $coupon_code );
}
}
} else {
if( in_array($coupon_code, $applied_coupons) ){
$cart->remove_coupon( $coupon_code );
}
}
}
When I disable $cart->add_discount( $coupon_code );, it running successfully, so I pretty sure that adding discount is the trigger.
And the flow that I used is
Go to checkout, conditions fulfilled, add_discount error, another function re-run update_order_review, coupon applied.
Change shipping methods, conditions not fulfilled, applied coupon is deleted
Change shipping methods, conditions fulfilled, add_discount error, update_order_review become 502, no other function re-run update_order_review, infinite loading in checkout order review
So any wrong code or flow do I have? or there are any ways to trick the error since it will applied the code if the update_order_review are re-run
Thank you

Do you get any helpful information in your browser console or the wp error log?
If I remember right 502 on AJAX request means the code fails to give you a valid response.
Hm, I don't yet have a helpful answer.
I refactored your code a bit to improve readability.
This should do exactly what your code does.
add_discount() has been renamed apply_coupon() - maybe try that.
<?php
add_action('woocommerce_before_calculate_totals', 'discount_based_on_weight_threshold');
function discount_based_on_weight_threshold( $cart ) {
if ( ! defined( 'DOING_AJAX' ) )
return;
// Your settings
$coupon_code = 'freeongkir8krpx'; // Coupon code
// Initializing variables
$applied_coupons = $cart->get_applied_coupons();
$coupon_code = sanitize_text_field( $coupon_code );
$methods = WC()->session->get( 'chosen_shipping_methods' );
$method = isset( $methods[ 0 ] ) ? $methods[ 0 ] : false;
$kupon = new WC_Coupon('freeongkir10k');
// remove code if already applied
if( in_array($coupon_code, $applied_coupons) ){
$cart->remove_coupon( $coupon_code );
}
if(
$method !== 'jne_shipping_test') &&
$cart->get_subtotal() <= $kupon->get_minimum_amount()
){
$user = wp_get_current_user();
error_log( 'CHECK USER ROLES');
error_log( print_r($user->roles, TRUE) );
if( in_array( 'administrator', (array) $user->roles ) ){
// Applying coupon
$cart->apply_coupon( $coupon_code );
wc_clear_notices();
}
}
}

Related

Extra fee based on user ID, user role and payment method in WooCommerce checkout

I have a piece of code that adds a small fee to the checkout for a certain user role (b2bcustomer). Everything works OK.
Now I would like one particular user ID in this user role (b2bcustomer) not to be charged a fee.
I tried to complete the code below, but the fee for this user ID is still charged. Any advice?
Code in functions.php:
add_action( 'woocommerce_cart_calculate_fees', 'b2b_fee_for_gateway' );
function b2b_fee_for_gateway() {
$user_id = get_current_user_id(); /*added*/
if(is_checkout() && WC()->customer->get_role() != current_user_can( 'b2bcustomer' ) && ($user_id != 1083)) /*added && ($user_id != 1083)*/
return;
global $woocommerce;
$chosen_gateway = $woocommerce->session->chosen_payment_method;
if ( $chosen_gateway != 'cod' && current_user_can( 'b2bcustomer' ) && ($user_ID != 1083))
$surcharge = 10;
$woocommerce->cart->add_fee( 'B2B processing cost', $surcharge, true, '');
}
}
Some comments/suggestions regarding your code attempt/question
The use of global $woocommerce; is not necessary, since $cart is passed to the callback function
You can group the conditions but also list them 1 by 1 for clarity.
It is certainly not necessary to repeat the same conditions several times
But the most important, given the chosen payment method. In WooCommerce checkout when choosing a payment method, checkout totals are not refreshed. So something additional is required to refresh checkout "order review" section
So you get:
function action_woocommerce_cart_calculate_fees( $cart ) {
if ( is_admin() && ! defined( 'DOING_AJAX' ) ) return;
// 1. Only for 'b2bcustomer' user role
if ( ! current_user_can( 'b2bcustomer' ) ) return;
// 2. Exclude certain user ID
if ( get_current_user_id() == 1083 ) return;
// 3. Only for 'cod' payment method
if ( WC()->session->get( 'chosen_payment_method' ) != 'cod' ) return;
// Fee
$surcharge = 10;
// Applying
$cart->add_fee( __( 'B2B processing cost', 'woocommerce' ), $surcharge, true, '' );
}
add_action( 'woocommerce_cart_calculate_fees', 'action_woocommerce_cart_calculate_fees', 10, 1 );
// jQuery - Update checkout on method payment change
function action_wp_footer() {
if ( is_checkout() && ! is_wc_endpoint_url() ) :
?>
<script type="text/javascript">
jQuery( function($){
$( 'form.checkout' ).on( 'change', 'input[name="payment_method"]', function() {
$( document.body ).trigger( 'update_checkout' );
});
});
</script>
<?php
endif;
}
add_action( 'wp_footer', 'action_wp_footer' );

Disable specific payment methods depending on Woocommerce order status syntax error, unexpected 'elseif' (T_ELSEIF) [duplicate]

This question already has answers here:
Disable specific payment methods depending on Woocommerce order status
(2 answers)
Closed 2 years ago.
help i get this error
I am doing the same function Disable specific payment methods depending on Woocommerce order status I get
Copy and paste the same code and it didn't work
syntax error, unexpected 'elseif' (T_ELSEIF)
add_filter( 'woocommerce_available_payment_gateways', 'conditionally_hide_payment_gateways', 100, 1 );
function conditionally_hide_payment_gateways( $available_gateways ) {
// 1. On Order Pay page
if( is_wc_endpoint_url( 'order-pay' ) ) {
// Get an instance of the WC_Order Object
$order = wc_get_order( get_query_var('order-pay') );
// Loop through payment gateways 'pending', 'on-hold', 'processing'
foreach( $available_gateways as $gateways_id => $gateways ){
// Keep paypal only for "pending" order status
elseif ($gateways_id !== 'paypal' && $order->has_status('pending') ) {
unset($available_gateways[$gateways_id]);
}
}
}
// 2. On Checkout page
elseif( is_checkout() && ! is_wc_endpoint_url() ) {
// Disable paypal
if( isset($available_gateways['paypal']) ) {
unset($available_gateways['paypal']);
}
}
return $available_gateways;
}
enter image description here
The elseif inside the foreach doesn't have a preceding if, so that's your error.
add_filter( 'woocommerce_available_payment_gateways', 'conditionally_hide_payment_gateways', 100, 1 );
function conditionally_hide_payment_gateways( $available_gateways ) {
// 1. On Order Pay page
if( is_wc_endpoint_url( 'order-pay' ) ) {
// Get an instance of the WC_Order Object
$order = wc_get_order( get_query_var('order-pay') );
// Loop through payment gateways 'pending', 'on-hold', 'processing'
foreach( $available_gateways as $gateways_id => $gateways ){
// Keep paypal only for "pending" order status
if ($gateways_id !== 'paypal' && $order->has_status('pending') ) {
unset($available_gateways[$gateways_id]);
}
}
}
// 2. On Checkout page
elseif( is_checkout() && ! is_wc_endpoint_url() ) {
// Disable paypal
if( isset($available_gateways['paypal']) ) {
unset($available_gateways['paypal']);
}
}
return $available_gateways;
}

Wordpress: issue on update_post_meta for checkbox metabox

I have a metabox (one checkbox) , in posts, to check if a post will be featured..
My code:
function add_featured_post_checkbox() {
add_meta_box(
'custom_featured_meta',
'Featured post for Sidebar',
'featured_post_checkbox_callback',
'Post',
'side',
'high'
);
} add_action( 'add_meta_boxes', 'add_featured_post_checkbox' );
Callback function:
function featured_post_checkbox_callback( $post ) {
wp_nonce_field( 'custom_save_data' , 'custom_featured_nonce' );
$featured = get_post_meta($post->ID, '_featured_post', true);
echo "<label for='_featured_post'>".__('Is Featured? ', 'foobar')."</label>";
echo "<input type='checkbox' name='_featured_post' id='featured_post' value='1' " . checked(1, $featured) . " />";
}
Save function:
function custom_save_data( $post_id ) {
if( ! isset( $_POST['custom_featured_nonce'] ) ){
return;
}
if( ! wp_verify_nonce( $_POST['custom_featured_nonce'], 'custom_save_data') ) {
return;
}
if( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ){
return;
}
if( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
if( ! isset( $_POST['_featured_post'] ) ) {
return;
}
$my_data_featured = sanitize_text_field( $_POST['_featured_post'] );
update_post_meta( $post_id, '_featured_post', $my_data_featured );
} add_action( 'save_post', 'custom_save_data' );
This code works well only when I want to make a post featured..
For example if a post is not checked as featured and I select the choice then database updated perfect (with value 1 in post_meta) and the ckeckbox has the tick on box
But after, If I try to uncheck and save the post then the ckeckbox is again with the tick and nothing changed in database..
I try to find a solution for days on stackoverflow and general in web but I can't find.
Please help me
Thank you
When a checkbox is unchecked, $_POST will not contain the key _featured_post. Therefore, in your save callback, you need to change your strategy. You do not want to bail out if the key is not set. Rather, you either want to save a 0 or delete the post meta. Let's go with the later option.
function custom_save_data( $post_id ) {
if( ! isset( $_POST['custom_featured_nonce'] ) ){
return;
}
if( ! wp_verify_nonce( $_POST['custom_featured_nonce'], 'custom_save_data') ) {
return;
}
if( defined('DOING_AUTOSAVE') && DOING_AUTOSAVE ){
return;
}
if( ! current_user_can( 'edit_post', $post_id ) ) {
return;
}
if ( isset( $_POST['_featured_post'] ) ) {
update_post_meta( $post_id, '_featured_post', 1 );
} else {
delete_post_meta( $post_id, '_featured_post' );
}
}
Notice that you do not need to use sanitize_text_field(). Why? Because the value will only be a 1. That's it. Here we can hardcode that value.
Another Problem I Saw
Running your code I saw that upon being checked, the HTML for checked=checked is being rendered as text. Why? Because running the function checked() will echo it out to the browser unless you tell it not to. You'd have to check your code to:
checked(1, $featured, false);
That's happening because of return after the check if _featured_post is set in $_POST array. It's not set when you do not check the checkbox. Your approach here should be next
if (isset($_POST['_featured_post']))
{
update_post_meta($object_id, '_featured_post', 1); // can only be 1 when checked anyway
}
else
{
delete_post_meta($object_id, '_featured_post');
}

Woocommerce custom coupon type with custom discount always return 0;

what I am trying to do is creating custom coupon type with custom programmatically discount in woocommerce using hooks. This what my code looks like
//add new coupon type called "custom_discount"
function custom_discount_type( $discount_types ) {
$discount_types['custom_discount'] =__( 'custom discount', 'woocommerce' );
return $discount_types;
}
// add the hooks
add_filter( 'woocommerce_coupon_discount_types', 'custom_discount_type',10, 1);
//function to get coupon amount for "custom_discount"
function woocommerce_coupon_get_discount_amount($discount, $discounting_amount, $cart_item, $single, $coupon) {
if ($coupon->type == 'custom_discount'){ //if $coupon->type == 'fixed_cart' or 'percent' or 'fixed_product' or 'percent_product' The code Works
$discount = 85;
return $discount;
} else {
return $discount;
}
}
//add hook to coupon amount hook
add_filter('woocommerce_coupon_get_discount_amount', 'woocommerce_coupon_get_discount_amount', 10, 5);
the coupon always return with 0(Zero) discount amount. Am I missing something.
Thanks for helping =)
Ok so this topic lead me on the road to making my own coupon type so I thought I should answer it to help others.
I tried out your code and was wondering why any normal coupon showed "-£xx" before the remove button, but the custom coupon type just was lank, no minus, no pound sign jut empty. Turns out it's an issue with how WooCommerce validates the coupon on cart products. It will become active if the coupon is valid regardless if the item in the cart are.
Your coupon needs to be either valid and valid for a product, or valid for the entire cart. However when Woocommerce checks to see if your product is valid for the product it ONLY checks if the coupon type is fixed_product or 'percent_product'. If it's anything else it runs the woocommerce_coupon_is_valid_for_product hook with the existing data but defaulting to false, so your woocommerce_coupon_get_discount_amount hook will not fire.
So you need to hook into woocommerce_coupon_is_valid_for_product and validate the product to make sure it's valid. In my case I just copied the default valid function and changes $this to $coupon.
// Check if coupon is valid for product
add_filter('woocommerce_coupon_is_valid_for_product', 'woocommerce_coupon_is_valid_for_product', 10, 4);
function woocommerce_coupon_is_valid_for_product($valid, $product, $coupon, $values){
if ( ! $coupon->is_type( array( 'bogof' ) ) ) {
return $valid;
}
$product_cats = wp_get_post_terms( $product->id, 'product_cat', array( "fields" => "ids" ) );
var_dump( $coupon->product_ids );
// Specific products get the discount
if ( sizeof( $coupon->product_ids ) > 0 ) {
if ( in_array( $product->id, $coupon->product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $coupon->product_ids ) ) || in_array( $product->get_parent(), $coupon->product_ids ) ) {
$valid = true;
}
}
// Category discounts
if ( sizeof( $coupon->product_categories ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $coupon->product_categories ) ) > 0 ) {
$valid = true;
}
}
if ( ! sizeof( $coupon->product_ids ) && ! sizeof( $coupon->product_categories ) ) {
// No product ids - all items discounted
$valid = true;
}
// Specific product ID's excluded from the discount
if ( sizeof( $coupon->exclude_product_ids ) > 0 ) {
if ( in_array( $product->id, $coupon->exclude_product_ids ) || ( isset( $product->variation_id ) && in_array( $product->variation_id, $coupon->exclude_product_ids ) ) || in_array( $product->get_parent(), $coupon->exclude_product_ids ) ) {
$valid = false;
}
}
// Specific categories excluded from the discount
if ( sizeof( $coupon->exclude_product_categories ) > 0 ) {
if ( sizeof( array_intersect( $product_cats, $coupon->exclude_product_categories ) ) > 0 ) {
$valid = false;
}
}
// Sale Items excluded from discount
if ( $coupon->exclude_sale_items == 'yes' ) {
$product_ids_on_sale = wc_get_product_ids_on_sale();
if ( isset( $product->variation_id ) ) {
if ( in_array( $product->variation_id, $product_ids_on_sale, true ) ) {
$valid = false;
}
} elseif ( in_array( $product->id, $product_ids_on_sale, true ) ) {
$valid = false;
}
}
return $valid;
}
This question may be a little old, but there are two main coupon types, and you should add another filter based on the coupon you're adding. The filter for woocommerce_cart_coupon_types is for cart discounts, while woocommerce_product_coupon_types is for product discounts.
What you want to use is the second one woocommerce_product_coupon_types when you want WooCommerce to automatically validate the product/category_in and product/category_exclude fields etc.
You can still use the answer submitted by Chris for any manual validation, without using the above filters as required, however, please note that there are two validation functions to use/filter: $coupon->is_valid_for_cart() and $coupon->is_valid_for_product().

How to Hide Payment Method in Woocommerce based on Postal Code

In this woocommerce setup, I have 2 Payment methods, Paypal and Cash on Delivery.
Now how can Cash on Delivery be hidden/disabled for certain Postal codes only.
This is the code I found on Gist
// Disable gateway based on country
function payment_gateway_disable_country( $available_gateways ) {
global $woocommerce;
if ( isset( $available_gateways['ccavenue'] ) && $woocommerce->customer->get_country() <> 'IN' ) {
unset( $available_gateways['ccavenue'] );
} else if ( isset( $available_gateways['paypal'] ) && $woocommerce->customer->get_country() == 'IN' ) {
unset( $available_gateways['paypal'] );
}
return $available_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'payment_gateway_disable_country' );
Gist Link
To disable/hidden "Cash on Delivery", Place this code in your theme's function.php .
For more detail: woocommerce-hide-payment-gatway-based-on-visitors-country
// Disable gateway based on country
function payment_gateway_disable_country( $available_gateways ) {
global $woocommerce;
if ( isset( $available_gateways['cod'] ) && $woocommerce->customer->get_country() <> 'IN' ) {
unset( $available_gateways['cod'] );
}
return $available_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'payment_gateway_disable_country' );
In the "checkout page" user can have two addresses - billing and shipping one.
To work correctly only with changes of Shipping one if it's filled I changed a bit the code. You have to test shipping countrycode if it's set, if not just user countrycode:
function payment_gateway_disable_country( $available_gateways ) {
global $woocommerce;
$country = !empty($woocommerce->customer->get_shipping_country()) ? $woocommerce->customer->get_shipping_country() : $woocommerce->customer->get_country();
if ( isset( $available_gateways['cod'] ) && $country <> 'CZ' ) {
unset( $available_gateways['cod'] );
}
return $available_gateways;
}
add_filter( 'woocommerce_available_payment_gateways', 'payment_gateway_disable_country' );
In the code above you used country code to disable payment gateway by, but you mentioned you would like to do it by Postal Code.
You're right about using woocommerce_available_payment_gateways, but instead of using $woocommerce->customer->get_country() you have to use WC()->customer->get_shipping_postcode() (or WC()->customer->get_billing_postcode() for some situations).
You mentioned PayPal and Cash on Delivery payment gateway, we need their IDs, there are paypal and cod accordingly.
In the code below let's deactivate Cash on Delivery for a couple postal codes, for example '1234' and '5678':
add_filter( 'woocommerce_available_payment_gateways', function( $available_gateways ) {
// if Cash on Delivery is already disabled, let's exit the function
if( empty( $available_gateways['cod'] ) ) {
return $available_gateways['cod'];
}
// get postal code
$postal_code = WC()->customer->get_billing_postcode();
// deactivate payment method
if( in_array( $postal_code, array( '1234', '5678' ) ) ) {
unset( $available_gateways['cod'] );
}
return $available_gateways;
} );
Code can be inserted to your current theme functions.php file or a custom plugin. More info you can find in this tutorial: https://rudrastyh.com/woocommerce/hide-payment-methods-based-on-postal-code.html

Resources