Creating a Vue.js Serverless Checkout Form: Configure the Checkout Component

This is the fourth post in a four-part series. In Part one, we set up a serverless Stripe function on Azure. Part two covered how we hosted the function on Github. The third part covered Stripe Elements in Vue. This last post shows how to configure the checkout component and make the shopping cart fully functional.

Article Series:

  1. Setup and Testing
  2. Stripe Function and Hosting
  3. Application and Checkout Component
  4. Configure the Checkout Component (This Post)

As a reminder, here’s where we are in our application at this point:

cart and checkout

Configuring the Checkout Component

We have to do a few things to adjust the component in order for it to meet our needs:

  • Make sure the form is only displaying if we haven’t submitted it—we’ll deal with the logic for this in our pay method in a moment
  • Allow the form to take a customer’s email address in case something is wrong with the order.
  • Disable the submit button until the required email address is provided
  • Finally and most importantly, change to our testing key

Here’s our updated checkout component with the changes to the original code highlighted:

<div v-if="!submitted" class="payment"> <h3>Please enter your payment details:</h3> <label for="email">Email</label> <input id="email" type="email" v-model="stripeEmail" placeholder="name@example.com"/> <label for="card">Credit Card</label> <p>Test using this credit card: <span class="cc-number">4242 4242 4242 4242</span>, and enter any 5 digits for the zip code</p> <card class='stripe-card' id="card" :class='{ complete }' stripe='pk_test_5ThYi0UvX3xwoNdgxxxTxxrG' :options='stripeOptions' @change='complete = $event.complete' /> <button class='pay-with-stripe' @click='pay' :disabled='!complete || !stripeEmail'>Pay with credit card</button>
</div>

There are a number of things we need to store and use for this component, so let’s add them to data or bring them in as props. The props that we need from our parent component will be total and success. We’ll need the total amount of the purchase so we can send it to Stripe, and the success will be something we need to coordinate between this component and the parent, because both components need to know if the payment was successful. I write out the datatypes as well as a default for my props.

props: { total: { type: [Number, String], default: '50.00' }, success: { type: Boolean, default: false }
},

Next, the data we need to store will be the stripeEmail we collected from the form, stripeOptions that I left in to show you can configure some options for your form, as well as status and response that we’ll get from communicating with the server and Stripe. We also want to hold whether or not the form was submitted, and whether the form was completed for enabling and disabling the submit button, which can both be booleans.

data() { return { submitted: false, complete: false, status: '', response: '', stripeOptions: { // you can configure that cc element. I liked the default, but you can // see https://stripe.com/docs/stripe.js#element-options for details }, stripeEmail: '' };
},

Now for the magic! We have everything we need—we just need to alter our pay() method, which will do all the heavy lifting for us. I’m going to use Axios for this, which will receive and transform the data:

npm i axios --save

…or using Yarn:

yarn add axios

If you’re not familiar with Axios and what it does, check out this article for some background information.

pay() { createToken().then(data => { this.submitted = true; // we'll change the flag to let ourselves know it was submitted console.log(data.token); // this is a token we would use for the stripeToken below axios .post( 'https://sdras-stripe.azurewebsites.net/api/charge?code=zWwbn6LLqMxuyvwbWpTFXdRxFd7a27KCRCEseL7zEqbM9ijAgj1c1w==', { stripeEmail: this.stripeEmail, // send the email stripeToken: data.token.id, // testing token stripeAmt: this.total // send the amount }, { headers: { 'Content-Type': 'application/json' } } ) .then(response => { this.status = 'success'; this.$emit('successSubmit'); this.$store.commit('clearCartCount'); // console logs for you 🙂 this.response = JSON.stringify(response, null, 2); console.log(this.response); }) .catch(error => { this.status = 'failure'; // console logs for you 🙂 this.response = 'Error: ' + JSON.stringify(error, null, 2); console.log(this.response); }); });
},

The code above does a number of things:

  • It allows us to track whether we’ve submitted the form or not, with this.submitted
  • It uses Axios to post to our function. We got this URL from going to where the function lives in the portal, and clicking “Get Function URL” on the right side of the screen.
    get function url
  • It sends the email, token, and total to the serverless function
  • If it’s successful, it changes the status to success, commits to our Vuex store, uses a mutation to clear our cart, and emits to the parent cart component that the payment was successful. It also logs the response to the console, though this is for educational purposes and should be deleted in production.
  • If it errors, it changes the status to failure, and logs the error response for help with debugging

If the payment fails, which we’ll know from our status, we need to let the user know something went wrong, clear our cart, and allow them to try again. In our template:

<div v-if="status === 'failure'"> <h3>Oh No!</h3> <p>Something went wrong!</p> <button @click="clearCheckout">Please try again</button>
</div>

The button executes the following clearCheckout method that clears a number of the fields and allow the customer to try again:

clearCheckout() { this.submitted = false; this.status = ''; this.complete = false; this.response = '';
}

If the payment succeeds, we will show a loading component, that will play an SVG animation until we hear back from the server. Sometimes this can take a couple of seconds, so it’s important that our animation make sense if it is seen for a short or long amount of time, and can loop as necessary.

<div v-else class="loadcontain"> <h4>Please hold, we're filling up your cart with goodies</h4> <app-loader />
</div>

Here’s what that looks like:

See the Pen shop loader by Sarah Drasner (@sdras) on CodePen.

Now if we revisit the first cart component we looked at in pages/cart.vue, we can fill that page based on the logic we set up before because it’s been completed:

<div v-if="cartTotal > 0"> <h1>Cart</h1> ... <app-checkout :total="total" @successSubmit="success = true"></app-checkout>
</div> <div v-else-if="cartTotal === 0 && success === false" class="empty"> <h1>Cart</h1> <h3>Your cart is empty.</h3> <nuxt-link exact to="/"><button>Fill 'er up!</button></nuxt-link>
</div> <div v-else> <app-success @restartCart="success = false"/> <h2>Success!</h2> <p>Your order has been processed, it will be delivered shortly.</p>
</div>

If we have items in our cart, we show the cart. If the cart is empty and the success is false, we’ll let them know that their cart is empty. Otherwise, if the checkout has just been processed, we’ll let them know that everything has been executed successfully!

We are now here:

success.vue in the application

In the AppSuccess.vue component, we have a small SVG animation designed to make them feel good about the purchase:

See the Pen success by Sarah Drasner (@sdras) on CodePen.

(You may have to hit “Rerun” to replay the animation.)

We also put a small timer in the mounted() lifecycle hook:

window.setTimeout(() => this.$emit('restartCart'), 3000);

This will show the success for three seconds while they read it then kick off the restartCart that was shown in the component above. This allows us to reset the cart in case they would like to continue shopping.

Conclusion

You learned how to make a serverless function, host it on Github, add required dependencies, communicate with Stripe, set up a Shopping Cart in a Vue application, establish a connection with the serverless function and Stripe, and handle the logic for all of the cart states. Whew, way to go!

It’s worth mentioning that the demo app we looked at is a sample application built for specifically for this purpose of this tutorial. There are a number of steps you’d want to go through for a production site, including testing, building the app’s dist folder, and using real Stripe keys. There are also so many ways to set this up, Serverless functions can be so flexible in tandem with something like Vue. Hopefully this gets you on track and saves you time as you try it out yourself.


Creating a Vue.js Serverless Checkout Form: Configure the Checkout Component is a post from CSS-Tricks

Creating a Vue.js Serverless Checkout Form: Application and Checkout Component

This is the third post in a four-part series. In part one, we set up a serverless Stripe function on Azure. Part two covered how we hosted the function on Github. This post will focus on wiring everything up as a Vue.js application.

Article Series:

  1. Setup and Testing
  2. Stripe Function and Hosting
  3. Application and Checkout Component (This Post)
  4. Configure the Checkout Component

Stripe has a number of ways to build out a checkout form, the most basic being a single button on the page that you trigger to pull up their custom modal. There’s a repo and component for this, but as easy as that is to implement (it’s probably the most simple way to do it), I wanted a little more customization and wanted the checkout flow to be part of the page and application. This approach wouldn’t work for my needs.

Stripe Elements

Stripe also offers a thing called Elements. Elements allow you to integrate Stripe’s payment system into your own checkout form and style it like your own site for a cohesive experience. It won’t feel like you’re using a third party plugin. They do have some pre-styled examples if you prefer something you can use right out of the box.

Luckily for us, there’s a really nice repo with a Vue version of Stripe Elements called vue-stripe-elements. The repo’s documentation is really nice, so you could check that out. Here’s how I put it to use:

npm i vue-stripe-elements-plus --save

…or using Yarn:

yarn add vue-stripe-elements-plus

Now let’s talk about our cart and integrate it.

The Cart

Here’s what everything looks like as a birds eye view of the application. We’ve already addressed the function and stripe pieces, now let’s dig into the application itself.

birds eye view of the application structure

We’re not going to go through setting up the entire application in these posts, rather just the Cart and Checkout. I’d suggest checking out the following links before continuing if you need to catch up on the basics of Vue, Vuex, and Nuxt:

  • CSS-Tricks Vue Guide
  • Simple Server Side Rendering, Routing, and Page Transitions with Nuxt.js

In our general store set up with Vuex, we hold a manifest of all of our product data used to populate the pages with items. We’ll also use that information to populate a (currently empty) cart object where items can be added for purchase. We’ll use that data on a page called `Cart.vue` in the pages directory. If you’re unfamiliar with Nuxt.js, it allows us to use .vue components as pages by creating them in this pages directory. We can still populate these pages with components from the components directory to create a more modular application. Here are the parts we’re discussing now:

Showing we're discussing cart.vue and vuex in the application

We’ll need two pieces of information from that store in Vuex: the contents of the cart and the cartTotal.

We’ll use computed properties in pages/Cart.vue to fetch that information so that we can cache and use them in the cart.

computed: { cart() { return this.$store.state.cart; }, cartTotal() { return this.$store.state.cartTotal; }, ...
}

…and we’ll create a new computed property that will store the monetary total of the items in the cart as well:

computed: { ... total() { return Object.values(this.cart) .reduce((acc, el) => acc + (el.count * el.price), 0) .toFixed(2); }
}

The first thing that we’ll do is see if the cart has items in it. If it does, then we need to check that the payment hasn’t already been processed. We need to do this because there’s no need to display a checkout form if there are no items in the cart or if payment has already been processed for the items that were added.

<div v-if="cartTotal > 0"> <!--we'll add our checkout here-->
</div> <!--If the cart is empty, give them the ability to get back to the main page to add items-->
<div v-else-if="cartTotal === 0 && success === false" class="empty"> <!--we'll add our empty state here-->
</div> <!--If there's a success, let's let people know it's being processed, we'll add a success component later on-->
<div v-else> <!--we'll add success here-->
</div>

We’ll also create a success property in our data that we’ll initially set to false and use later to record whether or not a payment was successfully submitted.

data() { return { success: false };
},

We want to show cart items if they exist, their individual totals (as we can have multiple counts of the same item) and the final total.

<div v-if="cartTotal > 0"> <h1>Cart</h1> <div class="cartitems" v-for="item in cart" key="item"> <div class="carttext"> <h4>{{ item.name }}</h4> <p>{{ item.price | usdollar }} x {{ item.count }}</p> <p>Total for this item: <strong>{{ item.price * item.count }}</strong></p> </div> <img class="cartimg" :src="`/${item.img}`" :alt="`Image of ${item.name}`"> </div> <div class="total"> <h3>Total: {{ total | usdollar }}</h3> </div> <!--we're going to add our checkout here-->
</div>

We’re using a filter to format the prices in US dollars. I format them this way instead of hardcoding them in case I need to support other currencies in the future.

filters: { usdollar: function(value) { return `$${value}`; }
}

Setting up the Checkout Component

Now we’re going to create our checkout component, which will hold all of the Stripe checkout logic and connect to the serverless function we set up in Part Two. We’ll register the component in the Cart.vue file:

import AppCheckout from './../components/AppCheckout.vue'; export default { components: { AppCheckout }, ...
}

Here’s where we’re at now:

Showing the Cart.vue in pages, as well as the checkout component

And, in the checkout component itself, we’ll bring over the base for the file that we saw in the vue-stripe-elements repo documentation:

<template> <div id='app'> <h1>Please give us your payment details:</h1> <card class='stripe-card' :class='{ complete }' stripe='pk_test_XXXXXXXXXXXXXXXXXXXXXXXX' :options='stripeOptions' @change='complete = $event.complete' /> <button class='pay-with-stripe' @click='pay' :disabled='!complete'>Pay with credit card</button> </div>
</template>
<script>
import { stripeKey, stripeOptions } from './stripeConfig.json'
import { Card, createToken } from 'vue-stripe-elements-plus' export default { data () { return { complete: false, stripeOptions: { // see https://stripe.com/docs/stripe.js#element-options for details } } }, components: { Card }, methods: { pay () { // createToken returns a Promise which resolves in a result object with // either a token or an error key. // See https://stripe.com/docs/api#tokens for the token object. // See https://stripe.com/docs/api#errors for the error object. // More general https://stripe.com/docs/stripe.js#stripe-create-token. createToken().then(data => console.log(data.token)) } }
}
</script>

Next Up…

So far, this is what the component looks like out of the box. We’re going to have to update this component a bit to fit our needs, but not too much. Stay tuned tomorrow for the final installment when we connect our component to our serverless function and finish up the checkout!

Article Series:

  1. Setup and Testing
  2. Stripe Function and Hosting
  3. Application and Checkout Component (This Post)
  4. Configure the Checkout Component

Creating a Vue.js Serverless Checkout Form: Application and Checkout Component is a post from CSS-Tricks

Creating a Vue.js Serverless Checkout Form: Stripe Function and Hosting

We’re now in the second post of a four-part series where we’re creating a checkout form application in Vue.js that can accept payments via the Stripe API. In part one, we looked at the concept of serverless functions, set one up in Azure, and connected it to a Stripe account. In this post, we’ll focus on setting up Stripe as a serverless function and hosting it all on Github.

Article Series:

  1. Setup and Testing
  2. Stripe Function and Hosting (This Post)
  3. Application and Checkout Component
  4. Configure the Checkout Component

First, we’re going write our function and test it out in the portal, but eventually we’re going to move it over to Github and have Azure pull in the code. I’ll explain why we do this in a moment.

For now, in order to get it working and testable, we’re going to write it in the portal and fill in the request body to perform the test. But we need to know what Stripe will expect from us first.

Dun dun dun…

Working With Stripe as a Serverless Function

If you check out Stripe’s documentation, you can see that we’ll need to grab the Stripe token in the dashboard. This will eventually mirror the POST parameters submitted by our form. Stripe makes it easy, so it’s fairly straightforward to use their library for the server-side function with Express:

app.get('/', (req, res) => res.render('index.pug', { keyPublishable })); app.post('/charge', (req, res) => { let amount = 500; stripe.customers .create({ email: req.body.stripeEmail, source: req.body.stripeToken }) .then(customer => stripe.charges.create({ amount, description: 'Sample Charge', currency: 'usd', customer: customer.id }) ) .then(charge => res.render('charge.pug'));
}); app.listen(4567);

We won’t need to set up all of Node and Express for this, though, as what we really need is the amount, the currency, the description, and the token, which we can integrate with the testing code we were provided earlier in the portal’s view of our function. So, let’s head over to the Azure portal where our function lives and update that default testing code to accept the parameters we need for Stripe, and also populate the request.body in the test panel.

We’ll add our Stripe testing key and kick everything off. To be totally sure, we’re going to log what we’ve gotten started:

var stripe = require('stripe')('sk_test_whateveryourtestingkeyisgoeshere');
// ^ this is a stripe testing key module.exports = function(context, req) { context.log('starting to get down');

If we have a request body, an email, and a token, then let’s get started. We’ll create a customer from the email and then use that customer to create the Stripe charges, passing in the amount of the charge as we do so.

if ( req.body && req.body.stripeEmail && req.body.stripeToken && req.body.stripeAmt
){ stripe.customers .create({ email: req.body.stripeEmail, source: req.body.stripeToken }) .then(customer => { context.log('starting the stripe charges'); stripe.charges.create({ amount: req.body.stripeAmt, description: 'Sample Charge', currency: 'usd', customer: customer.id }); }) ...

We also want to test if this all completed successfully, or if it errored out. If it did error, we need to log what that error is. We’ll also see if the whole thing errored entirely, making sure we’re logging everything appropriately along the way.

You’ll note that I log a lot. I think it’s not enough to know that something has errored. I want to know when the error happened and why so that I can track it down. This makes it much easier to debug if something were to go wrong.

 ... .then(charge => { context.log('finished the stripe charges'); context.res = { // status: 200 body: 'This has been completed' }; context.done(); }) .catch(err => { context.log(err); context.done(); }); } else { context.log(req.body); context.res = { status: 400, body: "We're missing something" }; context.done(); }
};

In the testing area on the right side of the portal, we’ll fill the request.body with the stripeEmail, stripeToken (a testing token in this case), and some random amount for the charge. When we run this, we can see that it works! We get a 200 OK Status, and we’ve logged This has been completed in the output.

actual function with testing request body params
Testing the request body parameters with the actual function in Azure.

Github-Hosted Serverless Function

Let’s put everything in Github now that it’s working. One big reason we want to do this is because our function will have a dependency on Stripe’s library. If you head over to the sample-stripe-handler repo I’ve created for this tutorial, you’ll see a package.json file. The most important lines in that file are these:

"dependencies": { "stripe": "^5.3.0"
}

This tells the function to pull in the correct version of the Stripe API that we need to use in order for our app to properly function. As a note, you could also use this method to write other kinds of functions using other libraries. This means the possibilities for what to create are endless!

We’ll pull everything from our function into this repo. This includes the function itself, the package.json file, as well as the contents of the function.json file that you’ll see in the “View Files” tab on the right in the Azure portal.

Once we have that all in ready to go in a Github repo, we’ll head back over to the Azure portal, because now we have to let Azure know that we’d like to use this repo to host our function instead of our test. We can still test our function inside the portal—we just won’t be able to edit it via the GUI anymore.

Click on the “Platform Features” tab and select the “Deployment Options” item.

Azure Portal Deployment Options

From here, click “Settings” then “Choose source” and a number of options will be provided. I’m going to choose Github because that’s where I want to host mine, but you can see that there are a lot of other ways we could have done this.

Choose github
Deployment settings source options, including Github.

Once Github has been selected, you will be able to configure which repo you would like to use as your deployment source. I chose the sample-stripe-handler repo that we created earlier.

Choose github repo for deployment option
Configuring Github as the deployment source.

After we’ve done this and it’s loaded, you’ll be taken to a “Deployments” screen that shows the last commit that you made to the repo. That means everything’s working correctly!

first deploy

Let’s test this a little further. My function didn’t work properly the first time because I was using ES6. I could have added in Babel, but I just converted it back to ES5 and pushed to the master branch. You can see the function.json becomes inactive as the last deployment, and my latest commit message—which is mostly me grumbling—is now the latest deploy! Awesome.

New deployment working

We can’t be too careful so, to check that these tests did indeed work, I’m going to head over to the Stripe dashboard. Sure enough, there are testing charges showing up in our dashboard 😀

Stripe dashboard with a little activity

One last thing!

We would be remiss to exclude our good friend CORS, which we need to properly enable for everything to communicate as it should. Let’s go to our function in the dashboard, and select CORS:

select cors

In the prompt that appears, we’ll whitelist our localhost dev server, as well as our final URL for the site. Voila! We’re all set.

whitelist localhost

Next Up…

We got a lot done in this post! Next, we’ll want to learn how to move away from testing only within the function and get this sucker communicating freely with a checkout experience that we’ll build within a Vue.js application. Stay tuned!

Article Series:

  1. Setup and Testing
  2. Stripe Function and Hosting (This Post)
  3. Application and Checkout Component
  4. Configure the Checkout Component

Creating a Vue.js Serverless Checkout Form: Stripe Function and Hosting is a post from CSS-Tricks