Reputation: 2914
I am using Stripe for my subscriptions and I have it set so when a user cancels their subscription (to turn off automatic renewal) it will keep the subscription active until the end of the billing period on Stripe.
The action works via Stripe, but how can I setup so that the cancelled
column in my database takes the same affect? Currently if the user clicks on the cancel subscription link it will mark their cancelled
column to 1
. I would like for it to not mark as cancelled until the end of their billing period so the user can continue have access to the website until their final billing day (I have autorenwal turned on)
I have read txdavidtx suggestion. What he suggests would mark all Users as cancelled at the end of their billing period. That method would not fit with what I am looking to accomplish.
I have subscriptions set to autorenew. I would need a cancel
action created that would only mark the current_user
as cancelled at the end of their billing period.
For example:
User A signs up for the monthly subscription on September 27. User A decides on December 15 they want to cancel their subscription. User A still has 12 days left in their subscription. User A clicks on the cancel
link. User A has autorenew and subscription cancelled in their PayPal or Stripe account. Inside my database their cancelled
attribute value will not change until those 12 days have finished (December 27).
If someone can assist that would be great.
Subscriptions controller:
def new
plan = Plan.find(params[:plan_id])
@subscription = plan.subscriptions.build
render layout: 'new_application'
if params[:PayerID]
@subscription.paypal_customer_token = params[:PayerID]
@subscription.paypal_payment_token = params[:token]
@subscription.email = @subscription.paypal.checkout_details.email
end
end
def create
@subscription = Subscription.new(params[:subscription])
if @subscription.save_with_payment
redirect_to @subscription, :notice => "Thank you for subscribing!"
else
render :new
end
end
def show
@subscription = Subscription.find(params[:id])
render layout: 'new_application'
end
def paypal_checkout
plan = Plan.find(params[:plan_id])
subscription = plan.subscriptions.build
redirect_to subscription.paypal.checkout_url(
return_url: new_subscription_url(:plan_id => plan.id),
cancel_url: root_url
)
end
def updatesubscription
@user = current_user
@customer = Stripe::Customer.retrieve(@user.subscription.stripe_customer_token)
if @user.subscription.plan_id == 12
@customer.update_subscription(:plan => "1", :prorate => true)
current_user.subscription.update_attributes(:plan_id => 1)
flash.alert = 'Your subscription has been changed to monthly!'
redirect_to root_url
elsif @user.subscription.plan_id == 1
@customer.update_subscription(:plan => "12", :prorate => true)
current_user.subscription.update_attributes(:plan_id => 12)
current_user.save!
flash.alert = 'Your subscription has been changed to annually!'
redirect_to root_url
end
end
def cancelsubscription
@user = current_user
@customer = Stripe::Customer.retrieve(@user.subscription.stripe_customer_token)
@customer.cancel_subscription(:at_period_end => true)
current_user.subscription.update_attributes(:cancelled => 1)
current_user.save!
flash.alert = 'Your subscription has been cancelled successfully!'
redirect_to root_url
end
def showcard
@user = current_user
Stripe::Customer.retrieve(@user.subscription.stripe_customer_token).cards.all()
end
def suspend
@user = current_user
@user.subscription.suspend_paypal
current_user.subscription.update_attributes(:cancelled => 1)
flash.alert = 'Billing has been suspended!'
redirect_to root_url
end
def reactivate
@user = current_user
@user.subscription.reactivate_paypal
current_user.subscription.update_attributes(:cancelled => nil)
flash.alert = 'Billing has been activated!'
redirect_to root_url
end
def edit_card
@user = current_user
end
def update_card
@user = current_user
card_info = {
name: params[:name],
number: params[:number],
exp_month: params[:date][:month],
exp_year: params[:date][:year],
cvc: params[:cvc]
}
if @user.subscription.update_card(@subscriber, card_info)
flash.alert = 'Saved. Your card information has been updated.'
redirect_to root_url
else
flash.alert = 'Stripe reported an error while updating your card. Please try again.'
redirect_to root_url
end
end
end
Upvotes: 2
Views: 2085
Reputation: 10874
The easiest solution to this is, for the method to check if the user still has access, check that the next recurring date is in the future.
This allows you to mark canceled
as 1
instantly.
Upvotes: 0
Reputation: 1881
You need to updated the cancelled column of your user at then end of their trial- not when they actually cancel it.
Kimooz almost had it right. You're going to use a Stripe Webhook and I'll explain what you need to do in order to achieve what you want.
First, if I understand correctly you want to mimic what happens in Stripe. Basically, you want your database cancelled column to remain false on the user until the user has expired all their time. This means if your user clicks 'Cancel', nothing changes in cancelled column until ALL time has expired, at which point it should then be set to true.
So, cancel your users account with Stripe like you normally would when the user clicks cancel. The only thing different about your users Stripe::Subscription object at this point is that the property cancel_at_period_end
gets set to true. The subscription.status
property remains active
until all the time has expired. Your user record remains the same until ALL time has expired.
Then, set up a Stripe Controller to handle the webhooks. It may seem like a pain to go through this process but this is the proper way to achieve the solution.
You need to parse the webhooks at this point for the customer.subscription.updated
event NOT the customer.subscription.deleted
event. Stripe will only update your customers subscription.
Stripe will not delete your customers subscription if you cancel it
... it will only update the status of the subscription if you cancel it. This means the subscription and the information associated it remains persisted with Stripe and it still belongs to your user if they were to renew it later.
Okay, so.. say your user cancels on Dec. 15, but their subscription ends Dec. 31. Stripe will send two events to your webhook.
Dec. 15: This event will be a customer.subscription.updated
event that will tell you that cancel_at_period_end
property has been updated to true
. (the status
property remains unchanged and active
)
Dec. 31.: This event will be a customer.subscription.updated
event that will tell you that the status
property has changed from active
to cancelled
.
When the second event is sent your Stripe Webhook, you now can parse the event and change the cancelled
status in your own database since all time has expired.
Upvotes: 1
Reputation: 961
I think the easiest way is to use stripe webhooks, where as soon as the subscription ends stripe will ping your system with a customer.subscription.deleted event and at that point you should just cancel the user subscription.
The setup is really easy.
Detect the user and cancel his subscription with at_period_end params true
customer = Stripe::Customer.retrieve("cus_3R1W8PG2DmsmM9") customer.subscriptions.retrieve("sub_3R3PlB2YlJe84a").delete(:at_period_end => true
A customer.subscription.updated event is immediately triggered if you cancel a subscription at the end of the billing period instead, reflecting the change in the subscription’s cancel_at_period_end value. When the subscription is actually canceled at the end of the period, a customer.subscription.deleted event will occur. Stripe Doc
4- Then Setup your callback controller and detect subscription deleted
<pre>
class StripeEventsController
skip_before_filter :verify_authenticity_token
def catch
object = params[:data][:object]
case params[:type]
when "customer.subscription.deleted"
customer = object[:id]
user = User.find_by_stripe_id(customer)
user.subscription.update_attributes(cancelled: "1")
end
end
end
Upvotes: 8
Reputation: 4633
Bad planning IMO:
Your subscription seems to be based on days remaining. So you just need to ensure that days remaining are not 0 when asking for user_is_subscripted.
current_user.subscription_expired?
def subscription_expired?
days == 0
end
Create a column: subscription days, and each time your user renews the subscription, add 30 days to the count. Each day run a script that reduces the subscription day in that column by 1. This way you don't need to take in account dates.
Also:
if subscription_expired? and autorenewal?
renew_subscription(days)
end
def renew_subscription(days)
#paypal magic
current_user.subscription_days += days
end
And when asking for expiration day:
def expiration_day
Time.now + current_user.subscription_days.days
end
Upvotes: 0
Reputation: 179
I would suggest one of two things.
First option:
Add a :cancellation_date attribute then create a helper method in your application controller that checks for the date of cancellation at sign in. If they're over their subscription end date, have your :subscription set itself to "1".
# application_controller.rb
def my_helper
if current_user.has_cancelled?
redirect_to registration_path
end
end
# user.rb although better to have the subscription do it instead
def has_cancelled?
if cancellation_date.present? && cancellation_date > (paid_on + 30)
subscription.update_attributes(cancelled: "1")
return true
else
return false
end
end
Second option:
Use a background job like sucker_punch or sidekiq to run every morning and set expired subscriptions to "1".
Upvotes: 0