In this chapter, we're going to add another trigger to our project. Just like we've added the Google Form trigger, we're now going to add the Stripe trigger. Basically, when certain Stripe events reach our application's webhook, we're going to initiate the workflow based on that workflow id. But before we do that, let's go ahead and resolve one TypeScript error that I keep postponing. Let's go ahead inside of our executor registry and in here we have a problem with our HTTP request.
So what I'm going to do is I'm going to go inside of the HTTP request executor here which you can find inside of source features executions components HTTP request folder and then find the executor. Let's go ahead and bring back the optional question marks here. This will then resolve the problem right here so we can remove the to do. But now we have a problem inside. One easy way of fixing this is instead of doing validation checks outside of step.run, let's simply do it inside.
So the reason this needs to work is TypeScript flow control works in a very specific way. Even though we just validated that data method exists, that variable name exists, and that endpoint exists, There is nothing guaranteeing us that once this step.run happens, data itself won't change. That's why in here it still thinks it can be undefined. Same with the endpoint. It still thinks it can be undefined.
So one easy way of fixing this is just by moving all of these if checks inside of step.run. So let's go ahead and add them here. And you can see that the moment we add them all of the errors go away. So it's actually that simple to resolve this. And now when you hover over data endpoint it's a string.
Method it's I think the error is no longer here so I think it's just showing us options that method okay yeah I see but the method itself yes it definitely exists perfect And same thing for the variable name, which was the problematic one. Basically it's no longer causing us any errors. I think that if you did variable name here, data, variable name and hovered over this, there we go, it tells you it's a string. So just by moving the if checks inside of the scope of this function with fixed type script flow control and now it knows that these must exist because they can no longer be mutated after these if checks. Perfect.
So let's go ahead and close that. Now that we have this ready, let's go ahead and let's implement the stripe trigger. So this will be quite simple as we've recently just implemented the Google Form trigger. Let's go ahead and copy most of the files. I'm going to go inside of Features, Triggers, I'm going to copy Google Form trigger and I'm going to paste it in the components.
I'm going to rename this to Stripe Trigger. Instead of Stripe Trigger, let's go ahead and modify all of these files. I think that we can already delete the utils because there will be no Google Forms script. So let's remove the entire utils file. Then let's go inside of node of the new Stripe trigger.
And let's go ahead and rename this to Stripe trigger. Is it called a node in the end? I just want to be consistent. Yes, it has the keyword node at the end. So let's call it stripe trigger node.
Perfect. Let's go ahead and change this to stripe. And let's do when stripe event is captured, or anything like that. Later, if you want to be more specific, for example, you could add a description which will be based on the trigger dialog where users could choose whether they want to listen to invoice, customer, purchase, failed, right, any specific events. So in this chapter, I'm just going to show you the kind of overall idea of how you would do this and then you can specialize it into one specific case or allow the user to select what case they want.
That's why I'm doing such a broad description here. And now we have to change this. So instead of using Google form.svg go ahead inside of my node-based assets folder and in the images here you should find stripe.svg. Once you have stripe.svg go inside of public logos and add it here. So just go ahead and add it here and you should have stripe.svg inside.
Perfect. Oops, I added two of them. Now let's go ahead back inside of our node.txt for the stripe trigger. Make sure you are in the correct one, and let's change this to stripe.svg. And I can leave this as is.
Now let's go ahead and add it to our node components. I think this is inside of source config folder node dash components. And then we have to just copy and paste this and change this to be stripe trigger. Obviously we have an error here because we haven't added the stripe type but let's just prepare this with stripe trigger node. Now let's go and set up our schema.prisma and let's add stripe trigger.
Once we add that let's go ahead and do npx prisma migrate dev. And let's go ahead and give it a name of stripe trigger node. So very simply stripe trigger node. And after you do that, it should synchronize the database. As always, I recommend restarting your Next server, your InGIS server, and even ngrok wouldn't hurt.
Let me just go ahead and quit the entire thing and do npm run dev all. Looks like ngrok was running twice, So there we go. Now it's fixed. Great. Now that I have the stripe trigger here, I should no longer have the error inside of here.
If you do just restart your VS code or your TypeScript entirely. Now we have to go inside of components, inside of node selector. And we have to copy Google Form trigger, change this to a Stripe trigger, and use the Stripe SVG. Let's go ahead and do Stripe event here. And let's go ahead and change the...
Runs the flow when a Stripe event is captured. Or anything that you feel is sufficient to explain how this workflow will be triggered. Let's go ahead and refresh our app to make sure everything is working. Every time you restart Next.js, you should do this. And it looks like when I deleted that utils folder inside of my stripe it messed up the components in that inside of stripe trigger right here dialogue.tsx it uses generate Google form scripts so just remove that And let's go ahead and remove the entire on click here like this.
This way we shouldn't have any errors. Perfect. And now when we click here, we should have a stripe event here. Runs the flow when a stripe event is captured. There we go.
When stripe event is captured. Perfect. So we can now visually add Stripe trigger to our app. Now let's go ahead and create the proper dialog for it. So the dialog itself will actually be quite similar to the one in the Google forum trigger.
Let's go ahead and make sure we rename it first. Instead of Google Form Trigger, this will be Stripe Trigger Dialog. And we're still going to use the params, we still need the workflow ID and we still need to generate the webhook URL. But instead of going to webhooks.googleform, we're going to go to webhooks.stripe. Copy the clipboard function can stay exactly as it is.
And now let's just change the title and the description to describe exactly what we are doing in regards to the stripe event. So Stripe trigger configuration. And let's go ahead and change the description to something useful. Configure this webhook URL in your Stripe dashboard to trigger this workflow on payment events. Obviously, again, a broad description.
Later, you can specify this to be something specific to make it a bit more useful to your viewers. I mean to your users. These fields will be exactly the same. There's nothing we have to change here. And for the setup instructions, we should just you know change what we have to do.
So I'm just going to go ahead and show you the first step will be open your Stripe dashboard. After that, we're going to go ahead and go to developers webhooks, then we're going to add, click add endpoint, We're then going to paste the endpoint which users will see in the input above. Users will then have to specify which events they want to listen for. For example, payment intent succeeded. And let's go ahead and tell them to copy and save the sign-in secret.
And then in here we have the Google Apps script which we can completely remove because no such thing exists for the Stripe event. And for the available variables here, you can be as creative as you want. For example, I'm just going to go ahead and add a few here. So instead of this unordered list, I'm just going to remove the entire content inside so it's empty. For example, one thing you can do is the payment amount.
Using the list element, the code element, class name BG, background PX1, PY 0.5 rounded, and just render stripe.amount, because stripe will be the name of the variable where we're going to store the initial context off. And that will be the payment amount. And then you can just go ahead and add a bunch of these for anything useful. For example, currency, or maybe customer ID. Or you could just show the users how they can access the entire Stripe object by using our JSON helper.
So again, this is just UI helpers. This is, It doesn't matter if you make a typo here. It's just to make it easier for your users. Another useful one might be the stripe event. Stripe event type.
So they can see exactly what happened. Great. So once we have this, let me see, can I remove anything? I think everything here is ready to go. We can now go inside of the node.tsx and we can change this to the Stripe trigger dialog.
Let's go ahead and use it right here. There we go. And immediately now you can see Stripe trigger configuration. In here it uses webhooks, Stripe, perfect. I can click copy here, set up instructions, include the Stripe dashboard and available variables are listed here.
Perfect. So what we have to do now is obviously update the node status so it uses the Stripe channel and not the Google Form channel. So that shouldn't be too hard. Let's go ahead and go inside of ingest channels, copy Google form, paste it here, rename this to Stripe trigger, go inside of your newly created Stripe trigger, go ahead and change this to replace all instances of Google Form with Stripe. So Stripe trigger channel name, again, Stripe trigger execution, and this will be Stripe trigger channel.
Everything else will stay exactly the same. Once we have Stripe trigger, let's go ahead inside of ingest functions.ds. Let's make sure to register the new Stripe trigger channel. Just make sure you have imported this. Great, once we have that, we can go ahead inside of, well, back where we created this, inside of features, triggers, stripe trigger, actions.ts.
And let's just modify it, right? So immediately we can change this import to be from stripe trigger, stripe trigger channel, type of stripe trigger channel, Use Stripe channel here. Let's go ahead and rename all instances of Google Form to be Stripe. And I think that should be enough. Perfect.
Now obviously we have an error here because we have to go back inside of the node of the Stripe trigger and we have to import Stripe fetch, Stripe trigger real-time token. There we go. And I think, oh, the only thing we have to change here is the import. So Stripe Trigger, Stripe Trigger Channel Name. I think that might be it.
And what I always like to do is I like to right click on the folder, Stripe Trigger, and I would like to click Find in Folder and search for Google Form. Yeah. And now you will see everything that we have left over to fix. So it's the executor one we haven't fixed. Perfect.
So immediately change this from Google Form to Stripe. So I am inside of executor.ts instead of the Stripe trigger folder. And in here instead of Google Form trigger executor, it's going to be So let me just first resolve the name, stripe trigger executor. Then let's change the channel to be stripe trigger, stripe trigger channel. All instances should also use stripe trigger channel.
The step name should be stripe trigger. There we go. And now I think we're done. I think if I go ahead and do find in folder again Google form or Google does not exist as a search result. Perfect.
So now I think we might actually be ready to try this. So the only way we can actually try this is since we don't have any transform nodes we can just go ahead and add this just so we have like two nodes available let's go ahead and just use any here just set whatever you want click Save And basically what we have to do now is we have to configure this inside of Stripe to make it work. And before we actually do this in Stripe, it would be a good idea to create the webhook. I completely forgot that we need the webhook as well. Lucky for us, it is super simple to what we had before.
So just going to set up source app API, webhooks, Google Form, Go ahead and copy it and paste it here, rename it to Stripe. If it asks you to update the imports you can select yes and then you get this cache, you can save that, close it and make sure to close that folder, it's not important. Go inside of StripeRoute.ts and let's go ahead and improve this. So this is exactly the same. We still need the workflow ID and now the form data will be a little bit different.
So what I suggest that you keep here is at least the event metadata basically allowing the user to quickly access the event ID for example. Then you could do event type basically just those useful things. Obviously you would modify this you know how your users are using this and what they expect, what they think it's better. And then to give them all the other useful things, I would suggest using raw like this. And in here, they can basically access customer ID, amount, currency, session ID, payment status, customer email, description of the product, everything.
You can simplify this for them as much as you want, right? But do keep in mind that you would probably want to do it per stripe event because every stripe event has a different data object. So because of that, you should probably be careful with what you pass here. Let's just go ahead and fix this. Instead of Google form webhook error, this would be stripe webhook error, failed to process stripe event.
Stripe event, that's it. Now in order to send workflow execution, we have to modify the initial data to be Stripe. And I will just pass in Stripe data here. And this would be Stripe data. There we go.
Perfect. So once we have this, I think we should be ready. So make sure it is inside of webhooks stripe. And now we can go back in here. And basically, depending on when you created your stripe account, you might have this sandbox thing or maybe you won't have it.
Just go ahead and try and create a new account. Keep in mind that I think you can only create like one sandbox if you don't verify your business. Verifying your business basically means that you need to have an actual business information. So if you're just doing this for development, you have to use the single sandbox that you have, or just create a completely new account on Stripe. And then you will get this new sandbox thing.
And in here, you have to access webhooks somehow. So I just search for them in here and click a webhooks. You can see I already have one, so I'm just going to delete it so it doesn't confuse you. Another two ways you can do this. You can do it with an actual production ready URL link, which would be useful for when you actually deploy this application.
But another way would be here, test with a local listener here. So in order to make that work, you need to download the Stripe CLI. It is as easy to do this as ngrok. So just go ahead and follow the install the Stripe CLI on Mac OS, Windows or Linux. So depending on what you use, you can go ahead and see all the instructions.
So in order to check if you did this correctly, go ahead and type stripe and you shouldn't see any error instead you should see the flags that are available. All right now let me go back here and let's try and trigger an event here. Let's try and make that happen. So click test with a local listener here. Let's go ahead and first do Stripe login.
There we go. So go ahead and open this. Once you open it, you should allow access. Just always confirm that what you see here is exactly what you see here. So you're not accidentally allowing access to some other device.
You may now close this window. Perfect. There we go. So this step is now correct. And now we have to do the other step, which would basically be 3000 API, webhooks forward slash stripe.
And yes, I think we also need the exact, but basically this right here. So just copy that part too. So forward to and just this entire URL. Obviously in production this would be much easier. Your user would just copy this.
They will go ahead and actually add the webhook endpoint here. But we can't do that because it cannot target localhost. So what you can do is you can use your ngrok forwarded URL and then add it here as the actual destination. But I think it's just simple to do this too. Okay, no matches found.
Maybe we need to like wrap this Like that. Does that work? Okay. So now that we are connected here, let me just see. We should try and trigger an event.
So I'm going to open a new tab and let's see, did they do this correctly or not? So Stripe trigger payment intent succeeded. And looks like it is receiving something, but it's receiving back 500. So I don't think that this event fired. Is this the one that just happened?
I'm not sure. I'm trying to figure out, oh, no executor found for node Stripe trigger. Oh, I think everything is actually working. We're just making one mistake here. We forgot to do inside of stripe features executions lib executor registry, we forgot to add node type stripe trigger stripe trigger executor.
Make sure you import it. So the same mistake I made with the Google form trigger. And I think that now it should actually work. So let's just go ahead and focus on this. So it should just trigger this and then this should trigger that as simple as well that.
Oh yeah. You need to have this forwarding here. Let's go ahead and do this again. Let's wait for a second. And there we go.
So we successfully triggered our app. Looks like when we run this, it run a few times. That's probably because it received, as you can see, a lot of events. I think that's probably because instead of route.ds in the stripe here, what I'm doing here is just I'm accepting like anything here, but you will probably want to like limit this to specific events that your user required with that workflow ID of course and then it shouldn't react that many times because I'm not sure if payment intent succeeded only fires once or multiple times because we can see that in here it fires a lot of time now I can still see 500 here I think that's because I always need to return something so return next response dot json Let's go ahead and do success true. And let's go ahead and add status here.
Of 200. One thing I forgot to tell you, yes, is that webhooks always need to end with some kind of successful return. Otherwise they will keep repeating. So let's try this again. So I'm running this locally.
I'm going to try and trigger this again since I already know it's working. There we go. Now we have 200. Perfect. And this is still working.
Great. So yes, make sure that you add that here And you should probably also add it in the Google Form 1. So always, you know, end up saying some kind of success message so they know they don't have to retry the webhook because there's a limit to how long they will retry before they turn it off. And it is that easy to do it with Stripe. So if you're confused, like why do we need this local listener?
Well, because it's easier to demonstrate in development mode, but you don't have to use it. So if I just go ahead and close this, what I would usually do is in here I wouldn't see localhost 3000 I would see mydomain.com or you could use the ngrok public domain and then inside of here you would click add destination your users would click that. I'm not sure not too familiar with Stripe I think. It should be your account. They will select the events they want to listen to.
I don't know. I think the most popular ones are inside of checkout, checkout session completed. When someone successfully purchases something they will select webhook endpoint and then they would basically paste that URL with your actual domain that would be here, right? But in here, you can see it noticed that it's a localhost. So if you're using localhost, you need to use Stripe CLI.
So even if you want to use your ngrok one, you could do that. But then you have another problem. I mean, problem. It's not a problem. It's just hard to test.
Even if you added this, so node-based webhook test, it would be a bit difficult to like test it. Oh, you can do send test events. Okay, Maybe not too difficult. So I think if I refresh here, it should work even if I send the test event from here. Okay, so it's still telling me to do it through the CLI, right?
Testing the events is difficult. You would have to set up an entire Stripe app to make it work. But let me see if I can just like fire this event and see if that works. Let's wait a second. Looks like not.
Again, I'm not sure if this is because of the way I... Maybe I have to log in again. Let me go ahead and check inside of the events here. This is something else. This is not that, I think.
Yeah, I think this is just being confused now because I logged in to test it locally. And now I didn't log in again to test it here. So node webhook test uses HTTPS, then my ngrok URL, webhooks stripe with this specific workflow ID that I'm in, that's important. And if I click send test events, Let's go ahead and try and stripe login again. Maybe I have to do it again.
Just trying to prove that it still works, but maybe I'm missing something obvious. I mean, the code definitely works. That's not the problem. I'm just trying to bring it as close to production as possible for you. Let's see.
Yeah, I'm not sure why it's not working now. Let me see inside of npm here. HTTPS. Webhooks edit destination. So this should be a completely valid endpoint.
Let's just see. 405 is correct in this case. I should be getting 405 because it's an invalid method request I'm not seeing anything new here oh because I didn't select my events Let me go ahead and try and find the event I'm actually firing. Payment intent succeeded. Payment intent.
There we go. It's such a bad search function. Okay, fifth time is the charm. Let's try the event again. Maybe now it will work when it listens to the event.
There we go. And you can see how it doesn't repeat it now. So it looks like yeah, it only repeats in localhost version. When it's actually using the URL, it doesn't repeat the events. Perfect.
Now, obviously in a real world app, you will have to protect this endpoint. Now, as I said, I will try to find time to add some like basic protection for our webhooks because they're not publicly available. Literally anyone can find a workflow ID and just trigger this endpoint. That's not good, right? So we would have to protect it in some way.
With Stripe, you can actually do it quite easily because your users would simply have to copy the signing secret. You would have to allow them to add the signing secret somewhere here, right? And then in the webhook, we will simply check if that signing secret is correct. And if it's not, break the endpoint, someone else try to access this, right? Who is not the user who added Stripe.
But if you wanted to do it without that, so like a universal protector, you would have to like generate a secret every time you create a workflow. And then you can reuse that kind of authentication flow for all of your webhooks. That's something I'm going to try and find time for. And if you want to, you can also use Svicks, which is another thing I think I demonstrated already. It's basically Webhooks as a service.
It's really cool. A lot of like high profile companies use them. As you can see, like Clark, Brax, these are really like high profile companies. So if you're looking for production level protection, you could look into six. Amazing amazing job.
I think that is everything we wanted to do in this chapter. Let's see, we added the Stripe node, dialogue, executor, real-time channel, webhook, and we managed to trigger a Stripe event both through a local host and through our port forwarded ngrok public domain so we know it's going to work in production as well. So 23 Stripe trigger. Let's go ahead and merge that, create a new branch. 23 stripe trigger.
I'm going to go ahead. Add all of the changes 23 stripe trigger commit and I'm going to publish the branch. And since this was almost identical to our previous pull request, we don't have to review it again because we know exactly, we just literally copied the same number of files, right? So 15 files I have here, we added a new event, we added an icon. And then we just copied every single thing we had with the Google form.
And we just repurposed it to work with Stripe. We also fixed the issue of invalid types in the executor registry. But besides that, I don't think there's anything worth waiting here. We can just go ahead and merge this. But still, it stands what CodeRabbit told us in the previous chapter that we should protect these endpoints somehow, right?
So the same is true for this one. And let's go ahead now and once we merge it, Go back to main, go ahead and click on synchronize changes, OK. And instead of your source control tab here, once it synchronizes, open graph and confirm that you have it here, 23 stripe trigger. Great. So I believe that marks the end of this chapter.
We pushed to GitHub, we reviewed it technically because it's exactly the same as the previous one. Amazing job and see you in the next chapter.