#mehdi_downgrade-pending
1 messages ยท Page 1 of 1 (latest)
๐ Welcome to your new thread!
โฒ๏ธ We'll be here soon! Typically we respond in a few minutes, but sometimes we might take a bit longer if the server is busy or if you have a particularly tricky question.
โฑ๏ธ We close idle threads, which makes them read-only. Once a thread is closed it won't be reopened, but you can always start a new thread if you have another question.
๐ This thread will always be available, even after it's closed. You can find it again using Discord's search, or you can save this link: https://discord.com/channels/841573134531821608/1240703932209037322
๐ Have more to share? Add more details, code, screenshots, videos, etc. below.
mehdi_downgrade-pending
Rest of the cut text :
All those updates needs to be done via pending_if_incomplete, so that the changes aren't applied until the invoice generated has been paid.
We're having difficulties implementing this logic, and we couldn't find some open source projects to see examples of how we could implement it.
@brave plover why are you doing an Invoice during a downgrade since there's nothing to pay?
Nope no invoice is being done on downgrade
Just the next phase of the subscription schedule gets updated
then why do you use pending_if_incomplete for this? You said
What we've tried is using pending if incomplete during downgrades
Oh my bad my bad, I meant 'upgrades', I apologize
totally fine
Okay so what is the problem exactly? Sorry those can be tough to grasp and explain so it would help if you walked me through one exact concrete example and the related issue
Thank you!
So the most important requirement is that the upgrades do not get applied until the payment is done.
For that we use pending_if_incomplete in payment_behavior option.
The second requirement is that the downgrades don't get applied until the next billing cycle.
Let's say the user have the premium plan and have bought 5 of the additional storage addon, which gives him an additional 5GB of storage.
Those updates (which are upgrades) should be immediately after the payment is validated, for that, we use pending if incomplete, which work flawlessly when no schedule exist.
For the downgrade part, let's say the user updates the additional storage addon from 5 to 2GB, we create a subscription schedule or update it's second phase if it already exists, so that the user can keep the 5GB of storage until the end of his current billing cycle.
The issues starts when we try to upgrade the addon storage (subscription item) while a downgrade for it is already scheduled.
So if we continu with our example, it would be upgrading the addon to 10GB while the 2GB downgrade is scheduled.
What happen is that the user pays the upgrade, and for a brief moment (5 seconds) the change is done correctly, then it gets canceled and the user get 5GB again.
This issue doesn't happen with cards like 4242 ... which doesn't require 3d secure, but it happens with this card for example 4000002760003184, which always requires it.
okay sorry was deep in another thread, let me read!
The issues starts when we try to upgrade the addon storage (subscription item) while a downgrade for it is already scheduled.
yeah thepending_if_incompleteflow is really specific and limited to a small use-case. It can't work with SubscriptionSchedules.
You basically would have to cancel the SubscriptionSchedule, do your upgrade and then re-put the SubscriptionSchedule (assuming they still want to downgrade later)
No worries ๐ Thank you again!
that's what I would do if I were you at least
Hello ๐ I'm the other dev working with Mehdi on this, so what we've found out is that canceling the schedule and re-creating another one it would risk having unwanted results and incrementing the complexity of the upgrade.
What we've found is that you could do normal upgrades to a scheduled subscription (Just like you would do normally to a subscription without schedules) and it works fine, it updates the current phase and leaves the next phases with the old values, so we change them in the webhook we receive from Stripe. This works just fine with the 4242 card, which works without 3DS like Mehdi said, but it doesn't work with cards that need 3DS authentication, because of the fact that it first attaches the invoice to the subscription as latest_invoice - which we receive a webhook for, but ignore - and then once the payment is completed we receive another webhook with the items in the previous_attributes key, this is how we know we changed the items. The issue is that in this case in which we have 3DS, we receive 2 calls having different updates to the items, so it's a bit chaotic, so we were wondering if there's a scenario to treat this, or we would have to deep compare the objects and previous_attributes & metadata to know what's going on?
Thanks!
๐ I'm sorry you did lose me completely with that paragraph. Can you try and explain in details exactly what happens with both exact API request(s) and exact Event ids and what is confusing you in those?
Sure, imagine that we currently have a downgrade scheduled for next month, and the user wants to do an upgrade on the same item, for whatever reason, what we'll do is:
-
Perform a normal request to update subscription item with
proration_behavior=always_invoiceandpayment_behavior=pending_if_incompleteto bill it immediately. -
Once he's billed and the invoice is paid directly, without 3DS (Because the card is already in his account, so we did 3DS when adding it), the changes get applied to the current phase of the subscription schedule (So phase 0 in the array)
-
We receive a
customer.subscription.updatedwebhook saying that the items were changed in the subscription, so what we do is compareprevious_attributesent in the webhook with theobjectsend, which contains the updated data. -
We apply those changes to the future phases (So phase 1 in the array) and save the schedule - This way we avoid the upgrade from changing in the future phase, if we don't do this step it'll downgrade automatically to how it was.
This works fine with cards that do not require aditional actions, however, when we are using cards that need aditional actions, we're in a pickle, because we won't be able to differentiate the items in the webhook calls.
So the main question in this situation is: How can I know that I have the correct customer.subscription.updated webhook call to apply the changes in its body to the future phases? Because I think right now we're not identifying correctly the webhooks and as a result it does weird things like get paid on a upgrade but revert it back as it updates the current phase of the subscription somehow...
If I give you some webhook IDs are you able to read them? just as an example on my testing Stripe account.
๐ hopping in here since koopajah has to head out soon - give me a few minutes to catch up
Ah I think I hear what you're saying - you rely on the customer.subscription.updated webhook to know what change needs to be applied forward to you subscription schedule phases
Yup, exactly
I think there's a change on how subscription schedules work on update between the API version 2019 and 2024, right?
There aren't any major ones that I'm aware of - the bigger changes I know of are from https://docs.stripe.com/upgrades#2019-10-17
Hmm, I think it might be something like that, I'll see if I can find something in there and I'll work more around how I manage the schedules, I have a feel that they are the ones giving me trouble. Thanks for the info!
It may also be that you're not properly considering how schedule phases are split when the underlying subscription is updated. If you add a subscription item mid-cycle, then the schedule's current phase will be immediately split into two so that it reflects the updated state - it's only the future phases that you'd need to be sure to update with the new item
Yup, that's what I'm doing, I'm retrieving the schedule attached to the subscription from Stripe, changing the future phases only (So the phases whose the start date is greater than now), update the items in there, and merge it with the past phases that remain untouched. But somehow the subscription schedule's current phase is not updated when retrieving it from Stripe, it has the data before the update, so it ends up reverting the changes I think when I save the subscription schedule...
I developed the logic with the API version on 2019 and now I'm with 2024, I'm afraid it has something to do because before it worked just fine - it's also true that I didn't test it with 3DS up to now, so idk really
Yeah my guess would be that you're triggering the schedule update too early and you're accidentally using the customer.subscription.updated from before the latest invoice was paid so the current phase of the schedule didn't split yet
Yeah... I'm working through the webhooks for this so I don't have much room here, but thanks for the info ๐ I'll see how I can figure it out via trial & error
๐ feel free to send over an example schedule update request if you want me to take a closer look
Just FYI in case you need it in the future, I disabled all the updates in my webhook and all, Stripe does not update the current phase of subscription schedules when aditional_actions are required from the user to process the payment.
The payment intent goes through and the invoice is paid, but the change is applied until the next billing cycle, meanwhile if I do the exact same change with the exact same code with a card that goes through without aditional_actions, it'll apply the change to the current phase.
It's weird but I think I'll have to apply the change manually to the current phase to split it and apply the change and it should be good for now.
The prices on top with the current cycle and what's in it, and next invoice on the bottom with what's in it. This is without touching the schedule from my end, just processing the upgrade and that's it
And if we look at the bigger picture, we'll have a schedule that goes from 24 to 17 (which was the previous value), and the next invoice will bill 25 addons ๐
It's definitely odd that the pending update being applied doesn't split the current phase of the subscription schedule
Yup, my code was assuming it did, and so did I
Yeah I'll flag this to the team to take a closer look, but in the mean time you'll likely need to build that into your logic ๐ฆ
Yeah already did, it's working fine now ๐
Thanks for your amazing support @rare mica and @tiny mica !
๐