You can 🎥 watch a video to see how this server was implemented and read the transcripts.
CJ Avilla (00:00): In this episode, we cover the basic server side implementation for accepting a one-time payment with a custom form. If you're interested in a faster integration path using Stripe hosted checkout, head over to the checkout playlist in the Stripe developers channel. The payment flow you'll see today for collecting a one-time payment has two steps. First creating the payment intent on the server, and second confirming the payment on the client, using the client secret for the payment intent. In this episode, you'll learn how to add an endpoint to your server, to create the payment intent.
CJ Avilla (00:33): Then depending on whether you're using Vanilla JS, React on the web, or Stripe iOS or Stripe Android on mobile, you can watch videos for those specific front-end implementations that will pair with the code we implement here. So rather than start from scratch, we're going to jumpstart our server implementation using the developer office hours based sample. If you want to see how we go from zero to one, check out the starter episode, linked in the description.
CJ Avilla (00:57): You might also be interested in an episode about working with the Stripe CLI. It's a really handy tool for helping you build and test your Stripe integrations. So from the terminal, we run Stripe samples, create, developer office hours, and we'll give this one an alias of tutorial. We can now pick node and that'll download and scaffold a bare bones project with a simple client and server. In this episode, we'll spend all of our time in the server directory and in future episodes, we'll implement the front end to confirm payment. Let's change into the server directory, install dependencies with npm install, and then open server.js.
CJ Avilla (01:36): You'll see server.js already contains a root route for rendering some basic HTML, and a skeleton for the web hook end point. So at a minimum, we need to add one new server end point to create a payment intent that will later confirm the front end. This route will accept post requests, as by convention, we're creating a new resource, a payment intent. Let's stick with the same naming conventions as the Stripe docs and name our route create payment intent. So the logic in this route is very simple. We create a payment intent using the Stripe node client library to make an API request to Stripe. Initially we'll hard code and pass just the minimum required arguments of amount and currency. This amount value is denoted in the smallest denomination for the given currency. So in this case, it's in cents.
CJ Avilla (02:22): Finally, we need to return some json with only the client secret property of the newly created payment intent. Let's jump into another terminal instance so we can start the server with NPM start and experiment with the end point using curl to make a direct request to this payment intent endpoint. So this will be a post request to local host 4242 with some application json in the request body. For now, we're just going to send an empty json object. Notice the response is well-formed json and includes the client secret for the newly created payment intent.
CJ Avilla (02:57): Returning to our code let's talk about this API call for creating a payment intent. There's more than two dozen optional parameters available for tailoring the payment experience for your customers. One of the optional parameters, payment method types, which takes a list of string values for the payment method options you'd like to allow your customers to pay with.
CJ Avilla (03:18): Now by default, this is set to an array with one element. It's just a string with the word card, making this payment intent only confirmable with a credit card payment method. Now in practice, you could hard code a list of string values for all the payment method types you want to accept here. Now in this series, you'll learn how to accept a wide variety of payment method types, so let's refactor our end point to accept some arguments by deserializing the json request body, and we're going to extract out the payment method type and furthermore, some payment method types only work with specific currency. So let's allow the currency to be passed from the client as well.
CJ Avilla (03:58): Okay. Let's head back over to the terminal, restart our server and update our curl request to pass in the payment method type in the currency in the request body. That works great but if we try to pass an AU Becs debit as our payment method type, and we keep USD as the currency notice this fails because AU Becs debit only works with Australian dollars. In this case, our promise was rejected, but no error handling caught the exception. If we update our curl request to pass AUD, the server responds with a client secret as expected. So in the future, we'll want to surface these failures in a nice format so that our front ends can consistently parse and present errors to our customers. So let's wrap this API call and a try catch block and render an error response with well-formed json in a failure case. So we're just going to return a 400 and follow the structure where the response has an error property pointing at an object with a string message. This matches the shape returned directly from the Stripe API for client side calls.
CJ Avilla (05:00): So it'll make implementing the error handling logic on the clients just a little bit easier to reason about. After restarting our server and attempting to create a payment intent with an invalid combination of the payment method type in currency we'll now see a well-formed json error response. This is great. Most payment method types, complete payment asynchronously. For some, payments complete nearly instantly like cards, but for others, payments can take a few days or even more to complete.
CJ Avilla (05:27): Due to the asynchronous nature of the way money flows through networks, we highly recommend implementing web hooks in order to automate fulfillment. We have episodes all about how to set up your web hook handler. So let's update our web hook end point to simply print to the server log when payment intent events fire. It's quite common to automate things like email notifications, updating your database, pulling from inventory, printing shipping labels, and more as part of your web hook handler. It's also worth noting that you could use a third party tool like Zapier or IFTTT as a low-code or no-code solution for handling web hook notifications. So for now, we'll simply check to see if the event type is one of payment intent created and print a simple status update.
CJ Avilla (06:11): From the terminal. We'll restart our server and then test our new web hook logic with the Stripe CLI using the listen command. Stripe listen forms a direct connection from Stripe to this locally running server so that when events happen on the demo Stripe account, they're delivered to the development server without needing any tunneling software, like ngrok. The straight listen command, accepts a URL to which events should be forwarded. Now, if we successfully create a new payment intent with, let's say ideal and Euros, we see in the server log that the payment intent was created and we can see in the logs for the Stripe listen that the payment intent created event fire. When receiving event types that start with payment intent dot an instance of the payment intent is available on the event.data.object. Let's improve our log statement now to include the IDs of the event and the payment intent and the status of the payment intent.
CJ Avilla (07:03): We'll restart the server and fire another test to confirm our improved log statement. Now we see the idea of the event, the idea of the payment intent and the status of the payment intent. Note that this payment intent was not created with a payment method so its status is requires payment. We'll assign a new payment method when confirming the payment on the front end again in a future episode. So as we add support for more payment methods in future episodes, it'll be interesting to see all of these status messages in the server logs and align those with what the customer is seeing and with the various states for payment intents. Note that depending on which payment method types you plan to support and which automations you're planning to build, you likely won't need to handle every single one of these event types. At this point, the server is technically ready to add a front end.
CJ Avilla (07:53): However, we'll add one last simple configure out for fetching a publishable key so that we don't need to hard-code that on the front end. This simple helper is purely for serving our publishable keys when receiving a get request to slash config. This is a best practice when working with mobile clients so that if for some reason you need to roll your API keys, the publishable key is not hard-coded into the production mobile apps that are shipped to users. That is required that all mobile users install an updated version of the app to get a new key. As a quick recap, we went through and added a new end point to create payment intents, which included an API call to create a payment intent that passed the amount currency, and now a specific type of payment method that we want to allow. We also added some logging to the web hook handler for debugging and looking to the future when implementing application logic to automate fulfillment. And finally, we added a quick helper route for returning the publishable key so that we don't hard-code that in mobile clients.
CJ Avilla (08:53): All right, next, we recommend heading over to one of the playlists that most closely fits your front end implementation. You can use the links in the description, or just head over to the Stripe developers channel and take a look at those playlists thanks for watching and we'll see you in the next one.