WooCommerce Subscriptions hook: Get Order Items - php

WooCommerce 3.0 broke my app and I cannot figure out how to fix it now.
I have an action for when a subscription is added/changed running here:
Inside the function I was getting the order details and finding the line item for a variable subscription to update my custom DB with the option as well as getting custom order meta that I added via woocommerce_form_field:
This no longer works and everything appears protected? How can I update this to work with 3.0?
add_action( 'woocommerce_subscription_status_changed', 'update_subscription', 10, 3 );
function update_subscription( $id, $old_status, $new_status ) {
$sitelink_db = new SSLA_DB_Sitelink();
$order = new WC_Order( $id );
$items = $order->get_items();
$subscription_type = '';
$user_id = $order->get_user_id();
$sitelink_domain = get_post_meta( $order->id, 'ssla_sitelink_url', true );
foreach ($items as $item) {
if( "SiteLink Subscription" === $item['name'] ) {
$subscription_type = $item['brand'];
}
}
$customer_data = array(
'user_id' => $user_id,
'subscription_type' => $subscription_type,
'domain_referrer' => $sitelink_domain,
'active_subscription' => $new_status,
'date_modified' => date( 'Y-m-d H:i:s' ),
);
$sitelink_db->add( $customer_data );
}
Basically I need to get that variation name of the subscription to store in my DB, as well as that custom meta field I made. Which does not work anymore either

Here's my best guess. It's impossible to test since I don't have the same setup as you.
Few notes:
The $subscription object is passed to the woocommerce_subscription_status_changed hook so let's use it.
$order->id should be replaced by $order->get_id() in WC3.0, but we're going to use the the $subscription object (the subscription order class extends the order class so it's similar).
getters must be used on the WC_Order_Item_Product object that is returned when looping through get_items() so $item['name'] becomes $item->get_name()
Here's the full code block:
add_action( 'woocommerce_subscription_status_changed', 'update_subscription', 10, 4 );
function update_subscription( $subscription_id, $old_status, $new_status, $subscription ) {
$match_this_id = 99; // Change this to the product ID of your special subscription
$sitelink_db = new SSLA_DB_Sitelink();
$items = $subscription->get_items();
$subscription_type = '';
$user_id = $subscription->get_user_id();
$sitelink_domain = $subscription->get_meta( 'ssla_sitelink_url' );
foreach ($items as $item) {
if( $match_this_id === $item->get_product_id() ) {
$product = $item->get_product();
if( $product->is_type( 'variation' ) ){
$subscription_type = $product->get_attribute( 'brand' );
}
}
}
$customer_data = array(
'user_id' => $user_id,
'subscription_type' => $subscription_type,
'domain_referrer' => $sitelink_domain,
'active_subscription' => $new_status,
'date_modified' => date( 'Y-m-d H:i:s' ),
);
$sitelink_db->add( $customer_data );
}

Related

Show billing email from customer orders in WooCommerce My account Edit address

I Want to show meta "email" of a customers each order, but this is throwing an error.
add_action( 'woocommerce_before_edit_account_address_form', 'save_billing_email_to_user', 12, 1 );
function save_billing_email_to_user( $user_id ) {
$query = new WC_Order_Query(
array(
'customer_id' => $user_id
)
);
$orders = $query->get_orders();
foreach($orders as $ro => $inn){
echo $ro['email'];
}
}
What Im doing wrong here?
Use instead the following revisited code that will query one order to get the billing email:
add_action( 'woocommerce_before_edit_account_address_form', 'save_billing_email_to_user', 12 );
function save_billing_email_to_user( $user_id ) {
$orders = wc_get_orders( array( 'customer_id' => $user_id , 'limit' => '1' ) );
if ( ! empty( $orders ) ) {
$order = reset( $orders );
$order_id = $order->get_id();
$billing_email = $order->get_billing_email();
echo $billing_email;
}
}
Code goes in functions.php file of the active child theme (or active theme). Tested and works.
Or you can show the billing email address from each order of a customer like:
add_action( 'woocommerce_before_edit_account_address_form', 'save_billing_email_to_user', 12 );
function save_billing_email_to_user( $user_id ) {
$orders = wc_get_orders( array( 'customer_id' => $user_id ) );
foreach( $orders as $order ){
$order_id = $order->get_id();
echo $order->get_billing_email() . '<br>';
}
}

Woocommerce subscriptions: Updating order meta data from subscription meta

I've written the following code to take Stripe payment details from a Woocommerce subscription and update the related order with those details.
It's grabbing the details fine but the update_post_meta code doesn't seem to fire and just leaves those fields blank in the post meta, what am I missing? The rest of the code works as intended.
$order_id = wc_get_order( '143025' );
$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
$stripe_cus = get_post_meta( $current_subs_id, '_stripe_customer_id' );
$stripe_cus = $stripe_cus[0];
$stripe_src = get_post_meta( $current_subs_id, '_stripe_source_id' );
$stripe_src = $stripe_src[0];
update_post_meta( $order_id, '_stripe_customer_id', $stripe_cus );
update_post_meta( $order_id, '_stripe_source_id', $stripe_src );
}
The two strings look like cus_Hjgys757 and src_1nHyyin75 for $stripe_cus and $stripe_src.
It seems that you make a confuncion between order object and order ID in your code. Also you could try using CRUD methods instead like:
$order_id = 143025;
$order = wc_get_order( $order_id ); // Order Object
$subscriptions = wcs_get_subscriptions_for_order( $order_id, array( 'order_type' => 'any' ) ); // Array of subscriptions Objects
foreach( $subscriptions as $subscription_id => $subscription ){
$stripe_cust_id = $subscription->get_meta( '_stripe_customer_id');
$stripe_src_id = $subscription->get_meta( '_stripe_source_id' );
$order->update_meta_data( '_stripe_customer_id', $stripe_cust_id );
$order->update_meta_data( '_stripe_source_id', $stripe_src_id );
$order->save();
}
Or the old way using WordPress get_post_meta() and update_post_meta() functions:
$order_id = 143025;
$order = wc_get_order( $order_id ); // Order Object
$subscriptions = wcs_get_subscriptions_for_order( $order_id, array( 'order_type' => 'any' ) ); // Array of subscriptions Objects
foreach( $subscriptions as $subscription_id => $subscription ){
$stripe_cust_id = get_post_meta( $subscription_id, '_stripe_customer_id', true );
$stripe_src_id = get_post_meta( $subscription_id, '_stripe_source_id', true );
update_post_meta( $order_id, '_stripe_customer_id', $stripe_cust_id );
update_post_meta( $order_id, '_stripe_source_id', $stripe_src_id );
}
Both should work.

Subscription entry in the database when payment is complete

I have a problem in wordpress.
So at the end of a payment in woocommerce, I would like to make a query and insert in a table in the database the name of the user, the type of subscription and the days required.
How do you think I could do that?
So far, I've written this
function wpglorify_change_role_on_purchase( $order_id ) {
global $wpdb;
$order = new WC_Order( $order_id );
$items = $order->get_items();
$product_id = 193; // that's a specific product ID
foreach ( $items as $item ) {
if( $product_id == $item['product_id'] && $order->user_id ) {
$user = new WP_User( $order->user_id );
// Remove old role
$user->remove_role( 'Author' );
// Add new role
$user->add_role( 'Subscriber' );
$query = $wpdb->insert('wp_pms_member_subscriptions', array(
'id' => 1,
'user_id' => $user->id,
'subscribtion_plan_id' => 233,
'status' => 'active'
));
$wpdb->query($query);
}
}
}
But it doesn't really do what I want and I would like to make a query in this table and insert the member directly in the subscription table
And the name of table is
wp_pms_member_subscriptions

Only allow a customer to buy a certain product once, even if they aren't logged in

I'm trying to stop customers buying a certain product more than once, even if they aren't logged in.
I've tried a few different functions.
<?php
add_action('woocommerce_after_checkout_validation', custom_after_checkout_validation');
function custom_after_checkout_validation( $posted ) {
$address = $order->get_billing_address_1();
$email = $order->get_billing_email();
// Set HERE ine the array your specific target product IDs
$prod_arr = array( '19861', '19908' );
// Get all customer orders
$customer_orders = get_posts( array(
'numberposts' => -1,
'meta_key' => '_billing_email',
'meta_value' => $email,
'post_type' => 'shop_order', // WC orders post type
'post_status' => 'wc-completed' // Only orders with status "completed"
) );
foreach ( $customer_orders as $customer_order ) {
// Updated compatibility with WooCommerce 3+
//$order_id = method_exists( $order, 'get_id' ) ? $order->get_id() : $order->id;----------------
$orders = wc_get_order( $customer_order );
foreach ($orders as $order2) {
$address2 = $order->get_billing_address_1();
foreach($order2->get_items() as $item){
// WC 3+ compatibility
if ( version_compare( WC_VERSION, '3.0', '<' ) ) {
$product_id = $item['product_id'];
}
else {
$product_id = $item->get_product_id();
}
// Your condition related to your 2 specific products Ids
if ( in_array( $product_id, $prod_arr ) ) {
if ($address == $address2) {
wc_add_notice( __( "You have already purchased this product.", 'woocommerce' ), 'error' );
}
}
}
}
}
}
?>
This one is returning an internal server error. Please Help! TIA
You're passing a variable named $posted and inside the function you're using the $order class, which is not defined.
Change $posted to $order in the fourth line and it should work.
Edit: If it still doesn't work, the function may gets passed just the order-id instead of the class. In this case you could insert $order = wc_get_order( $posted ); after line four.

Avoiding action Being Triggered Twice in woocommerce_thankyou hook

I am using the woocommerce action below to call a custom function, but for some reason it's being triggered twice on every order. Does anyone know why this could be or how to fix it so that it only calls once per order?
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
function parent_referral_for_all( $order_id ) {
....
}
UPDATE
I thought the action was being triggered twice but I'm not so sure now. I'm using this action to add another referral inside the affiliatewp plugin, which is adding twice, yet my echo of "Thank You" only appears once.
Everything is working as intended except that the referral (and it's associated order note) are being added twice.
Any help would be greatly appreciated.
That full function:
function parent_referral_for_all( $order_id ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
To avoid this repetition, you couldadd a custom post meta data to the current order, once your "another" referral has been added the first time.
So your code will be:
function parent_referral_for_all( $order_id ) {
## HERE goes the condition to avoid the repetition
$referral_done = get_post_meta( $order_id, '_referral_done', true );
if( empty($referral_done) ) {
//Direct referral
$existing = affiliate_wp()->referrals->get_by( 'reference', $order_id );
$affiliate_id = $existing->affiliate_id;
//Total amount
if( ! empty( $existing->products ) ) {
$productsarr = maybe_unserialize( maybe_unserialize( $existing->products ) );
foreach( $productsarr as $productarr ) {
$bigamount = $productarr['price'];
}
}
//Parent amount
$parentamount = $bigamount * .1;
$affiliate_id = $existing->affiliate_id;
$user_info = get_userdata( affwp_get_affiliate_user_id( $existing->affiliate_id ) );
$parentprovider = $user_info->referral;
//Affiliate id by username
$userparent = get_user_by('login',$parentprovider);
$thisid = affwp_get_affiliate_id($userparent->ID);
$args = array(
'amount' => $parentamount,
'reference' => $order_id,
'description' => $existing->description,
'campaign' => $existing->campaign,
'affiliate_id' => $thisid,
'visit_id' => $existing->visit_id,
'products' => $existing->products,
'status' => 'unpaid',
'context' => $existing->context
);
$referral_id2 = affiliate_wp()->referrals->add( $args );
echo "Thank you!";
if($referral_id2){
//Add the order note
$order = apply_filters( 'affwp_get_woocommerce_order', new WC_Order( $order_id ) );
$order->add_order_note( sprintf( __( 'Referral #%d for %s recorded for %s', 'affiliate-wp' ), $referral_id2, $parentamount, $parentamount ) );
## HERE you Create/update your custom post meta data to avoid repetition
update_post_meta( $order_id, '_referral_done', 'yes' )
}
}
}
add_action( 'woocommerce_thankyou', 'parent_referral_for_all', 10, 1 );
I hope this will help.
You can check for the existence of the _thankyou_action_done post_meta key like so:
<?php
/**
* Allow code execution only once when the hook is fired.
*
* #param int $order_id The Woocommerce order ID.
* #return void
*/
function so41284646_trigger_thankyou_once( $order_id ) {
if ( ! get_post_meta( $order_id, '_thankyou_action_done', true ) ) {
// Your code here.
}
}
add_action( 'woocommerce_thankyou', 'so41284646_trigger_thankyou_once', 10, 1 );

Categories