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:
As a reminder, here’s where we are in our application at this point:

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="[email protected]"/>
<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.
- 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:

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, using real Stripe keys, only sending the item from the client and adding the price in the serverless function, etc. 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.
Thanks for the awesome tutorial series! Not sure if this is configured somewhere and I missed it, but I had to use the cents amounts 50.00 -> 5000 for the transactions to pull into Stripe. Thanks again!
Oh interesting! I didn’t have to do that, I wonder what was different. Thanks for letting folks know in case they run into that too.
I believe it’s not secure to directly send the price from the client side! You should mention it in the conclusion for the newcomers!
Ah yes, definitely, I’ll throw that extra bullet point in the caveats at the end. Thanks!
Hey, great article. But how do we know what customer bought? You are only sending amount to stripe. Should azure function maybe get items and store them in some db so we can deliver order?
Thanks
Yep! That’s mentioned in the conclusion :)
As a small note, NPM no longer requires the –save flag to include a package in dependencies.