RailsApps / rails-stripe-membership-saas

An example Rails 4.2 app with Stripe and the Payola gem for a membership or subscription site.

Home Page:http://railsapps.github.io/rails-stripe-membership-saas

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Can't change plans or cancel account after signing up with the same email

opened this issue · comments

Hi, I'm runing Rails 4.2 and I'm using this app (built with the composer).

I'm getting:
Completed 500 Internal Server Error in 1283ms

Stripe::InvalidRequestError (Customer cus_69I57CUBHgGx3z does not have a subscription with ID sub_69I53Q4VAeansf):
app/controllers/registrations_controller.rb:72:in `cancel_subscription'

This only happens if signup with the app under one of the plans (example: monthly) and then I login to that account as a user and switch plans (example: to yearly). When I log back in to cancel the account, I get this error.

The thing is, the customer it's complaining about is one I created (using the same email address) about 15 minutes before the one I'm trying to cancel now and that I was able to sign up , switch plans and cancel with no errrors. Also I verified the user was completely removed from the db when I cancelled. But the customer is still in stripe.

Now I've created a new account with that same email address (and was given a new customer ID in stripe) I can see my plan change didn't work either for this account.

So basically if a user signs up with the same email address, it will create two accounts in stripe but only update one that could potentially (or actually) be deleted from the site db.

update: it looks like the first subscription was not removed from the payola subscriptions db table as well. When I clicked "cancel account" it removed the user from the db but not the subscription on the first account. It also didn't delete the customer from stripe. So I guess It's better to add a check to not allow someone to signup with the same email address if there is already and existing subscription with that email attached in the payola subscriptions table. Or it would be great if there was another way to track subscriptions for a user other than email addresses (like a subscription id).

Thanks for the report (especially the careful investigation and write-up).

Devise (which manages accounts in the app) won't let someone create an account with a duplicate email address. The connection to Stripe (to obtain a Stripe token) happens before the attempt to create a Devise account. But the Payola::CreateSubscription call happens only after a Devise account is created. So in theory, it should not be possible to create two accounts with the same email address.

I'd like to ask other developers to reproduce this error and investigate further.

Thanks Daniel. :)

I think you are 100% correct if I hadn't actually cancelled the first account (which removed the Devise user) and then re-signed up with the same email. Devise would have given me the smackdown for trying to use the same email address.

However in this case, the Devise user with that email no longer existed so Devise still let me create a new account with that email and Payola created a new subscription for it in the db and on stripe. It appears having two subscriptions with the same email in the db is causing this issue. And the reason why there are two is because when a user cancels their account the subscription doesn't get deleted from the database or Stripe. Double wammy. :)

In the real world, this would happen quite often if a user decides they no longer want the account and cancels it but then decides later to come back (we can only hope). :)

It seems the cancel account feature should remove the user and payola subscription. Or Payola should say no to duplicate emails on subscriptions as well but I'd like both. And it appears having two subscriptions keeps the second account from canceling altogether. Seems like Payola is only dealing with the very first subscription it finds using the user email address and doesn't care if it's active or not.

I've solved this issue. The bug is the find_by call in RegistrationsController#cancel_subscription.

The line:
subscription = Payola::Subscription.find_by!(email: current_user.email)

Needs to be updated to:
subscription = Payola::Subscription.find_by(email: current_user.email, state: 'active')

Alternatively, it might make more sense to delete the payola_subscription record when Customers cancel their accounts.

You're awesome JM!

I actually changed these lines for updating and cancelling the subscriptions to:

Update:
subscription = Payola::Subscription.find_by!(owner_id: current_user.id, state: 'active')

Cancel:
subscription = Payola::Subscription.find_by!(owner_id: current_user.id, state: 'active')

*Just added the active state due to your reply. But I don't think finding by email is a good idea either because when the user updates the account email address it doesn't get updated in the subscription record for that account or on Stripe. So then find by email no longer works.

Also in app/services/payola/cancel_subscription.rb, I changed the code below and it deleted the stripe user and subscription from stripe.com but died in the app and didn't delete the user or any of the associated records from the db.

From This:
customer = Stripe::Customer.retrieve(subscription.stripe_customer_id, secret_key)
customer.subscriptions.retrieve(subscription.stripe_id,secret_key).delete({},secret_key)

To This:
customer = Stripe::Customer.retrieve(subscription.stripe_customer_id).delete
customer.subscriptions.retrieve(subscription.stripe_id).delete

Awesome! Thanks for the contributions. Open source FTW!

I've updated the code in the repository.

@threewest you said you made a change in code to app/services/payola/cancel_subscription.rb. That's in the Payola gem itself, not in this rails-stripe-membership-saas application, if I understand you correctly. Could you open an issue on the Payola gem repo and see if Pete Keen can fix it (or submit a pull request there).

Hi Daniel,

I moved part of this to the Payola repo. I wasn't sure if you guys were working together or not. :)

Actually, the issue with the email address not updating. Wouldn't that be an issue with this app? Updating the email via "edit account" seems like there should be some code to also make the change in the subscription in the DB which should then trigger some kind of webhook event to update the email on stripe....

To do a little cleanup on the Payola side to avoid having two subscriptions in the database, I changed this:

def cancel_subscription
subscription = Payola::Subscription.find_by!(owner_id: current_user.id, state: 'active')
Payola::CancelSubscription.call(subscription)
end

To this:
def cancel_subscription
subscription = Payola::Subscription.find_by!(owner_id: current_user.id, state: 'active')
Payola::CancelSubscription.call(subscription)
subscription.destroy
end

And now the subscription is removed from the Payola subscription table when you use the subscription cancel feature of the app. I don't know if the "Stripe sync" feature of Payola will then remove the subscription from Stripe.

@threewest: I considered doing that too, but then I decided it's probably valuable to have some record of inactive subscriptions somewhere.

@jmschultz Ultimately I feel the same way but I feel uneasy about how Payola handles subscriptions. For instance, it seems like it searches for the very first subscription it finds based on the email address (to change the plan). So if you have two subscriptions in there with the same email address and the first one is inactive, Payola will still only deal with that first one. So until I have an idea of what's going on with Payola, I feel more comfortable just deleting the subscription from the DB. It looks like the cancelled subscription does stay in Stripe though if you want to leave a record there (confirmed with Peter, Payola dev, that the subscription stays on Stripe as "cancelled" so you'll have that record). I'm doing more testing this weekend as this may all be "over" with the changes we made with adding state: "active" but I want to make sure. :)

I created code to update the Payola_Subscriptions table and Stripe.com