I am working on a 'Sponsor An Orphan' project using the Woocommerce Subscriptions plugin. Monthly or yearly subscriptions are available.
When a new subscription is created, I am allocating an orphan to the subscription when payment completes like this..
add_action('woocommerce_subscription_payment_complete', 'allocate_orphans');
It correctly allocated the new orphan for a new subscription but it is also allocating an orphan on every renewal.
I think I am using wrong action hook. Which action hook should I use for new subscription for new orphan (that should not allocate the orphan on next payment)?
Any help will be appreciated.
Just had the same problem myself. I think it's very strange WC haven't made this into its own hook, it would be very easy to add an else statement to the if which creates the "non-initial" payment hook that exists...
Anyway, at least we can use their logic in our action:
function assign_orphan($subscription) {
$last_order = $subscription->get_last_order( 'all', 'any' );
if ( false !== $last_order || wcs_order_contains_renewal( $last_order ) ) {
//get out of here
return;
}
//go ahead and allocate on initial sign up
}
add_action('woocommerce_subscription_payment_complete','assign_orphan',10,1);
Related
Given that I have multiple websites in my Magento instance, how do I identify the website where a particular event happened? For example, observing the checkout_cart_add_product_complete event lets me catch all Add to Cart events. Let's say I wanted to get the website Id of the website where this Add to Cart event happened, how do you I do that?
public function addToCart(Varien_Event_Observer $observer) {
$product = $observer->getEvent()->getProduct();
$websiteId = $observer->getEvent()->get ??? ();
}
I know that I can get the websiteIds of the product that was added to the cart, by doing the following
$websiteIds = $observer->getEvent()->getProduct()->getWebsiteIds();
But that is not what I want, because if the product belongs to more than one website, it will give me all the websites and not the one where the Add to Cart event happened.
Thanks
Have you tried:
Mage::app()->getStore()->getId()
within your observer? That should give you the current store id..
You can directly get the StoreId from Mage, not from the observer object.
StoreId:
Mage::app()->getStore()->getStoreId();
WebsiteId:
Mage::app()->getStore()->getWebsiteId();
I am a total beginner in programming with PHP. I wanted to create a PHP file in which the order_status from a predefined order (in my case 108) gets changed to completed.
Therefore I need the woocommerce functions get_order($ID) and update_status but I do not know how to use them in my PHP. I hope you understand my problem. From Java I could imagine that I need to get an instance from a class or something like that?
Here is the code I have so far:
<?php $ord = new WC_Order(108); $ord->update_status('completed'); ?>
When I open the page I receive the following error:
Fatal error: Uncaught Error: Class 'WC_Order' not found (...)
In general on Wordpress/WooCommerce you will include your functions code:
In your active child theme (or active theme) function.php file
In a plugin…
You can also enable some code in:
Your theme templates
WooCommerce templates that you will override through your active child theme (or active theme).
Now to execute that function, you will need an event that will execute your function.
In (Wordpress) Woocommerce there is a lot of action hooks that are triggered on some specific events that you can use to execute your function. In this case your function will be hooked (ready to be executed on a specific event).
If you want to change the status of a specific order is better to do it in the related order edit page in backend.
An example:
For example you can change the order status when a customer has submit his order after checkout on order-received end point (thankyou page):
add_action( 'woocommerce_thankyou', 'custom_woocommerce_auto_complete_order');
function custom_woocommerce_auto_complete_order( $order_id ) {
if ( ! $order_id ) return;
// Get an instance of the WC_Order object
$order = wc_get_order( $order_id );
// Change order status to "completed"
$order->update_status( 'completed' );
}
This code is an official code snippet: Automatically Complete Orders.
It is a good example that shows you how things can work… So in your case you are using here WC_Order class methods like update_status().
Now with this code base, you can refine the behaviors like in this answer:
WooCommerce: Auto complete paid Orders (depending on Payment methods)
Related to orders: How to get WooCommerce order details
According to the Stripe documentation, it appears as though you can actually apply coupons to a customer as a whole, but you can also apply coupons to their subscriptions. I am trying to apply a coupon to the customer's subscription after the subscription has already been created. I am using Laravel Cashier 4.2.
Here is what I have tried:
$company = Auth::user()->company;
$customer = $company->subscription()->getStripeCustomer(); // returns a StripeGateway object
$customer->subscription->applyCoupon($input['coupon_code']);
Here is the error message:
"Call to undefined method Stripe_Subscription::updateSubscription()"
I can use the applyCoupon() method to a customer as a whole, but not an actual subscription... Ideas appreciated.
The Stripe documentation only shows how to remove a discount from a subscription:
Discount Object. The only other information I've been able to find in their documentation is:
Coupons and discounts
If you want to provide discounts to certain customers, you can create coupon codes in the Dashboard. Coupons have a discount percentage and a duration, so you could create coupons like a 10% lifetime discount, or a one month 50% discount, etc. Coupons can also have expiration dates attached, after which they can't be used. Here's an example of adding a discount to a user's subscription. In this case, we've already created a coupon called 50OFF1MONTH:
curl https://api.stripe.com/v1/customers/cus_4fdAW5ftNQow1a \
-u sk_test_4LOIhlh19aUz8yFBLwMIxnBY: \
-d coupon=50OFF1MONTH
However, this isn't very helpful. Once again, Laravel's documentation is a little to elegant, and it's missing any info on the topic.
I wonder if I just need to recreate the subscription object entirely with the new coupon... But that's not ideal because you need a card token for that.
Update 1
I can confirm that this does in fact apply a coupon to the subscription itself
$user->subscription('monthly')
->withCoupon('code')
->create($creditCardToken);
However, once again the question is how can a coupon be added after the fact.
Starting with Cashier v11.* there are new convenience methods to update the underlying Stripe subscription object. To apply a coupon to the user's Subscription model instead of the Customer model, try this in the controller:
$user->subscription('default')->updateStripeSubscription(['coupon' => $couponCode]);
To remove the coupon from the subscription, send an update request and pass an empty coupon string. As described under another question, a coupon set to an empty string removes it, while passing null is ignored.
$user->subscription('default')->updateStripeSubscription(['coupon' => '']);
It doesn't look like what I'm trying to do is possible, so I found another way. All I do is swap the users existing plan to the current plan and use the withCoupon method. This does appear to apply the coupon to the subscription and doesn't seem to charge the user or change their billing period, but I could be wrong...
$company->subscription($company->stripe_plan)
->withCoupon($input['coupon_code'])
->swap();
Adding a Coupon to an Existing Subscription.
You can also use cashier method as below:
try {
$user->applyCoupon('coupon_code');
return redirect()->back()->withMessage('Coupon Applied');
} catch (Exception $e) {
return back()->withError($e->getMessage());
}
When applying a coupon, it's best to wrap this in a try / catch block because if the user enters an invalid coupon, Stripe will return an exception with the reason for the failure.
For everyone who can't find an easy and convenient way to swap the subscriptions with a coupon, here's what I came up with recently.
First of all, we'll have to create a custom Subscription eloquent model and extend Laravel\Cashier\Subscription class.
Now we're going to create the withCoupon() method there:
public function withCoupon($coupon)
{
$this->coupon = $coupon;
return $this;
}
The next thing will be extending the original swap() method and adding the coupon part. Just copy and paste the original method and add this piece of code somewhere before the $subscription->save(); line.
...
// Applying the discount coupon if such exists
if ($this->coupon) {
$subscription->coupon = $this->coupon;
}
...
$subscription->save();
The last thing you've got to do in order to make things work properly is to create a custom Billable trait and change subscriptions() method to use your custom Subscription model.
After that, you can apply your very own Billable trait to the billable model and apply coupons while swapping plans.
$user
->subscription($product)
->withCoupon($coupon)
->swap($plan);
Using Laravel 7.x with Cashier 10 you have the applyCoupon() method on a Billable (User) model.
Something like this in the controller:
/**
* Apply Stripe coupon
*/
public function applyCoupon(Request $request){
$user = $request->user();
if ($user->subscribedToPlan('plan_name', 'default')) {
try {
$user->applyCoupon($request->couponCode);
return redirect('subscription')->withMessage('Coupon Applied');
} catch (\Exception $e) {
return redirect('subscription')->withError($e->getMessage());
}
}
}
I sell two services in my Magento store. I've disabled cart and multi-page checkout. I want to sell only one service at a time. Means I want to accomplish, if customer tries to add both services to cart so the previous service should be removed.
How can I accomplish this? I've been searching this from last 5 hours.
in the file
app/code/core/Mage/Sales/Model/Quote.php
there is a method public function addProduct($product, $request = null);
you should only add $this->removeAllItems(); to be the first line in the method, like:
public function addProduct(Mage_Catalog_Model_Product $product, $request = null)
{
$this->removeAllItems(); // new code
....
}
of course, it's better idea to be overridden in the local pool.
I'm working on an extension for Magento (1.4 CE) that needs to be triggered once an order has been paid for. I'm having trouble finding an event to hook in to that will trigger when Paypal IPN (Paypal Standard) has done its thing.
I've tried using the sales_order_invoice_save_after and sales_order_invoice_register events, but neither of these seem to be triggered by a Paypal IPN response.
I'm now trying to use the sales_order_save_after event to detect when the order enters the "processing" status, like so:
class Lightbulb_Blastramp_Model_Observer {
public function sendOrderToBlastramp(Varien_Event_Observer $observer) {
Mage::log('Start' . "\n\n", null, 'blastramp.log');
$order = $observer->getEvent()->getOrder(); // get order data
// make sure the order is in the processing state
if ($order->getState() != Mage_Sales_Model_Order::STATE_PROCESSING) {
Mage::log('Not processing, return.' . "\n\n", null, 'blastramp.log');
return $this;
}
// order has reached "processing" state, do stuff...
}
}
From the log files I can see that my code is triggered when the order is initially created with the "payment pending" state, but does not get triggered when it moves to the "processing" state. Is there some event I can hook in to that will trigger when an order hits the "processing" stage as set by Paypal IPN?
Cheers
After struggling with this for a while, I eventually had success with this by overriding the _isCaptureFinal($amount) method in Mage_Sales_Model_Order_Payment.
eg.
class Lightbulb_Blastramp_Model_Order_Payment extends Mage_Sales_Model_Order_Payment {
public function _isCaptureFinal($amount) {
// do things here
return parent::_isCaptureFinal($amount);
}
}
This is thanks to the answer to another question: https://stackoverflow.com/a/5024475/602734
Hopefully this helps someone!
In case anyone stumbled upon this question like i did, an observer that does this as was originally requested;
checkout_onepage_controller_success_action
This returns just the order id, so;
$order_id = $observer->getData('order_ids');
$order = Mage::getModel('sales/order')->load($order_id);
and you see that the order status is 'processing' and the payment is aproved (or not).