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:
- Setup and Testing
- Stripe Function and Hosting
- Application and Checkout Component (This Post)
- Configure the Checkout Component (Coming Soon)
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.
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:
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:
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:
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:
- Setup and Testing
- Stripe Function and Hosting
- Application and Checkout Component (This Post)
- Configure the Checkout Component (Coming Soon)
Creating a Vue.js Serverless Checkout Form: Application and Checkout Component is a post from CSS-Tricks