I'm aware of the customer.subscriptions.trial_will_end event. It fires 3 days before a trial ends.
I couldn't find an event that actually fires when the trial is over and the customer hasn't paid. This would be useful to do something simple like this to turn off features:
customer.update_attributes(active_account: false)
Without a webhook like that, I'm looking at scheduling some tasks to check unconfirmed customers periodically and turn off features accordingly. The webhook seems cleaner though and less prone to errors on my side. Is there an event/webhook in line with these goals? FYI, customers don't have to put in a card when they start the trial - so autobilling is not an option.
When the trial period ends, there will be a customer.subscription.updated event and an invoice.created event. An hour (or so) later, you'll then either see an invoice.payment_succeeded event or an invoice.payment_failed event. From those, you'll know whether the payment went through or not.
Cheers,
Larry
PS I work on Support at Stripe.
To add to Larry's answer and share how I got around the lack of a trial ended webhook, here's what I did.
In invoice.payment_failed webhook, I checked:
Is this the first invoice since the subscription start?
Does the customer have any cards saved?
If these checks fail, then I assume the trial has just ended with no billing details entered, and I cancel the subscription.
Example in Python:
# get account from my database
account = models.account.get_one({ 'stripe.id': invoice['customer'] })
# get stripe customer and subscription
customer = stripe.Customer.retrieve(account['stripe']['id'])
subscription = customer.subscriptions.retrieve(account['stripe']['subscription']['id'])
# perform checks
cards_count = customer['sources']['total_count']
now = datetime.fromtimestamp(int(invoice['date']))
trial_start = datetime.fromtimestamp(int(subscription['start']))
days_since = (now - trial_start).days
# cancel if 14 days since subscription start and no billing details added
if days_since == 14 and cards_count < 1:
subscription.delete()
Just add 3 days to the free trial period and use the customer.subscriptions.trial_will_end event and update the subscription with 'trial_end=now'
I think there is another way that can be handled easly. So invoice.payment_failed should be listened, in all invoice related events, inside event.data.object, there is subscription id or subscription object, you should get subscription id and retrieve subscription then you can get both product id and price id.
By price id or by product id you can know current subscription.
Related
I have a setup where I would like to send automatic reminders for users if they haven't made a monthly rental payment. The landlord has the opinion to define the day of the month on which the tenant should make the payment(due_date). What I would like to achieve is that if the payment hasn't been made in 2 days since the due_date the tenant would be sent an email. Currently, I have the following task for this.
namespace :rent_due_reminder do
desc "Sends monthly reminders to tenants who haven't paid their rent."
task :send => :environment do
Offer.all.each do |offer|
if offer.status == "accepted" && offer.due_date.present?
landlord = User.find_by(id: offer.landlord_id)
tenant = User.find_by(id: offer.user_id)
stripe_account = Stripe::Account.retrieve(landlord.merchant_id)
payments = Stripe::Charge.list(
{
limit: 100,
expand: ['data.source_transfer.source_transaction.dispute', 'data.application_fee'],
source: {object: 'all'}
},
{ stripe_account: landlord.merchant_id }
)
due_date = "#{Date.today.year}-#{Date.today.month}-#{offer.due_date}".to_datetime
last_payment_date = DateTime.strptime(payments.data.last.created.to_s,'%s')
if (SOME CONDITION)
OfferMailer.with(offer: offer, tenant: tenant, landlord: landlord).rent_due.deliver
end
end
end
end
end
So what I'm doing is that I'm picking the last payment and getting the date of that and this is what I should use to condition the mailer. Initially, I thought that I could just look and see whether last_payment_date < due_date + 2, but this didn't seem to work. I might be getting confused about this since if I have multiple landlords and some of them have their due date for example 30th of every month and some of them have 1st of every month, should I still be running the task monthly or how should I set this up? Apologies for the reasonably bad explanation, I myself am getting very confused with this also.
There is a flaw in your logic. Even if a user made a payment on the due date, the last_payment date would still be less than due_date+2. So you need to add one more check that ensures that the payment was made for the current month.
e.g. If the due date for my rent is the 5th of every month you need to check the payment was made between 1st and 5th. Currently you are only checking if the last payment date was before 7th. That includes valid payments too.
Of course the above logic is flawed too because people could make advance payments and you will need to correct for that too. One way around it is to create a bill and ensure that all bills are paid rather than simply checking for last payments. That way you can calculate delinquencies for more than one month.
I was asked to increase the amount of all plan in Stripe. So I wrote the following script in ruby to delete old plan and create new plan with the same id with different amount:
for stripe_plan in Stripe::Plan.list
new_amount = (stripe_plan.amount*1.1).floor
Stripe::Plan.delete(stripe_plan.id)
new_stripe_plan = Stripe::Plan.create(
nickname: stripe_plan.nickname,
amount: new_amount,
interval: stripe_plan.interval,
currency: stripe_plan.currency,
product: stripe_plan.product
)
The problem is if one fails (most likely not), it might result in some plans are updated and others are not, which is not consistent.
The question is: Is there any transaction method to rollback Stripe create, update, delete when it fails?
If this is not a question for stackoverflow, please comment, I will delete this.
Instead of modifying the price of existing plans, you should create new plans with the updated cost, then assign them to the appropriate users. This will allow you to simply check if any users are on old plans if something were to go wrong.
In QBO API explorer, for the Purchase API V3, when I tried to get all credit card transaction. The query I used was "select * from Purchase where PaymentType = 'CreditCard'". This only returned 4 transactions whose value for is true, in other words, they are refunds. My most recent charges were not returned. The default value for this attribute is False. When I manually made those as true then they showed up.
Is there a way around this? I don't understand why by default only the refunds were returned by my query. I want all credit card transactions, not just refunds.
https://developer.intuit.com/apiexplorer?apiname=V3QBO#Purchase
https://developer.intuit.com/docs/0025_quickbooksapi/0050_data_services/030_entity_services_reference/purchase
Purchase entity query on PaymentType gives incorrect results. This is a known bug and will be resolved in our upcoming releases.
Bug number-QBO-35066
Refer-
https://developer.intuit.com/docs/0025_quickbooksapi/0058_faq/qbo_v3_known_issues
I would like to add a invite code requirement for users to register in my application. I investigated the devise_invitable gem for devise and this looks very promising. However, It should not be possible to continuously invite people to the application, like devise_invitable does.
To solve the problem, I need to implant user levels in my application. I found this project on github and now I got the following idea: Once a user registers (invited by another existing user) it starts in level 1 and must complete tasks in order to archive experience points. He works his/her way up to the next level where he can invite 1 new member and then in the next level he can invite 1 other and in the next level, the user can invite 2 members, so on, so on.
I am fairy new to Ruby and I'd like to know how to encomplish this and to know if this is even possible to insert in my users controller.
Thank you for reading and have a nice day.
This should be a fairly straight forward process, as the number of invitations a user has is just an integer stored in your database. It could be something as simple as:
def level_up(level)
self.invitation_limit += level
self.save
end
While a very simple implementation, you just pass in the users level, add it to their current number of invitations, and save the user. It all really depends on how fancy you want to get, but it really comes down to basic math, and saving it to the database.
I'm not sure if this changed since the question was asked in 2012, but devise_invitable has an invitation_limit parameter:
invitation_limit: The number of invitations users can send. The default value of nil means users can send as many invites as they want, there is no limit for any user, invitation_limit column is not used. A setting of 0 means they can't send invitations. A setting n > 0 means they can send n invitations. You can change invitation_limit column for some users so they can send more or less invitations, even with global invitation_limit = 0.
You can see how this parameter is used by looking at the source here. One important part:
# Return true if this user has invitations left to send
def has_invitations_left?
if self.class.invitation_limit.present?
if invitation_limit
return invitation_limit > 0
else
return self.class.invitation_limit > 0
end
else
return true
end
end
I am currently working on a project and facing a problem with a task. I am trying to randomly generate a 6 digit coupon number and post it to chargify account via there API. If the coupon creation is successful I want the same coupon code to be send to the customer through Email.
As per chargify documentation this is how I should send all the details to chargify from my application :
{"subscription":{
"product_handle":"[#product.handle]",
"customer_attributes":{
"first_name":"Joe",
"last_name":"Blow",
"email":"joe#example.com"
},
"credit_card_attributes":{
"full_number":"1",
"expiration_month":"10",
"expiration_year":"2020"
},
"coupon_code":"6 digit random code"
}}
"""
https://[#subdomain].chargify.com/subscriptions.json.
I am able to create a 6 digit random numerical code by this method :
rand(999999).to_s.center(6, rand(9).to_s).
However this does not seem to be working for me. Any suggestions would be highly appreciated.
Thanks
I'm not part of our tech or dev staff, but I'm 99% sure you can only specify a previously-defined coupon code in that API call. You must define coupon codes in the Chargify admin web interface. In the API call above, you can apply a coupon to the subscription, but the assumption is that you already defined that coupon code in the admin interface.
We will add that capability in the future, but I don't have a specific date for you.
Sorry about that.
--- Lance Walley
--- Chargify
I'm not sure what you're trying to do with your call to center. The most sensible thing to do would be to zero-fill the coupon code. This would do it:
"%06d" % rand(1000000)
This will generate codes such as "664001" and "061532".
Note that you want rand(1000000) rather than rand(999999). That's because rand gives you random integers between 0 and one less than the argument. rand(999999) will only give you random numbers up to 999998.
There's a violation of DRY (Don't Repeat Yourself) in the above code: Both the "06" and the "1000000" depend upon the length of the coupon code. Here's a fix for that:
COUPON_CODE_LENGTH = 6
"%0#{COUPON_CODE_LENGTH}d" % rand(10 ** COUPON_CODE_LENGTH)
Although longer, there's now only one thing to change if the coupon code length should change. The replacement of "magic numbers" with a named constant also helps the code to communicate its intent.