Yeah, as I said there is no current user in a workflow triggered via a webhook, so that won’t work.
So to be clear, I have everything correct apart from the Customer ID? If that is so, then I understand I can’t refer to the User directly, I can’t do a Search for and I can’t use the Request data … I don’t see how I can tell Bubble that the Thing to change relates to the User.
The options available are;
I don’t see how I can tell Bubble that the Thing to change relates to the User.
Well, firstly, it doesn’t relate to the User (you’re changing a Subscription, no? not a User)… so there’s no need to refer to the User at all here, just refer to the Subscription directly (based on the webhook data)…
But if you want or need to refer to the User as well then there are several ways to do it.
The simplest way is just to to refer to the Subscription’s User
Another way is to use the request data’s Customer (which is the Stripe Customer ID) to find the User.
Alternatively, you could make a Retrieve A Customer API call (with the Customer ID from the request data) to get the customer’s email address and search for that.
There are probably other ways too…
By the way, I’m talking here about in your API Endpoint workflow… in subsequent workflows you just need to pass the relevant data into the Workflow as a parameter.
Thank you for the additional information.
But if you want or need to refer to the User as well then there are several ways to do it.
No, I just need Bubble to know that the updated subscription relats to the current user.
refer to the Subscription directly (based on the webhook data)
Do you mean like this?
There’s no such thing as the current User in a workflow triggered by a webhook…
I assume what you mean is you want Bubble to know that the subscription relates to a particular User (but you must define that user somehow).
In any case, it’s the Subscription you’re working on here, so assuming your database is set up to store the User on the Subscription then that’s all you need to define the User (if you even need to do that at all)
Do you mean like this?
Yes, but obviously you don’t just want to change the first subscription in your database (unless that is what you want to do)…
Presumably want to change the subscription in your database that relates to the Subscription in the Stripe webhook?
So add a constraint to match the Subscription Id in your database (which you set when creating the subscription) to the subscription ID of the webhook object (i.e. the request data’s object ID)
Yes that’s correct.
Presumably want to change the subscription in your database that relates to the Subscription in the Stripe webhook?
Yes
So add a constraint to match the Subscription Id in your database (which you set when creating the subscription) to the subscription ID of the webhook object (i.e. the request data’s object ID)
The request data’s object ID
is not available.
I’m guessing you’re in the wrong workflow then, you can only access the request data in the API Endpoint workflow which is what you need to be in.
Then pass the data on to subsequent workflows in the workflow parameters.
I don’t think I am in the wrong workflow. The backend workflow first receives the customer.subscription.updated payload. This happens when a user click unsubscribe in Stripe.
Then, When the user is still active AND is set to cancel at the end of their billing cycle, I go to the next API workflow to update the my database’s field “Plan.Cancelled = Yes” - I should be able to then use that data to display Text saying "Your plan has been cancelled and will expires on [date].
The workflow in your second screenshot is not configured as an endpoint, so you won’t be able access any request data (there isn’t any)…
The workflow in the first screenshot is your endpoint workflow, so that’s where you need to be working to reduce the Subscription (not the second workflow)…
You’re triggering the second workflow from the first one, so you need to pass the relevant data from there into the second workflow via the parameters…
Thanks, now that I have added the ‘Make changes to Subscription…’ to the endpoint workflow (#1) , the second API (#2) now seems redundant because I am already Making changes to Subscription… in first endpoint workflow?
Sorry, ignore everything Adam.
I created a webhook as a test for invoice.created
and to simplify things I added a field to the “User” Datatype called “invoice-paid”. Despite this very simple setup I still can’t get data to save to the DB so I clearly have no idea what I am doing.
That looks like it should work…
the second API (#2) now seems redundant because I am already Making changes to Subscription… in first endpoint workflow?
Indeed, in your current set up you don’t need the second workflow, although you’ll need to set the correct conditionals on the actions within the workflow…
The issue you’ll have at the moment is that you’re not doing anything to differentiate what the update to the subscription is (as you’re running the workflow action directly inside the API Endpoint Workflow, with no conditionals on the actions)…
Meaning that ANY update to the user’s subscription will trigger that workflow, and the ‘make Changes To A Subscription Action’.
So, for example, if a customer adds a new payment method, that will update their subscription, the Stripe event will send the webhook, triggering your workflow which will change the Plan.Cancelled field to ‘yes’ - even though they haven’t cancelled their plan, they’ve just updated their payment details.
So you really need to put some careful conditionals in there somewhere…
If you’re workflows are very simple (which they appear to be), then there’s no reason you can’t just keep all the actions inside the same workflow, and apply the correct conditionals on each action.
If they are more complex, then it’s definitely better to break them up, and just use the API endpoint workflow to trigger the subsequent workflows based on the request data and the conditionals (which is what I was suggesting initially).
But as all you’re doing is changing a single data field, I would keep it simple and just keep all the actions inside the Endpoint workflow.
So add the appropriate conditional to the action.
I created a webhook as a test for
invoice.created
and to simplify things I added a field to the “User” Datatype called “invoice-paid”. Despite this very simple setup I still can’t get data to save to the DB so I clearly have no idea what I am doing.
I took a look at your server logs, and it looks like no User was being defined… hence nothing was being updated.
Assuming you had Users in your database with Stripe Customer ID’s, then it looks like it could be a privacy rules issue.
Looking at your current privacy rules, you have a rule in place that prohibits the Customer ID from being seen by anyone other than the User who’s customer ID it is, nor being found in searches… so it won’t work in a back-end workflow…
Although, saying that, you have checked the ‘Ignore Privacy Rules’ on the workflow itself, so I’m not sure if that is the issue…
But it might be worth updating your privacy rules and trying again to see if it’s that…
Or, just make sure the User exists in the database first AND they definitely have a Customer ID
Thank you for your detailed response and for letting me know I can keep all the actions inside the same workflow, and apply the correct conditionals on each action (as my workflow is not too complicated).
I still haven’t ever been able to populate any field in my database with any data when I create a new webhook. So, to keep this troubleshooting simple I will focus on the new invoice.created
webhook which should be populating the User’s “invoice-paid” field as soon as a user subscribes to a plan.
I have deleted all users and signed up fresh, once the new user checks out I can see that a User is created with a Stripe Customer ID, but still the field “invoice-paid” remains empty.
Regards to privacy, I have updated the User privacy to view everything. I actually can’t think of a scenario where the Current User would not want to be able to view everything related to them?!
Everyone else has privacy privileges switched on for fields related to the webhook data:
To be thorough I tried both Request Data 's created
and 's object created
.
On that subject, am I correct in thinking we can pull any data from the Stripe > webhook ‘Request data’? Below I am choosing to show “created” but I could show “account_country” using this same webhook right?
Ah, wait … whilst I was logged into Stripe I can see the previous two attempts failed, an error 404 has something to do with the initialize - odd because yesterday they were not failing.
I have now completed remove all my backend workflows and Stripe webhooks and create a new (ultra simple) webhook so it’s easier to find what’s going wrong.
User Datatype has these fields;
Stripe.CustomerID
Stripe.Created
I have set up and initialized a Stripe webhook for invoice.created
which occurs whenever a new invoice is created.
Backend API is very simple:
But, as always the field I am trying to populate remains empty?
I see that no webhook attempts have been made from Stripe:
But that directly contradicts the fact Stripe shows a Succeeded webhook for the test subscription I just did?
I had an encouraging response from Stripe who said:
You are correct that when canceling a subscription at the end of the billing period, only a
customer.subscription.updated
event will fire up ascustomer.subscription.deleted
will only occur once the subscription ended. So I agree that it will be more logical to name it ascustomer.subscription.ended
or add this as another webhook event.Therefore, I will go ahead and file a feature request for you. Hopefully our engineers will be able to implement it soon.
Having looked at your logs again, I think I’ve found the issue…
It looks to be a timing issue between the webhook running and the rest of the client side actions you’re running (which is kind of obvious now I think about it…).
To put it simply, when your ‘pay now’ button is clicked the following things are happening (in this chronological order, according to your server logs):
- You’re subscribing the User to a plan in Stripe via the plugin (on-page workflow)
- As soon as the User is subscribed to the plan the webhook is triggering the backend workflow
- The back end workflow is trying to find the User based on their Customer ID (which is still empty) so it fails to find a User
- You then create a Subscription in your database (on-page workflow)
- You then set the User’s Stripe Customer ID (on-page workflow)
So, as you can see, the issue is the time it takes for the on-page workflow to run (compared to the triggering of the backend workflow via the webhook).
In other words, using the Stripe plugin to set the Customer ID on the page (although it’s fine in itself) is too slow if you’re intending to use the Customer ID in the webhook-triggered workflow.
In the specific case of the process for you User ‘user4@test.com’, they were subscribed to the plan at 09:52:14, the webhook workflow ran at 09:52:32, then you created the subscription (in the on-page workflow) at 09:52:41, then set the User’s Stripe ID (via the plugin, in an -on-page workflow) at 09:52:41, a full 9 seconds after the webhook workflow on the backend had already been run (and obviously therefore was unable to find the User)
So to solve the issue you’ll need another solution.
As you’re using a plugin I don’t think you can pass custom metadata into the Stripe calls (which makes things a bit more complicated - even to the extent you might want to consider making your own API call instead)…
So basically, you can’t rely on setting the customer ID on the page if you’re using the customer ID to identify the User in the webhook on the back end (it will be too slow).
If you could send some custom metadata into the Stripe call (as you can if making your own calls) it would be simple - just create the Subscription in your database first, then make the call to subscribe the User to the plan (including the unique ID of the Subscription), then in the backend workflow retrieve the Subscription Unique ID (from the metadata), search for the Subscription based on that (then identify the User if you need to from that).
As it is (using the plugin), the only reliable data you will have to identify the User will be the email address…
So you’ll have to make an API call to retrieve the Customer (based on the customer ID in the request data), then get the Customer’s email address from that and search your database for the User by email address.
Then you can search for the User’s Subscription
That’s the only way I can think to do it using the plugin.
If making your own call it would be a lot simpler, as you can pass the unique ID of the Subscription into the call and retrieve it directly on the backend.
Although in your specific use case, there may be no need to actually use a webhook at all - I’m still not entirely clear what you’re trying to do here - you might be able to keep it all on the page OR user a backend database trigger instead to trigger any workflows you need to run.
Hi @adamhholmes
I’m so impressed with how you have found the source of the issue, and so grateful that you took your time to do this for me.
Although in your specific use case, there may be no need to actually use a webhook at all - I’m still not entirely clear what you’re trying to do here - you might be able to keep it all on the page OR user a backend database trigger instead to trigger any workflows you need to run.
I really don’t need much complexity, I plan to sell one product (subscription) for £8.99/month and I just need to be able to show (one) of the following alerts to my users;
- You are on the Free plan
- You have subscribed to the Professional plan and your next payment of £8.99 will be on [date]
- Your payment has expired, please update your credit card
- Your payment failed, please check your credit card
- You have just unsubscribed, sorry to see you leave. You can continue using the Professional plan features until the end of your billing period on [date]
So, as you can see, the issue is the time it takes for the on-page workflow to run (compared to the triggering of the backend workflow via the webhook).
I would never have caught this so thank you for digging in the logs. You’ve said that I’ll have to make an API call to retrieve data. Since I am not familiar with making API calls I need to essentially start over, so I guess I have an opportunity to rethink how I accept payments.
You mentioned that you don’t personally use the Stripe plugin, do you think it’s easier to not use it, and seeing as my use case is simple, what would you suggest is the best method for me? I am happy to go with any system which is simplest.
I’m fairly sure the Bubble Stripe plugin has a Retrieve a Customer Data Call built in, so you can just use that if you don’t want to use the API connector…
You mentioned that you don’t personally use the Stripe plugin, do you think it’s easier to not use it
In the case of Stripe (and many other things, especially regarding APIs), yes I do (personally) think it’s easier, and better, to use the API connector yourself - for a few reasons…
For 1 thing there are no limitations, or restrictions, on what you can do (within the confines of the API), and so you don’t have to try to come up with workarounds for limitation that a plugin may have.
Plugins are great, if they happen to do exactly what you need to do, in the way you need to do it. If that’s the case then great.
But when it comes to Stripe plugins some of them have limited features which, if they don’t include exactly what you need, means you either have to change what you’re trying to do - often making compromise in what your app can do - or find workarounds.
And some of them include virtually the entire Stripe API in a single plugin (90% of which you’ll probably never need) - and you still have to be familiar with the Stripe API docs to use them anyway - so why not just use the API connector.
That way you can keep things nice and simple - only use the parts of the API you need to use, without restriction, and don’t have to compromise on functions that can’t be done with a particular plugin.
I really don’t need much complexity, I plan to sell one product (subscription) for £8.99/month and I just need to be able to show (one) of the following alerts to my users;
- You are on the Free plan
- You have subscribed to the Professional plan and your next payment of £8.99 will be on [date]
- Your payment has expired, please update your credit card
- Your payment failed, please check your credit card
- You have just unsubscribed, sorry to see you leave. You can continue using the Professional plan features until the end of your billing period on [date]
If you plan/need to store that info in your own database, then for sure you’ll need to use webhooks (at least for marking failed payments, and unsubscribes) to save those things as they occur
But you could just use an API call on the page for all of that instead, and just get the data from Stripe when the page is loaded, then use conditionals to show the relevant message to your users.
Personally I would (and do) prefer to get that sort of data directly from Stripe (via API calls), as I inherently trust that Stripe will have the correct, up-to-date data about a user’s subscription more than my own database (I might have made an error in a backend workflow somewhere, a webhook may have failed for some reason, or there could be any other reason why my own database may be less that 100% accurate)… so for important customer info regarding a User’s Stripe data, I’d prefer to get it directly from Stripe when I need it (i.e. to show it to a User) but that’s just me…
But it’s really up to you (and you can use a combination of your own data and Stripes data to do any of those things).
That was really interesting to get your perspective and I agree, I can use an API call on the page for all the data I need to display.
The other benefit to using an API call is that I need to get familiar with the process so I can achieve my longer term goal of connecting to building suppliers APIs (such as Wickes, B&Q) so that my users (tradespeople) can search for local suppliers and view their stock.
I will start doing some research and try to get familiar with APIs. Thanks for your help!
I can’t find how to add a correct ‘GET’ URL to the API connector?
On the stripe API docs I read:
Clients can make requests as connected accounts using the special header
Stripe-Account
which should contain a Stripe account ID (usually starting with the prefixacct_
).
I don’t know what a connected account is. Also, it says it should contain a Stripe account ID (usually starting with the prefix acct_
), which I have done, but I don’t see any help telling me what else to put in the URL?
The base URL for Stripe’s API is https://api.stripe.com
followed by the API version you’re using, then the endpoint path and any other relevant data (such as IDs or connected accounts etc.)
Here are the docs for Subscriptions:
Stripe API Reference - Subscriptions
So to retrieve a subscription the endpoint URL would be: https://api.stripe.com/v1/subscriptions/sub_*****************
I don’t know what a connected account is
If you don’t know what a connected account is then I’m guessing you’re not using Stripe Connect (which is for handling multi-party payments, so I don’t know why you would be?..), so you can ignore anything to do with connected accounts.
The base URL for Stripe’s API is
https://api.stripe.com
followed by the API version you’re using, then the endpoint path and any other relevant data (such as IDs or connected accounts etc.)
Here are the docs for Subscriptions: Stripe API Reference - Subscriptions
Thank you for clearing that up. I am grateful you did because I don’t see any mention of the base URL on the Stripe API Reference page - I would have been stuck
So to retrieve a subscription the endpoint URL would be:
https://api.stripe.com/v1/subscriptions/sub_*****************
Perfect. I have now made my ‘GET’ URL
https://api.stripe.com/v1/subscriptions/sub_`[my Stripe-Account ID]`
When I initialize the call it produces an error saying I did not provide an API key. “You need to provide your API key in the Authorization header, using Bearer”.
A quick look at Stripe’s Authentication docs says to use -H "Authorization: Bearer sk_test_iYu43geAW35EI7W4iU2XbaCD00Btkrb4MS"
instead of -u
Ok, so I have edited my header to be -H Authorization: Bearer pk_test_McbOvciswdiXgtElJbLxqJGL00NGxxxxxx
but I get an error saying this header name is not correct"?