Accepting Payments in Flask Using Stripe and Vue.js

Accepting Payments in Flask Using Stripe and Vue.js

In this tutorial, we'll develop a web app for selling books using Stripe (for payment processing), Vue.js (the client-side app), and Flask (the server-side API).

This is an intermediate-level tutorial. It assumes that you a have basic working knowledge of Vue and Flask. Review the following resources for more info:

  1. Introduction to Vue
  2. Flaskr: Intro to Flask, Test-Driven Development (TDD), and JavaScript

Final app:

Main dependencies:

  • Vue v2.6.10
  • Vue CLI v3.7.0
  • Node v12.1.0
  • npm v6.9.0
  • Flask v1.0.2
  • Python v3.7.3

Objectives

By the end of this tutorial, you will be able to:

  1. Work with an existing CRUD app, powered by Vue and Flask
  2. Create an order checkout component
  3. Validate a form with vanilla JavaScript
  4. Use Stripe to validate credit card information
  5. Process payments using the Stripe API

Web app for selling books using Stripe Setup

Clone the flask-vue-stripe repo, and then check out the base tag to the master branch:

$ git clone https://github.com/testdrivenio/flask-vue-stripe --branch base --single-branch
$ cd flask-vue-stripe
$ git checkout tags/base -b master

Create and activate a virtual environment, and then spin up the Flask app:

$ cd server
$ python3.7 -m venv env
$ source env/bin/activate
(env)$ pip install -r requirements.txt
(env)$ python app.py
The above commands, for creating and activating a virtual environment, may differ depending on your environment and operating system.

Point your browser of choice at http://localhost:5000/ping. You should see:

"pong!"

Then, install the dependencies and run the Vue app in a different terminal tab:

$ cd client
$ npm install
$ npm run serve

Navigate to http://localhost:8080. Make sure the basic CRUD functionality works as expected:

Want to learn how to build this project? Check out the Developing a Single Page App with Flask and Vue.js blog post.

What are we building?

Our goal is to build a web app that allows end users to purchase books.

The client-side Vue app will display the books available for purchase, collect payment information, obtain a token from Stripe, and send that token along with the payment info to the server-side.

The Flask app packages that info together and sends it to Stripe to process charges.

Finally, we'll use a client-side Stripe library, Stripe.js, to generate a unique token for creating a charge and a server-side Python library for interacting with the Stripe API.

Like the previous tutorial, Developing a Single Page App with Flask and Vue.js, we'll only be dealing with the happy path through the app. Check your understanding by incorporating proper error-handling on your own.

Books CRUD App

First, let's add a purchase price to the existing list of books on the server-side and update the appropriate CRUD functions on the client -- GET, POST, and PUT.

GET

Start by adding the price to each dict in the BOOKS list in server/app.py:

BOOKS = [
    {
        'id': uuid.uuid4().hex,
        'title': 'On the Road',
        'author': 'Jack Kerouac',
        'read': True,
        'price': '19.99'
    },
    {
        'id': uuid.uuid4().hex,
        'title': 'Harry Potter and the Philosopher\'s Stone',
        'author': 'J. K. Rowling',
        'read': False,
        'price': '9.99'
    },
    {
        'id': uuid.uuid4().hex,
        'title': 'Green Eggs and Ham',
        'author': 'Dr. Seuss',
        'read': True,
        'price': '3.99'
    }
]

Then, update the table in the Books component, client/src/components/Books.vue, to display the purchase price:

<table class="table table-hover">
  <thead>
    <tr>
      <th scope="col">Title</th>
      <th scope="col">Author</th>
      <th scope="col">Read?</th>
      <th scope="col">Purchase Price</th>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr v-for="(book, index) in books" :key="index">
      <td>{{ book.title }}</td>
      <td>{{ book.author }}</td>
      <td>
        <span v-if="book.read">Yes</span>
        <span v-else>No</span>
      </td>
      <td>${{ book.price }}</td>
      <td>
        <div class="btn-group" role="group">
          <button type="button"
                  class="btn btn-warning btn-sm"
                  v-b-modal.book-update-modal
                  @click="editBook(book)">
              Update
          </button>
          <button type="button"
                  class="btn btn-danger btn-sm"
                  @click="onDeleteBook(book)">
              Delete
          </button>
        </div>
      </td>
    </tr>
  </tbody>
</table>

You should now see:


POST

Add a new b-form-group to the addBookModal, between the author and read b-form-groups:

<b-form-group id="form-price-group"
              label="Purchase price:"
              label-for="form-price-input">
  <b-form-input id="form-price-input"
                type="number"
                step="0.01"
                v-model="addBookForm.price"
                required
                placeholder="Enter price">
  </b-form-input>
</b-form-group>

The modal should now look like:

<!-- add book modal -->
<b-modal ref="addBookModal"
        id="book-modal"
        title="Add a new book"
        hide-footer>
  <b-form @submit="onSubmit" @reset="onReset" class="w-100">
    <b-form-group id="form-title-group"
                  label="Title:"
                  label-for="form-title-input">
        <b-form-input id="form-title-input"
                      type="text"
                      v-model="addBookForm.title"
                      required
                      placeholder="Enter title">
        </b-form-input>
    </b-form-group>
    <b-form-group id="form-author-group"
                  label="Author:"
                  label-for="form-author-input">
      <b-form-input id="form-author-input"
                    type="text"
                    v-model="addBookForm.author"
                    required
                    placeholder="Enter author">
      </b-form-input>
    </b-form-group>
    <b-form-group id="form-price-group"
                  label="Purchase price:"
                  label-for="form-price-input">
      <b-form-input id="form-price-input"
                    type="number"
                    step="0.01"
                    v-model="addBookForm.price"
                    required
                    placeholder="Enter price">
      </b-form-input>
    </b-form-group>
    <b-form-group id="form-read-group">
        <b-form-checkbox-group v-model="addBookForm.read" id="form-checks">
          <b-form-checkbox value="true">Read?</b-form-checkbox>
        </b-form-checkbox-group>
    </b-form-group>
    <b-button-group>
      <b-button type="submit" variant="primary">Submit</b-button>
      <b-button type="reset" variant="danger">Reset</b-button>
    </b-button-group>
  </b-form>
</b-modal>

Then, add price to the state:

addBookForm: {
  title: '',
  author: '',
  read: [],
  price: '',
},

The state is now bound to the form's input value. Think about what this means. When the state is updated, the form input will be updated as well -- and vice versa. Here's an example of this in action with the vue-devtools browser extension:

Add the price to the payload in the onSubmit method like so:

onSubmit(evt) {
  evt.preventDefault();
  this.$refs.addBookModal.hide();
  let read = false;
  if (this.addBookForm.read[0]) read = true;
  const payload = {
    title: this.addBookForm.title,
    author: this.addBookForm.author,
    read, // property shorthand
    price: this.addBookForm.price,
  };
  this.addBook(payload);
  this.initForm();
},

Update initForm to clear out the value after the end user submits the form or clicks the "reset" button:

initForm() {
  this.addBookForm.title = '';
  this.addBookForm.author = '';
  this.addBookForm.read = [];
  this.addBookForm.price = '';
  this.editForm.id = '';
  this.editForm.title = '';
  this.editForm.author = '';
  this.editForm.read = [];
},

Finally, update the route in server/app.py:

@app.route('/books', methods=['GET', 'POST'])
def all_books():
    response_object = {'status': 'success'}
    if request.method == 'POST':
        post_data = request.get_json()
        BOOKS.append({
            'id': uuid.uuid4().hex,
            'title': post_data.get('title'),
            'author': post_data.get('author'),
            'read': post_data.get('read'),
            'price': post_data.get('price')
        })
        response_object['message'] = 'Book added!'
    else:
        response_object['books'] = BOOKS
    return jsonify(response_object)

Test it out!

Don't forget to handle errors on both the client and server!

PUT

Do the same, on your own, for editing a book:

  1. Add a new form input to the modal
  2. Update editForm in the state
  3. Add the price to the payload in the onSubmitUpdate method
  4. Update initForm
  5. Update the server-side route
Need help? Review the previous section again. You can also grab the final code from the flask-vue-stripe repo.

Order Page

Next, let's add an order page where users will be able to enter their credit card information to purchase a book.

Add a purchase button

Start by adding a "purchase" button to the Books component, just below the "delete" button:

<td>
  <div class="btn-group" role="group">
    <button type="button"
            class="btn btn-warning btn-sm"
            v-b-modal.book-update-modal
            @click="editBook(book)">
        Update
    </button>
    <button type="button"
            class="btn btn-danger btn-sm"
            @click="onDeleteBook(book)">
        Delete
    </button>
    <router-link :to="`/order/${book.id}`"
                class="btn btn-primary btn-sm">
        Purchase
    </router-link>
  </div>
</td>

Here, we used the router-link component to generate an anchor tag that links back to a route in client/src/router/index.js, which we'll set up shortly.


Create the template

Add a new component file called Order.vue to "client/src/components":

<template>
  <div class="container">
    <div class="row">
      <div class="col-sm-10">
        <h1>Ready to buy?</h1>
        <hr>
        <router-link to="/" class="btn btn-primary">
          Back Home
        </router-link>
        <br><br><br>
        <div class="row">
          <div class="col-sm-6">
            <div>
              <h4>You are buying:</h4>
              <ul>
                <li>Book Title: <em>Book Title</em></li>
                <li>Amount: <em>$Book Price</em></li>
              </ul>
            </div>
            <div>
              <h4>Use this info for testing:</h4>
              <ul>
                <li>Card Number: 4242424242424242</li>
                <li>CVC Code: any three digits</li>
                <li>Expiration: any date in the future</li>
              </ul>
            </div>
          </div>
          <div class="col-sm-6">
            <h3>One time payment</h3>
            <br>
            <form>
              <div class="form-group">
                <label>Credit Card Info</label>
                <input type="text"
                       class="form-control"
                       placeholder="XXXXXXXXXXXXXXXX"
                       required>
              </div>
              <div class="form-group">
                <input type="text"
                       class="form-control"
                       placeholder="CVC"
                       required>
              </div>
              <div class="form-group">
                <label>Card Expiration Date</label>
                <input type="text"
                       class="form-control"
                       placeholder="MM/YY"
                       required>
              </div>
              <button class="btn btn-primary btn-block">Submit</button>
            </form>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
You'll probably want to collect the buyer's contact details, like first and last name, email address, shipping address, and so on. Do this on your own.

Add the route

client/src/router/index.js:

import Vue from 'vue';
import Router from 'vue-router';
import Books from './components/Books.vue';
import Order from './components/Order.vue';
import Ping from './components/Ping.vue';

Vue.use(Router);

export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'Books',
component: Books,
},
{
path: '/order/:id',
name: 'Order',
component: Order,
},
{
path: '/ping',
name: 'Ping',
component: Ping,
},
],
});

Test it out.


Get the product info

Next, let's update the placeholders for the book title and amount on the order page:

Hop back over to the server-side and update the following route handler:

@app.route('/books/<book_id>', methods=['GET', 'PUT', 'DELETE'])
def single_book(book_id):
response_object = {'status': 'success'}
if request.method == 'GET':
# TODO: refactor to a lambda and filter
return_book = ''
for book in BOOKS:
if book['id'] == book_id:
return_book = book
response_object['book'] = return_book
if request.method == 'PUT':
post_data = request.get_json()
remove_book(book_id)
BOOKS.append({
'id': uuid.uuid4().hex,
'title': post_data.get('title'),
'author': post_data.get('author'),
'read': post_data.get('read'),
'price': post_data.get('price')
})
response_object['message'] = 'Book updated!'
if request.method == 'DELETE':
remove_book(book_id)
response_object['message'] = 'Book removed!'
return jsonify(response_object)

Now, we can hit this route to add the book information to the order page within the script section of the component:

<script>
import axios from 'axios';

export default {
data() {
return {
book: {
title: '',
author: '',
read: [],
price: '',
},
};
},
methods: {
getBook() {
const path = http://localhost:5000/books/${this.$route.params.id};
axios.get(path)
.then((res) => {
this.book = res.data.book;
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
},
},
created() {
this.getBook();
},
};
</script>

Shipping to production? You'll want to use an environment variable to dynamically set the base server-side URL (which is currently http://localhost:5000). Review the docs for more info.

Then, update the first ul in the template:

<ul>
<li>Book Title: <em>{{ book.title }}</em></li>
<li>Amount: <em>${{ book.price }}</em></li>
</ul>

You should now see:

Form Validation

Let's set up some basic form validation.

Use the v-model directive to bind form input values back to the state:

<form>
<div class="form-group">
<label>Credit Card Info</label>
<input type="text"
class="form-control"
placeholder="XXXXXXXXXXXXXXXX"
v-model="card.number"
required>
</div>
<div class="form-group">
<input type="text"
class="form-control"
placeholder="CVC"
v-model="card.cvc"
required>
</div>
<div class="form-group">
<label>Card Expiration Date</label>
<input type="text"
class="form-control"
placeholder="MM/YY"
v-model="card.exp"
required>
</div>
<button class="btn btn-primary btn-block">Submit</button>
</form>

Add the card to the state like so:

card: {
number: '',
cvc: '',
exp: '',
},

Next, update the "submit" button so that when the button is clicked, the normal browser behavior is ignored and a validate method is called instead:

<button class="btn btn-primary btn-block" @click.prevent="validate">Submit</button>

Add an array to the state to hold any validation errors:

data() {
return {
book: {
title: '',
author: '',
read: [],
price: '',
},
card: {
number: '',
cvc: '',
exp: '',
},
errors: [],
};
},

Just below the form, we can iterate and display the errors:

<div v-show="errors">
<br>
<ol class="text-danger">
<li v-for="(error, index) in errors" :key="index">
{{ error }}
</li>
</ol>
</div>

Add the validate method:

validate() {
this.errors = [];
let valid = true;
if (!this.card.number) {
valid = false;
this.errors.push('Card Number is required');
}
if (!this.card.cvc) {
valid = false;
this.errors.push('CVC is required');
}
if (!this.card.exp) {
valid = false;
this.errors.push('Expiration date is required');
}
if (valid) {
this.createToken();
}
},

Since all fields are required, we are simply validating that each field has a value. Keep in mind that Stripe will validate the actual credit card info, which you'll see in the next section, so you don't need to go overboard with form validation. That said, be sure to validate any additional fields that you may have added on your own.

Finally, add a createToken method:

createToken() {
// eslint-disable-next-line
console.log('The form is valid!');
},

Test this out.

Stripe


Sign up for a Stripe account, if you don't already have one, and grab the test mode API Publishable key.


Client-side

Add the key to the state along with stripeCheck (which will be used to disable the submit button):

data() {
return {
book: {
title: '',
author: '',
read: [],
price: '',
},
card: {
number: '',
cvc: '',
exp: '',
},
errors: [],
stripePublishableKey: 'pk_test_aIh85FLcNlk7A6B26VZiNj1h',
stripeCheck: false,
};
},
Make sure to add your own Stripe key to the above code.

Again, if the form is valid, the createToken method is triggered, which validates the credit card info (via Stripe.js) and then either returns an error (if invalid) or a unique token (if valid):

createToken() {
this.stripeCheck = true;
window.Stripe.setPublishableKey(this.stripePublishableKey);
window.Stripe.createToken(this.card, (status, response) => {
if (response.error) {
this.stripeCheck = false;
this.errors.push(response.error.message);
// eslint-disable-next-line
console.error(response);
} else {
// pass
}
});
},

If there are no errors, we send the token to the server, where we'll charge the card, and then send the user back to the main page:

createToken() {
this.stripeCheck = true;
window.Stripe.setPublishableKey(this.stripePublishableKey);
window.Stripe.createToken(this.card, (status, response) => {
if (response.error) {
this.stripeCheck = false;
this.errors.push(response.error.message);
// eslint-disable-next-line
console.error(response);
} else {
const payload = {
book: this.book,
token: response.id,
};
const path = 'http://localhost:5000/charge';
axios.post(path, payload)
.then(() => {
this.$router.push({ path: '/' });
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
}
});
},

Update createToken() with the above code, and then add Stripe.js to client/public/index.html:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Flask + Vue CRUD</title>
</head>
<body>
<noscript>
<strong>
We're sorry but client doesn't work properly without JavaScript enabled. Please enable it to continue.
</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
<script type="text/javascript" src="https://js.stripe.com/v2/"></script>
</body>
</html>
Stripe supports v2 and v3 (Stripe Elements) of Stripe.js. If you're curious about Stripe Elements and how you can integrate it into Vue, refer to the following resources:
  1. Stripe Elements Migration Guide
  2. Integrating Stripe Elements and Vue.js to Set Up a Custom Payment Form

Now, when createToken is triggered, stripeCheck is set to true. To prevent duplicate charges, let's disable the "submit" button when stripeCheck is true:

<button class="btn btn-primary btn-block"
@click.prevent="validate"
:disabled="stripeCheck">
Submit
</button>

Test out the Stripe validation for invalid:

  1. Credit card numbers
  2. Security codes
  3. Expiration dates

Now, let's get the server-side route set up.


Server-side

Install the Stripe library:

(env)$ pip install stripe==2.27.0

Add the route handler:

@app.route('/charge', methods=['POST'])
def create_charge():
post_data = request.get_json()
amount = round(float(post_data.get('book')['price']) * 100)
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')
charge = stripe.Charge.create(
amount=amount,
currency='usd',
card=post_data.get('token'),
description=post_data.get('book')['title']
)
response_object = {
'status': 'success',
'charge': charge
}
return jsonify(response_object), 200

Here, given the book price (which we converted to cents), the unique token (from the createToken method on the client), and the book title, we generated a new Stripe charge with the API Secret key.

For more on creating a charge, refer to the official API docs.

Update the imports:

import os
import uuid

import stripe
from flask import Flask, jsonify, request
from flask_cors import CORS

Grab the test-mode API Secret key:

Set it as an environment variable:

$ export STRIPE_SECRET_KEY=sk_test_io02FXL17hrn2TNvffanlMSy
Make sure to use your own Stripe key!

Test it out!

You should see the purchase back in the Stripe Dashboard:

Instead of just creating a charge, you may want to also create a customer. This has many advantages. You can charge multiple items to the same customer, making it easier to track customer purchase history. You could offer deals to customers that purchase frequently or reach out to customers that haven't purchased in a while, just to name a few. It also helps to prevent fraud. Refer to the following Flask example to see how to add customer creation.

Order Complete Page

Rather than sending the buyer back to the main page, let's redirect them to an order complete page, thanking them for making a purchase.

Add a new component file called OrderComplete.vue to "client/src/components":

<template>
<div class="container">
<div class="row">
<div class="col-sm-10">
<h1>Thanks for purchasing!</h1>
<hr><br>
<router-link to="/" class="btn btn-primary btn-sm">Back Home</router-link>
</div>
</div>
</div>
</template>

Update the router:

import Vue from 'vue';
import Router from 'vue-router';
import Books from './components/Books.vue';
import Ping from './components/Ping.vue';
import Order from './components/Order.vue';
import OrderComplete from './components/OrderComplete.vue';

Vue.use(Router);

export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'Books',
component: Books,
},
{
path: '/order/:id',
name: 'Order',
component: Order,
},
{
path: '/complete',
name: 'OrderComplete',
component: OrderComplete,
},
{
path: '/ping',
name: 'Ping',
component: Ping,
},
],
});

Update the redirect in the createToken method in client/src/components/Order.vue:

createToken() {
this.stripeCheck = true;
window.Stripe.setPublishableKey(this.stripePublishableKey);
window.Stripe.createToken(this.card, (status, response) => {
if (response.error) {
this.stripeCheck = false;
this.errors.push(response.error.message);
// eslint-disable-next-line
console.error(response);
} else {
const payload = {
book: this.book,
token: response.id,
};
const path = 'http://localhost:5000/charge';
axios.post(path, payload)
.then(() => {
this.$router.push({ path: '/complete' });
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
}
});
},

Finally, you could also display info about the book (title, amount, etc.) the customer just purchased on the order complete page.

Grab the unique charge id and pass it into the path:

createToken() {
this.stripeCheck = true;
window.Stripe.setPublishableKey(this.stripePublishableKey);
window.Stripe.createToken(this.card, (status, response) => {
if (response.error) {
this.stripeCheck = false;
this.errors.push(response.error.message);
// eslint-disable-next-line
console.error(response);
} else {
const payload = {
book: this.book,
token: response.id,
};
const path = 'http://localhost:5000/charge';
axios.post(path, payload)
.then((res) => {
// updates
this.$router.push({ path: /complete/${res.data.charge.id} });
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
}
});
},

Update the client-side route:

{
path: '/complete/:id',
name: 'OrderComplete',
component: OrderComplete,
},

Then, in OrderComplete.vue, grab the charge id for the URL and send it to the server-side:

<script>
import axios from 'axios';

export default {
data() {
return {
book: '',
};
},
methods: {
getChargeInfo() {
const path = http://localhost:5000/charge/${this.$route.params.id};
axios.get(path)
.then((res) => {
this.book = res.data.charge.description;
})
.catch((error) => {
// eslint-disable-next-line
console.error(error);
});
},
},
created() {
this.getChargeInfo();
},
};
</script>

Configure the new route on the server to retrieve the charge:

@app.route('/charge/<charge_id>')
def get_charge(charge_id):
stripe.api_key = os.environ.get('STRIPE_SECRET_KEY')
response_object = {
'status': 'success',
'charge': stripe.Charge.retrieve(charge_id)
}
return jsonify(response_object), 200

Finally, update the <h1></h1> in the template section of the OrderComplete component:

<h1>Thanks for purchasing - {{ this.book }}!</h1>

Test it out one last time.

Conclusion

That's it! Be sure to review the objectives from the top. You can find the final code in the flask-vue-stripe repo on GitHub.

Looking for more?

  1. Add client and server-side unit and integration tests.
  2. Create a shopping cart so customers can purchase more than one book at a time.
  3. Add Postgres to store the books and the orders.
  4. Containerize Vue and Flask (and Postgres, if you add it) with Docker to simplify the development workflow.
  5. Add images to the books and create a more robust product page.
  6. Capture emails and send email confirmations
  7. Deploy the client-side static files to AWS S3 and the server-side app to an EC2 instance.
  8. Going into production? Think about the best way to update the Stripe keys so they are dynamic based on the environment.
  9. Create a separate component for checking out.


Thanks for reading

If you liked this post, share it with all of your programming buddies!

Follow us on Facebook | Twitter

Learn More

Vue JS 2 - The Complete Guide (incl. Vue Router & Vuex)

Nuxt.js - Vue.js on Steroids

Build Web Apps with Vue JS 2 & Firebase

Front-end Developer Handbook 2019

ES Module Browser Build in Vue

Implementing Authentication in a Nuxt.js App

Getting Started with Vuex: Managing State in Vue.js

Build a Server-Side Rendered Vue App with Nuxt.js

React vs. Vue vs. Angular

Getting up and Running with the Vue.js 2.0 Framework

Hands-on Vue.js for Beginners

Vue CLI 3 Full-Stack App Structure

Originally published on https://testdriven.io

Top Vue.js Developers in USA

Top Vue.js Developers in USA

Vue.js is an extensively popular JavaScript framework with which you can create powerful as well as interactive interfaces. Vue.js is the best framework when it comes to building a single web and mobile apps.

We, at HireFullStackDeveloperIndia, implement the right strategic approach to offer a wide variety through customized Vue.js development services to suit your requirements at most competitive prices.

Vue.js is an open-source JavaScript framework that is incredibly progressive and adoptive and majorly used to build a breathtaking user interface. Vue.js is efficient to create advanced web page applications.

Vue.js gets its strength from the flexible JavaScript library to build an enthralling user interface. As the core of Vue.js is concentrated which provides a variety of interactive components for the web and gives real-time implementation. It gives freedom to developers by giving fluidity and eases the integration process with existing projects and other libraries that enables to structure of a highly customizable application.

Vue.js is a scalable framework with a robust in-build stack that can extend itself to operate apps of any proportion. Moreover, vue.js is the best framework to seamlessly create astonishing single-page applications.

Our Vue.js developers have gained tremendous expertise by delivering services to clients worldwide over multiple industries in the area of front-end development. Our adept developers are experts in Vue development and can provide the best value-added user interfaces and web apps.

We assure our clients to have a prime user interface that reaches end-users and target the audience with the exceptional user experience across a variety of devices and platforms. Our expert team of developers serves your business to move ahead on the path of success, where your enterprise can have an advantage over others.

Here are some key benefits that you can avail when you decide to hire vue.js developers in USA from HireFullStackDeveloperIndia:

  • A team of Vue.js developers of your choice
  • 100% guaranteed client satisfaction
  • Integrity and Transparency
  • Free no-obligation quote
  • Portal development solutions
  • Interactive Dashboards over a wide array of devices
  • Vue.js music and video streaming apps
  • Flexible engagement model
  • A free project manager with your team
  • 24*7 communication with your preferred means

If you are looking to hire React Native developers in USA, then choosing HireFullStackDeveloperIndia would be the best as we offer some of the best talents when it comes to Vue.js.

Top Web Application Developer

Top Web Application Developer

You can also contact a web application development company for your business but then why not to contact the best web application development company that can turn up your business and customer satisfaction ratio to sky touching heights.

Not long-ago internet came into existence and the world has never been the same ever since. The Internet made sure that people and business do evolve at a faster rate than ever and was never merciful to the slow ones. Because of this competition and availability of any business with few clicks made India one of the hubs as IT center. This trend of constantly updating has given rise to smartphones, smart machines, wearable gadgets and a lot more is yet to come in the upcoming years. In such time it is always a good idea to hire Web App Developer from India at your service. They are expert in developing not only websites but web applications as well.

We at HireFullStackDeveloperIndia, have a huge team of experienced developers that have grasped over different domains in front and back end development. You can hire web app developers in India from us with many advantages that you won’t get anywhere else in the industry.

Here is why we are the best option if you are looking forward to hiring web app developers in India:

  • Flexible hiring models available, as per your convenience and requirement
  • Maximum ROI, compared to any other company or team of developers.
  • We provide you with Source code Authorization meaning code written for you belong only to you. It cannot be used or copied anywhere else.
  • All of our developers are sound with agile development methodology, so you will be in a constant loop of suggestions, ideas, trends, and updates about your project.
  • Our developers are good with creating custom web applications as well; it guarantees you a better product without any compromise of non-existing functionalities.
  • You can save huge costing on infrastructure by utilizing our hire web app developer in India program.
  • Your hired developer or team will be easily accessible to your preferred mode of communication.
  • You get to exercise complete control over your team or individual that you hire.
  • We believe in Integrity and Transparency.
  • Our developers are highly creative and motivated to deliver excellence.


HireFullStackDeveloperIndia is a Web Application Development Company in India that is known worldwide for our Innovative guaranteed solutions. You can inquire with us about your project and we will be providing you multiple suitable developers that are the best fit for your requirements. You can evaluate them and select one or multiple whosoever deems fit to you. After this, all you have to do is provide your valuable input to the resource through sprint base project development until you get delivery of your project.

Our engagement model will also allow you to get our development team to your site location and proceed with development from your premises.

Create a Voice-Controlled Web Visualization with Vue.js and Machine Learning

Create a Voice-Controlled Web Visualization with Vue.js and Machine Learning

To Create a Voice-Controlled Web Visualization with Vue.js and Machine Learning, we’ll pair Vue.js, three.js and LUIS (Cognitive Services) to create a voice-controlled web visualization.

In this tutorial, we’ll pair Vue.js, three.js and LUIS (Cognitive Services) to create a voice-controlled web visualization.

What can we do if we can control the behaviors of the web in our favor, just by speaking? For an experiment, I decided to use LUIS, which is a machine learning-based service to build natural language through the use of custom models that can continuously improve. We can use this for apps, bots, and IoT devices. This way, we can create a visualization that responds to any voice — and it can improve itself by learning along the way.

Here’s a bird’s eye view of what we're building:

Setting up LUIS

We’ll get a free trial account for Azure and then go to the portal. We’ll select Cognitive Services.

After picking New → AI/Machine Learning, we’ll select "Language Understanding" (or LUIS).

Then we’ll pick out our name and resource group.

We’ll collect our keys from the next screen and then head over to the LUIS dashboard

It’s actually really fun to train these machines! We’ll set up a new application and create some intents, which are outcomes we want to trigger based on a given condition. Here’s the sample from this demo:

You may notice that we have a naming schema here. We do this so that it’s easier to categorize the intents. We’re going to first figure out the emotion and then listen for the intensity, so the initial intents are prefixed with either App (these are used primarily in the App.vue component) or Intensity.

If we dive into each particular intent, we see how the model is trained. We have some similar phrases that mean roughly the same thing:

You can see we have a lot of synonyms for training, but we also have the "Train" button up top for when we’re ready to start training the model. We click that button, get a success notification, and then we’re ready to publish. 

Setting up Vue

We’ll create a pretty standard Vue.js application via the Vue CLI. First, we run:

Bash
vue create three-vue-pattern
# then select Manually...

Vue CLI v3.0.0

? Please pick a preset:
default (babel, eslint)
❯ Manually select features

Then select the PWA feature and the other ones with the spacebar

? Please pick a preset: Manually select features
? Check the features needed for your project:
◉ Babel
◯ TypeScript
◯ Progressive Web App (PWA) Support
◯ Router
◉ Vuex
◉ CSS Pre-processors
◉ Linter / Formatter
◯ Unit Testing
◯ E2E Testing

? Pick a linter / formatter config:
ESLint with error prevention only
ESLint + Airbnb config
❯ ESLint + Standard config
ESLint + Prettier

? Pick additional lint features: (Press <space> to select, a to toggle all, i to invert selection)
❯ ◉ Lint on save
◯ Lint and fix on commit

Successfully created project three-vue-pattern.
Get started with the following commands:

$ cd three-vue-pattern
$ yarn serve

This will spin up a server for us and provide a typical Vue welcome screen. We’ll also add some dependencies to our application: three.js, sine-waves, and axios. three.js will help us create the WebGL visualization. sine-waves gives us a nice canvas abstraction for the loader. axios will allow us a really nice HTTP client so we can make calls to LUIS for analysis.

Bash
yarn add three sine-waves axios

Setting up our Vuex store

Now that we have a working model, let’s go get it with axios and bring it into our Vuex store. Then we can disseminate the information to all of the different components.

In state, we’ll store what we’re going to need:

JavaScript
state: {
intent: 'None',
intensity: 'None',
score: 0,
uiState: 'idle',
zoom: 3,
counter: 0,
},

intent and intensity will store the App, intensity, and intents, respectively. The score will store our confidence (which is a score from 0 to 100 measuring how well the model thinks it can rank the input).

For uiState, we have three different states:

  • idle - waiting for the user input
  • listening - hearing the user input
  • fetching - getting user data from the API

Both zoom and counter are what we’ll use to update the data visualization.

Now, in actions, we’ll set the uiState (in a mutation) to fetching, and we’ll make a call to the API with axios using the generated keys we received when setting up LUIS.

JavaScript
getUnderstanding({ commit }, utterance) {
commit('setUiState', 'fetching')
const url = https://westus.api.cognitive.microsoft.com/luis/v2.0/apps/4aba2274-c5df-4b0d-8ff7-57658254d042

https: axios({
method: 'get',
url,
params: {
verbose: true,
timezoneOffset: 0,
q: utterance
},
headers: {
'Content-Type': 'application/json',
'Ocp-Apim-Subscription-Key': ‘XXXXXXXXXXXXXXXXXXX'
}
})

Then, once we’ve done that, we can get the top-ranked scoring intent and store it in our state.

We also need to create some mutations we can use to change the state. We’ll use these in our actions. In the upcoming Vue 3.0, this will be streamlined because mutations will be removed.

JavaScript
newIntent: (state, { intent, score }) => {
if (intent.includes('Intensity')) {
state.intensity = intent
if (intent.includes('More')) {
state.counter++
} else if (intent.includes('Less')) {
state.counter--
}
} else {
state.intent = intent
}
state.score = score
},
setUiState: (state, status) => {
state.uiState = status
},
setIntent: (state, status) => {
state.intent = status
},

This is all pretty straightforward. We’re passing in the state so that we can update it for each occurrence — with the exception of Intensity, which will increment the counter up and down, accordingly. We’re going to use that counter in the next section to update the visualization.

JavaScript
.then(({ data }) => {
console.log('axios result', data)
if (altMaps.hasOwnProperty(data.query)) {
commit('newIntent', {
intent: altMaps[data.query],
score: 1
})
} else {
commit('newIntent', data.topScoringIntent)
}
commit('setUiState', 'idle')
commit('setZoom')
})
.catch(err => {
console.error('axios error', err)
})

In this action, we’ll commit the mutations we just went over or log an error if something goes wrong.

The way that the logic works, the user will do the initial recording to say how they’re feeling. They’ll hit a button to kick it all off. The visualization will appear and, at that point, the app will continuously listen for the user to say less or more to control the returned visualization. Let’s set up the rest of the app.

Setting up the app

In App.vue, we’ll show two different components for the middle of the page depending on whether or not we’ve already specified our mood.

HTML
<app-recordintent v-if="intent === 'None'" />
<app-recordintensity v-if="intent !== 'None'" :emotion="intent" />

Both of these will show information for the viewer as well as a SineWaves component while the UI is in a listening state.

The base of the application is where the visualization will be displayed. It will show with different props depending on the mood. Here are two examples:

HTML
<app-base
v-if="intent === 'Excited'"
:t-config.a="1"
:t-config.b="200"
/>
<app-base
v-if="intent === 'Nervous'"
:t-config.a="1"
:color="0xff0000"
:wireframe="true"
:rainbow="false"
:emissive="true"
/>

Setting up the data visualization

There were a number of major changes that needed to be done to make this work, and it actually ended up being a massive undertaking, even if the final visual expression appears similar to the original.

  • Due to the fact that we would need to tear down the visualization if we decided to change it, I had to convert the existing code to use bufferArrays, which are more performant for this purpose.
  • The original code was one large chunk, so I broke up some of the functions into smaller methods on the component to make it easier to read and maintain.
  • Because we want to update things on the fly, I had to store some of the items as data in the component, and eventually as props that it would receive from the parent. I also included some nice defaults (excited is what all of the defaults look like).
  • We use the counter from the Vuex state to update the distance of the camera’s placement relative to the object so that we can see less or more of it and thus it becomes more and less complex.

In order to change up the way that it looks according to the configurations, we’ll create some props:

JavaScript
props: {
numAxes: {
type: Number,
default: 12,
required: false
},
...
tConfig: {
default() {
return {
a: 2,
b: 3,
c: 100,
d: 3
}
},
required: false
}
},

We’ll use these when we create the shapes:

JavaScript
createShapes() {
this.bufferCamera.position.z = this.shapeZoom

if (this.torusKnot !== null) {
this.torusKnot.material.dispose()
this.torusKnot.geometry.dispose()
this.bufferScene.remove(this.torusKnot)
}

var shape = new THREE.TorusKnotGeometry(
this.tConfig.a,
this.tConfig.b,
this.tConfig.c,
this.tConfig.d
),
material
...
this.torusKnot = new THREE.Mesh(shape, material)
this.torusKnot.material.needsUpdate = true

this.bufferScene.add(this.torusKnot)
},

This is now split out into its own method. We’ll also create another method that kicks off the animation, which will also restart whenever it updates. The animation makes use of requestAnimationFrame:

JavaScript
animate() {
this.storeRAF = requestAnimationFrame(this.animate)

this.bufferScene.rotation.x += 0.01
this.bufferScene.rotation.y += 0.02

this.renderer.render(
this.bufferScene,
this.bufferCamera,
this.bufferTexture
)
this.renderer.render(this.scene, this.camera)
},

We’ll create a computed property called shapeZoom that will return the zoom from the store. If you recall, this will be updated as the user's voice changes the intensity.

JavaScript
computed: {
shapeZoom() {
return this.$store.state.zoom
}
},

We can then use a watcher to see if the zoom level changes and cancel the animation, recreate the shapes, and restart the animation.

JavaScript
watch: {
shapeZoom() {
this.createShapes()
cancelAnimationFrame(this.storeRAF)
this.animate()
}
},

In data, we’re also storing some things we’ll need for instantiating the three.js scene — most notably making sure that the camera is exactly centered.

JavaScript
data() {
return {
bufferScene: new THREE.Scene(),
bufferCamera: new THREE.PerspectiveCamera(75, 800 / 800, 0.1, 1000),
bufferTexture: new THREE.WebGLRenderTarget(800, 800, {
minFilter: THREE.LinearMipMapLinearFilter,
magFilter: THREE.LinearFilter,
antialias: true
}),
camera: new THREE.OrthographicCamera(
window.innerWidth / -2,
window.innerWidth / 2,
window.innerHeight / 2,
window.innerHeight / -2,
0.1,
1000
),

There’s more to this demo, if you’d like to explore the repo or set it up yourself with your own parameters. The init method does what you think it might: it initializes the whole visualization. I’ve commented a lot of the key parts if you’re peeping at the source code. There’s also another method that updates the geometry that’s called — you uessed it — updateGeometry. You may notice a lot of vars in there as well. That’s because it’s common to reuse variables in this kind of visualization. We kick everything off by calling this.init() in the mounted() lifecycle hook.

It’s pretty fun to see how far you can get creating things for the web that don’t necessarily need any hand movement to control. It opens up a lot of opportunities!

I hope this tutorial will surely help and you if you liked this tutorial, please consider sharing it with others.

Thanks for reading.