-
Notifications
You must be signed in to change notification settings - Fork 28
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks good to me, few tiny comments 🎉
(i) Have placed validation handling (if the subscription exists and belongs to the member) in the route handler itself. Was wondering if this kind of checks might belong on the stripe class itself?
That's a good point - the Stripe class already deals with the stripe database stuff, so it's not just stripe - I think a method that either verifies a subscription belongs to a member, or fetches a members subscription (or null) by id would work, or leave it as it is! - your call though
(ii) Another topic is how the deletion is handled on our side. Right now the flow would be following:
...
Should we be removing the record in the db in pt.3 as well thinking this would prevent the pt. 4 being a little confusing.
Yes 100% and we should probably do the same in the cancel all subscriptions method too!
@allouis fyi-sidenote:
While working on this bit had a debugger sitting on one of the webhooks. And had ended up having this state: mysql> select id, status, customer_id from members_stripe_customers_subscriptions where customer_id = 'cus_GIqT1EGK6l6INb';
+--------------------------+--------+--------------------+
| id | status | customer_id |
+--------------------------+--------+--------------------+
| 5de8b2399f966361b18059a1 | active | cus_GIqT1EGK6l6INb |
| 5de8b2399f966361b18059a2 | active | cus_GIqT1EGK6l6INb |
+--------------------------+--------+--------------------+
2 rows in set (0.00 sec) It's a very unlikely state to get into, but think we would benefit having at least some basic db level protection (unique key constraint) for columns like |
Can you rerun that query and include the We can't have a unique constraint on There should be a unique constraint on the |
@allouis soz, bad news 😬 mysql> select id, status, subscription_id, customer_id, updated_at from members_stripe_customers_subscriptions;
+--------------------------+----------+--------------------+--------------------+---------------------+
| id | status | subscription_id | customer_id | updated_at |
+--------------------------+----------+--------------------+--------------------+---------------------+
| 5de8d6588f2002187a758993 | active | sub_GIsyg8dyUlvASo | cus_GIswmsKIExk3IC | 2019-12-05 10:05:12 |
| 5de8d6588f2002187a758994 | active | sub_GIsyg8dyUlvASo | cus_GIswmsKIExk3IC | 2019-12-05 10:05:12 |
Yes, |
💃 Yup deffo! There might have been a reason we couldn't have that 🤔 Are you able to replicate this? |
Yes, this new state was made from scratch. To have it can:
|
Think this should've been But yeah, good question: was there a reason we couldn't have that? |
* @param {Object} subscription | ||
*/ | ||
async handleCustomerSubscriptionDelete(subscription) { | ||
return await this.storage.set({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@allouis have done the cancellation initiated by the user in this way, so that it doesn't interfere with the webhook's logic. Think it can be refactored into something nicer once the subscription model is passed in directly. Wdyt?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, I would've thought we'd want to update the cancelSubscription
method to be like
const cancelledSubscription = await del(this._stripe, blah blah); // this returns full subscription obj
await this._updateSubscription(cancelledSubscription);
return cancelledSubscription;
And use that method everywhere instead of raw calls to del(.....
🤔
AHA - found it: https://github.com/TryGhost/Ghost/blob/3.1.1/core/server/data/schema/schema.js#L339 |
I think this might be caused by listening to too many related webhooks - so we should think about removing the invoice.succeeded and invoice.failed webhooks. For now it seems like a super edgecase though - need to execute 5 webhooks at the exact same time - and should be fixed by using the relation plugin? |
c59d48a
to
23b80fd
Compare
Damn... wish they were allowed to be a little shorter... ref: https://stripe.com/docs/upgrades#what-changes-does-stripe-consider-to-be-backwards-compatible
Yeah, this seems like a sensible approach. Even though, having the constraint would be so much nicer :) |
Yup - that's the reason we got them so long atm 😞 Cool okay, you think we will do that in this cycle, or next? |
I think we can do this cycle if we have time. Don't want to pile new things before we finish with current work. |
- Relies on the request containing identity information to be able to verity if subscription belongs to the user
- When member could not be identified by the identity information present in the request we should throw instead of continuing processing - Handling and messaging inspired by https://github.com/TryGhost/Ghost/blob/3.1.1/core/server/services/mega/mega.js#L132
- As we now throw when the member is not found we car remove a redundant if and simlify the check drasticly
- When user initiates subscription cancellation we can safely mark the subascription as canceled so that it's not shown in the interface on subsequent request. Otherwise we end up in situation where we still return the subscription in the period untill Stripe triggers the webhook
- Subscription might not be found so assignment would fail with 'undefined' error
d4e74d4
to
2849c17
Compare
- If anything but boolean is passed to Stripe API it throws an error. Coercing the value on our side is a gives a better dev experience
@@ -275,7 +275,7 @@ module.exports = function MembersApi({ | |||
}); | |||
} | |||
|
|||
subscription.cancel_at_period_end = cancelAtPeriodEnd; | |||
subscription.cancel_at_period_end = !!(cancelAtPeriodEnd); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@allouis tried to be malicious about what we pass along to stripe and sent different types of data through this parameter. To avoid unnecessary complexity with error handling and type checking went with coercion solution. Let me know if you'd rather go with proper type checking here and maybe throw the same way Stripe API does?
refs TryGhost/Members#107 - Added 'cancel at period end' logic to members subscription details - Added stripe subscription link in subscriptions details
Adds the ability to cancel member's subscriptions.
@allouis a structural question about this change.
(i) Have placed validation handling (if the subscription exists and belongs to the member) in the route handler itself. Was wondering if this kind of checks might belong on the stripe class itself?
(ii) Another topic is how the deletion is handled on our side. Right now the flow would be following:
3.1. At some point, webhook about the cancellation comes back and we remove the record in the DB.
Should we be removing the record in the db in pt.3 as well 🤔 this would prevent the pt. 4 being a little confusing.
Sidenote. Will bring up a topic of the policy of cancellation in separate thread (if we should cancel immediately or for example use cancel_at_period_end instead.