Stripe webhook setup - Step-by-step

I need to show a text message when a user moves from the free plan to a paid plan and a different message when a user moves from the paid to a free plan.

Currently my users see the following text when they are on a paid plan;

You are on a paid plan. your next payment is on Current User's Stripe Customer subscription's Current period ending date

If a user unsubscribes they should see:

Your plan will be cancelled, but is still available until the end of your billing period on Current User's Stripe Subscription's End date

What actually happens is that a newly unsubscribed user still shows as active in Stripe until their last payment date, so they see the following;

  • You are on a paid plan. your next payment is on xx/xx/xx
  • Your plan will be cancelled, but is still available until the end of your billing period on xx/xx/xx

How confusing it that! These sentences contradict one another. Stripe support said “I’m afraid there is no such data to tell/send as soon as a user clicks unsubscribe.”.

They did suggest I use the Stripe Event customer.subscription.updated which looks for a user who changes their current subscription, but this is no good because it doesn’t differentiate between subscribing or unsubscribing. If I use this Event I would have to show the same message when a user moves from the free to a paid plan and vice versa.

I just to hide this sentence after a user unsubscribes but I can’t find any way to do it!

  • You are on a paid plan. your next payment is on xx/xx/xx
3 Likes

I am a lot of closer since finding this video on setting up webhooks but one thing wasn’t explained in the video.

As you can see below, Stripe sent a successful webhook for the Event customer.subscription.updated

2

But I have no clue what to put in the backend API. I guessed maybe I want to show the date but my database isn’t being updated with anything?

1 Like

I’m not entirely sure I follow what the issue is here… what do you mean by this…

I just to hide this sentence after a user unsubscribes but I can’t find any way to do it!

You are on a paid plan. your next payment is on xx/xx/xx

Where are you displaying that sentence to your users?..

Also, what’s confusing or contradicting about:

How confusing it that! These sentences contradict one another.

In any case, if I understand what you’re trying to do, I’m not sure there’s really any need to be using webhooks at all?.. (rather a simple Retrieve a Subscription API call might be the better way to go).

But i guess there are two ways to do it - 1. use a web hook to trigger some changes in your own database, then display data from that on the page.

Or 2, just make the API calls to Stripe as and when you need them and display the relevant data based on the response. (this is probably the simpler and more reliable way, and the way I would do it)

But either way it should be simple enough to display whatever message you want to your users with conditionals.

You should have a period end value in Unix timestamp. This will allow you to track the end date of any subscription automatically with a GET request.

This is available in our Stripe plugin.

I have done this but made no difference. The issue is that I can’t use the data send in the webhook for some reason.

Hi Adam, to clarify what I am trying to do I can actually use Stripe themselves as an example.

If a user subscribes they go to Stripe’s dahsboard and it says “Your next payment will be on xx/xx/xxxx”.

However, if they unsubscribe it removes that sentence and instead says “Your plan will be canceled on xx/xx/xxxx”

I cannot hide the first sentence in my app because canceled subscriptions end at the end of the month (not immediately), so I have both sentences; “Your next payment is on 01/01/2022 AND your last payment is on 01/01/2022” - these are contradicting.

Stripe says there’s no webhook available to resolve this, I don’t believe them because 99% of people would need this.

  1. use a web hook to trigger some changes in your own database, then display data from that on the page.

This is what I have tried to do, but I can’t get it to work.

Or 2, just make the API calls to Stripe as and when you need them and display the relevant data based on the response. (this is probably the simpler and more reliable way, and the way I would do it)

I considered this after seeing a tutorial yesterday. If I can’t get a freelancer to set up webhooks I may have to start to learn about API calls. I have to decide carefully because, as an example, just learning webhooks has been about 100 hours with no results which gets very disheartening.

I cannot hide the first sentence in my app because cancelled subscriptions end at the end of the month (not immediately), so I have both sentences; “Your next payment is on 01/01/2022 AND your last payment is on 01/01/2022” - these are contradicting.

Are you talking about in your own app (as opposed to the Stripe Customer Portal)?..

In which case I really don’t understand… if it’s your app then you can display or hide anything you like?

(just use a conditional to display the correct sentence - whether you’re using a simple API call on the page, or using a webhook to make changes to your own data on the back-end, you can look for the ‘cancel_at_period_end’ attribute on the Subscription object… if its value is ‘yes’ then you know the subscription has been cancelled, so display the appropriate sentence)…

Stripe says there’s no webhook available to resolve this

It’s not entirely clear to me why you’d need/want a webhook for this?.. You’re just displaying something on your page, based on conditionals (whether from your own database or from Stripe’s API)…

just learning webhooks has been about 100 hours with no results which gets very disheartening.

I’m not sure you’re using webhooks in the correct way here…

If you want or need to store in your own database the date when the current subscription period ends, then you’ll want to do that when the subscription is created and renewed (i.e. using the customer.subscription.created and invoice.payment_succeeded events as webhooks).

The customer.subscription.updated event is for getting updates to a User’s subscription, and returns a Subscription object- so you can access any data from the Subscription object through the request data from the webhook.

So, if you’re trying to change something in your own database when a users cancels their subscription, look for the relevant attributes on the Subscription object, and use conditionals on your API workflow to make the changes in your own database based on the data of the Subscription in the request data.

I have done this but made no difference. The issue is that I can’t use the data send in the webhook for some reason.

Why can’t you use it? What do you mean by that?.. (by the way, in your screenshot you’re just using the request data’s created field, which is just the timestamp of when the event was created, not anything to do with the subscription period end dates…)

1 Like

If that is correct then this solves the issue, but after several emails with Stripe they said that, because a cancelled subscriber remains active until the end of their payment period, there are not solutions. You may found one though!

Currently I have a larger issue in that Stripe webhooks are not updating my Bubble site when a user unsubscribes - users are ‘active’ all the time, even when unsubscribed. This is what I hope the freelancer can fix, then I can try your suggestion above.

It’s not entirely clear to me why you’d need/want a webhook for this?.. You’re just displaying something on your page, based on conditionals (whether from your own database or from Stripe’s API)…

I tried many conditionals but nothing worked due to the fact users remain ‘active’ until their payment period ends. I need to update the user (in Bubble) the second they click unsubscribe. You may be correct that there is a way to do this without a webhook but I can’t find it.

Why can’t you use it? What do you mean by that?.. (by the way, in your screenshot you’re just using the request data’s created field, which is just the timestamp of when the event was created, not anything to do with the subscription period end dates…)

It’s best I don’t answer this because I may confuse the thread. I have tried dozens of set-ups, I can’t recall what I have tried as I’ve done so many variations. For now I am just keeping fingers crossed that the freelancer sets up one webhook, then I can see how it works.

No, you definitely need a webhook for that, and the customer.subscription.updated event is precisely the one you need…

The Subscription Status will remain Active until the subscription has actually ended (i.e. the period that’s been paid for has come to an end) - only then will the Status change to Cancelled…

So it’s no good looking at the Subscription Status to determine whether or not it’s been cancelled…

As I said previously, to know whether a Subscription has been cancelled by the User you just need to look for the cancel_at_period_end attribute being ‘true’ (that’s what it’s for, as explained in the Stripe Docs) Stripe API Reference - The subscription object

So the best way to do it (this is how we do it in our Subscription apps)… is to set up a Webhook in Stripe with the customer.subscription.updated event and point it to your endpoint (API Workflow) in Bubble.

Then, in that API workflow, use a conditional action to trigger another workflow, based on the request data’s object (specifically: when request data's object cancel_at_period_end is 'yes' AND request data's object status is 'active' (you’ll probably also want to compare it to something in your own database to prevent the same workflow from running again if the user makes some additional change to their subscription, such as comparing your own database’s subscription status, or cancel fields)

Then use the second workflow to make the necessary changes to your User in your database.

2 Likes

No, you definitely need a webhook for that, and the customer.subscription.updated event is precisely the one you need…

Ok, that’s good to know.

The Subscription Status will remain Active until the subscription has actually ended (i.e. the period that’s been paid for has come to an end) - only then will the Status change to Cancelled…So it’s no good looking at the Subscription Status to determine whether or not it’s been cancelled…

However, as a test I set the subscription in Stripe to end immediately but still the ‘active’ status remained. The freelancer I just contracted has just said that the Bubble setup looks too complicated so he’s not able to help. So, as a further test I am going to follow the video https://www.youtube.com/watch?v=GE2fYHFWahw and remove subscriptions to see if a simple one time purchase will update the status.

As I said previously, to know whether a Subscription has been cancelled by the User you just need to look for the cancel_at_period_end attribute being ‘true’ (that’s what it’s for, as explained in the Stripe Docs) Stripe API Reference - The subscription object

Thanks Adam, I will try this.

So the best way to do it (this is how we do it in our Subscription apps)… is to set up a Webhook in Stripe with the customer.subscription.updated event and point it to your endpoint (API Workflow) in Bubble. Then, in that API workflow, use a conditional action to trigger another workflow, based on the request data’s object (specifically: when request data’s object cancel_at_period_end is ‘yes’ AND request data’s object status is ‘active’ (you’ll probably also want to compare it to something in your own database to prevent the same workflow from running again if the user makes some additional change to their subscription, such as comparing your own database’s subscription status, or cancel fields). Then use the second workflow to make the necessary changes to your User in your database.

I appreciate this, I will try to follow these instructions. Thank you.

1 Like

You can leverage the webhook and setup the Stripe webhook into the Bubble-

Here is how you can set up the webhook- https://youtu.be/uxHdMa21qlo

You need to track the customer.subscription.updated event.

https://stripe.com/docs/api/events/types#event_types-customer.subscription.updated

Let me know if you still face any issues?

Thanks @ankur1, I watched the video and learned that I can send multiple webhook Events in one go, instead of creating separate ones. I wonder why anyone would opt to send individual Events, seems more efficient to send all?

@adamhholmes I have created a fresh product which is a One-off Purchases. I thought this may be easier to test webhooks on a One-off Purchase because subscriptions have the issue that they remain Active until the subscription has actually ended.

Set up test
I deleted my existing webhooks in Stripe and Bubble and created a New API workflow in Bubble’s backend called “webhookOneTimePurchase”.

I want to test when a customer successfully purchases and then requests a refund so I am listening for:
charge.succeeded
charge.refunded

Note, there is a very useful webpage called The Stripe Webhook Event Cheatsheet to help find the webhooks you need.

Checkout button
I added a button to my website and in workflow I ‘Charge the current user’.

Test payment
New customer checked out and Stripe charged the User, but the webhook delivery failed.

Bubble has the following error (which is probably why the webhook delivery failed).

2

I need to reinitialize but, as before, there is no documentation or info online letting me know how to do this. If you Google “how to reinitialize webhook in stripe” you’ll find no results. Surely every Bubbler has to reinitialize their webhooks so I wonder why no one has spoken about this?

@darren.james7518
I think best is you initialise the webhook when you charge the user and remove the “/initlize” keywords from the stripe webhook url.

That way you will have the response that stripe is sending out.

I definitely wouldn’t recommend using a single webhook for both of those events (if that’s what you’re doing)… you’ll be triggering the same workflow whether a customer makes a purchase or gets a refund, which doesn’t make a lot of sense (sure, you could use conditionals on your backend workflows to figure out which event has been triggered, but why make it harder for yourself, when you can just use two separate webhooks, one for each event?..)

seems more efficient to send all?

Maybe, but it’s way more complicated to figure out what’s going on when you receive the webhook - you’ll need to use loads of conditionals in your workflows - which is completely unnecessary, and a lot of extra work and confusion. Just use a single webhook for the event (or maybe a few related events) that you need.

I need to reinitialize but, as before, there is no documentation or info online letting me know how to do this. If you Google “how to reinitialize webhook in stripe” you’ll find no results. Surely every Bubbler has to reinitialize their webhooks so I wonder why no one has spoken about this?

Re-initializing is just the same as initializing… so however you initialized the workflow in the first place (assuming you have initialized it) then just do the same thing again… (by the way, I don’t think I’ve ever had to re-initialize a webhook, once it’s done it’s done, unless you change something, so I’m not sure every Bubbler will have had to re-initialize their webhooks)…

Here’s the link to the Bubble manual about initializing API workflows (i.e. Detect Data): Workflow API - Bubble Docs

Thanks for the advice @adamhholmes. My thinking was that triggering the same workflow (whether a customer makes a purchase or gets a refund) wouldn’t require more work for myself.

For example, if I listen for when a user is subscribed, I would have a single condition saying; When user Stripe Status is "active" then say “You are subscribed”.

This single condition would be the same regardless of how many webhooks I listen for? Just because I am receiving a huge payload of Events, I only need to specify the ones I want to show in my Bubble app? Please correct me if I am wrong.

Re-initializing is just the same as initializing… so however you initialized the workflow in the first place (assuming you have initialized it) then just do the same thing again

This used to be very easy before Stripe changed the way webhooks are initialized. I haven’t initialized the webhook at all, I don’t know how to.

I think you may have created your webhooks prior to the changes at Stripe. I will watch more tutorials and see if anyone has a solution. I do recall one tutorial saying it won’t auto initialize but I can’t remember how he did it … I will update here soon.

Thank you but this did not work. Somethign which I can not change is that once I remove the “/initlize” from my webhook in Stripe, I then return to my Bubble app and in the backend workflow click Detect data, but the link it detect always has “/initlize” at the end.

Of course it would… unless you literally want to run the same workflow when a customer makes a purchase or is given a refund (I can’t think of any logical reason for doing that, but maybe you have an example?..)

Otherwise you’ll need to use conditionals to figure out what’s going on on the backend based on the request data (which even with 2 or 3 events can get complicated)

For example, if I listen for when a user is subscribed, I would have a single condition saying; When user Stripe Status is "active" then say “You are subscribed”.

But if the Customer has an active subscription, then their Subscription status will ALWAYS say active, no matter what event is triggering the webhook… they might just be updating their billing address, or might have passed a threshold date before their card is due to expire, or maybe they’ve made another purchase… but their subscription status will still say Active, so if that’s all you’re doing you’re going to have a complete mess of workflows being run at the wrong times, or multiple times, etc. anytime an active subscriber triggers any event in Stripe.

This used to be very easy before Stripe changed the way webhooks are initialized. I haven’t initialized the webhook at all, I don’t know how to.

Yes, before Stripe took away their ‘Send Test Event’ button all you had to do was set up your API worklfow in Bubble to be ready to initialize, then click the send test event button.

Now that’s not an options so you need to trigger the event some other way… as I’ve said before, you can use the Stripe CLI (I haven’t used that myself, so can’t offer any help with that)…

But the simpler way (what I do, and what is shown in the Video that @ankur1 shared) is to simply trigger the event from your Bubble app…

So you can just create a temporary page in your app to do that (or use the actual page from where the event will be triggered)… or if you’re specifically using Subscriptions you can use the Stripe test clock feature to skip to future times to test subscription renewals and things like that.

Once it’s initialized be sure to remove the /initialize from the endpoint URL in the Stripe webhook.

Ah yes, thank you this sentence made it click for me! Of course, I need to listen for individual webhooks to avoid complex conditionals within Bubble. Thank you for explaining that.

Now that’s not an options so you need to trigger the event some other way… as I’ve said before, you can use the Stripe CLI (I haven’t used that myself, so can’t offer any help with that)…

I did try but it’s beyond me and I want to avoid anything with actual traditional code/terminals because I get so stressed. Not sure why.

Once it’s initialized be sure to remove the /initialize from the endpoint URL in the Stripe webhook.

I have created a new user and checkout which should be all that is needed to initialize it? I have also removed /initialize but still the webhook is not being sent.

So just trigger the event from within your app…

I have created a new user and checkout which should be all that is needed to initialize it? I have also removed /initialize but still the webhook is not being sent.

Yes, exactly… but you NEED to include the /initialize in the URL in order to initialize it. Only remove it after it’s been successfully initialized.

To be clear, it is my understanding that if I preview my app and checkout as a new customer that is how we ‘trigger the event’. This is the only way I know to trigger an event.

Yes, exactly… but you NEED to include the /initialize in the URL in order to initialize it. Only remove it after it’s been successfully initialized.

Yes, I did that. I am going to delete the webhooks and start again as a new test.