Building a Simple Vue.js Interactive Credit Card Form: A Beginner’s Guide

In today’s digital landscape, secure and user-friendly online payment forms are paramount. Whether you’re building an e-commerce platform, a subscription service, or any application that requires financial transactions, a well-designed credit card form is essential. This guide will walk you through building a simple, yet functional, interactive credit card form using Vue.js, a popular JavaScript framework known for its simplicity and ease of use. We’ll cover everything from the basic HTML structure to incorporating real-time validation, providing a solid foundation for handling sensitive user data securely.

Why Build Your Own Credit Card Form?

You might be wondering, “Why not just use a third-party payment gateway?” While services like Stripe or PayPal offer convenient solutions, building your own credit card form has several advantages:

  • Customization: You have complete control over the form’s appearance and behavior, allowing you to tailor it to your brand and user experience.
  • Direct Integration: You can integrate the form seamlessly into your website’s design, providing a consistent look and feel.
  • Data Control: You have more control over the data collected, which can be beneficial for analytics and personalization (while adhering to PCI DSS compliance).
  • Learning Opportunity: Building a credit card form is an excellent way to learn about front-end development, form validation, and security best practices.

However, it’s crucial to understand that handling credit card data directly requires adhering to strict security standards, such as PCI DSS (Payment Card Industry Data Security Standard). This guide focuses on the front-end aspects of the form. The actual processing of credit card information should always be handled by a secure payment gateway. We’ll focus on creating a user-friendly and validated form that can then securely transmit data to a payment processor.

Setting Up Your Vue.js Project

Before we dive into the code, let’s set up a basic Vue.js project. If you don’t have Vue CLI (Command Line Interface) installed, you’ll need to install it globally:

npm install -g @vue/cli

Then, create a new Vue project:

vue create credit-card-form

During project creation, choose the default setup or manually select features. For simplicity, we’ll use the default settings. Once the project is created, navigate to the project directory:

cd credit-card-form

Now, let’s start the development server:

npm run serve

This will typically launch your application at http://localhost:8080/ (or a similar address). Open the project in your preferred code editor.

Building the HTML Structure

We’ll start by creating the basic HTML structure for our credit card form. Open the src/App.vue file (or your main component file) and replace its content with the following:

<template>
 <div class="credit-card-form">
 <h2>Credit Card Details</h2>
 <form @submit.prevent="handleSubmit">
 <div class="form-group">
 <label for="cardNumber">Card Number</label>
 <input type="text" id="cardNumber" v-model="cardNumber" placeholder="XXXX-XXXX-XXXX-XXXX" />
 <span class="error" v-if="errors.cardNumber">{{ errors.cardNumber }}</span>
 </div>

 <div class="form-group">
 <label for="cardholderName">Cardholder Name</label>
 <input type="text" id="cardholderName" v-model="cardholderName" placeholder="John Doe" />
 <span class="error" v-if="errors.cardholderName">{{ errors.cardholderName }}</span>
 </div>

 <div class="form-group">
 <label for="expiryDate">Expiry Date</label>
 <input type="text" id="expiryDate" v-model="expiryDate" placeholder="MM/YY" />
 <span class="error" v-if="errors.expiryDate">{{ errors.expiryDate }}</span>
 </div>

 <div class="form-group">
 <label for="cvv">CVV</label>
 <input type="text" id="cvv" v-model="cvv" placeholder="XXX" />
 <span class="error" v-if="errors.cvv">{{ errors.cvv }}</span>
 </div>

 <button type="submit">Submit</button>
 </form>
 </div>
</template>

<script>
 export default {
 data() {
 return {
 cardNumber: '',
 cardholderName: '',
 expiryDate: '',
 cvv: '',
 errors: {
 cardNumber: '',
 cardholderName: '',
 expiryDate: '',
 cvv: ''
 }
 };
 },
 methods: {
 handleSubmit() {
 // This is where you would send the data to a payment gateway.
 // For this example, we'll just log the data to the console.
 console.log('Card Number:', this.cardNumber);
 console.log('Cardholder Name:', this.cardholderName);
 console.log('Expiry Date:', this.expiryDate);
 console.log('CVV:', this.cvv);
 }
 }
 };
</script>

<style scoped>
 .credit-card-form {
 width: 400px;
 margin: 20px auto;
 padding: 20px;
 border: 1px solid #ccc;
 border-radius: 5px;
 }

 .form-group {
 margin-bottom: 15px;
 }

 label {
 display: block;
 font-weight: bold;
 margin-bottom: 5px;
 }

 input[type="text"] {
 width: 100%;
 padding: 10px;
 border: 1px solid #ccc;
 border-radius: 4px;
 font-size: 16px;
 }

 button {
 background-color: #4CAF50;
 color: white;
 padding: 12px 20px;
 border: none;
 border-radius: 4px;
 cursor: pointer;
 font-size: 16px;
 }

 button:hover {
 background-color: #3e8e41;
 }

 .error {
 color: red;
 font-size: 12px;
 }
</style>

Let’s break down this code:

  • Template: The <template> section defines the HTML structure. We have a container div with the class credit-card-form, a heading, a form, and various input fields for card number, cardholder name, expiry date, and CVV.
  • v-model: The v-model directive binds the input fields to the corresponding data properties in the Vue instance (e.g., cardNumber, cardholderName). This creates a two-way binding, so changes in the input fields automatically update the data, and vice versa.
  • @submit.prevent: This event listener prevents the default form submission behavior (which would reload the page). Instead, it calls the handleSubmit method.
  • Errors: We have <span class="error"> elements that will display error messages if any validation fails. The v-if="errors.cardNumber" directive conditionally renders the error message based on the errors data property.
  • Script: The <script> section contains the Vue component’s logic. The data() function initializes the data properties, and the methods object contains the handleSubmit method, which will handle the form submission (currently logging the data to the console).
  • Style: The <style scoped> section contains the CSS styles for the form. The scoped attribute ensures that these styles only apply to this component.

This is the basic structure. Save the file and check your browser; you should see the form rendered.

Adding Basic Form Validation

Now, let’s add some basic validation to ensure the user enters valid information. We’ll add validation rules for each field. Modify the handleSubmit method and add a new method called validateForm:

methods: {
 validateForm() {
 this.errors = {}; // Reset errors
 let isValid = true;

 if (!this.cardNumber) {
 this.errors.cardNumber = 'Card number is required.';
 isValid = false;
 } else if (!/^[0-9]{13,19}$/.test(this.cardNumber.replace(/s/g, ''))) {
 this.errors.cardNumber = 'Invalid card number.';
 isValid = false;
 }

 if (!this.cardholderName) {
 this.errors.cardholderName = 'Cardholder name is required.';
 isValid = false;
 } else if (!/^[a-zA-Zs]+$/.test(this.cardholderName)) {
 this.errors.cardholderName = 'Invalid cardholder name.';
 isValid = false;
 }

 if (!this.expiryDate) {
 this.errors.expiryDate = 'Expiry date is required.';
 isValid = false;
 } else if (!/^(0[1-9]|1[0-2])/([0-9]{2})$/.test(this.expiryDate)) {
 this.errors.expiryDate = 'Invalid expiry date (MM/YY).';
 isValid = false;
 }

 if (!this.cvv) {
 this.errors.cvv = 'CVV is required.';
 isValid = false;
 } else if (!/^[0-9]{3,4}$/.test(this.cvv)) {
 this.errors.cvv = 'Invalid CVV.';
 isValid = false;
 }

 return isValid;
 },
 handleSubmit() {
 if (this.validateForm()) {
 // If the form is valid, submit the data to the payment gateway
 console.log('Card Number:', this.cardNumber);
 console.log('Cardholder Name:', this.cardholderName);
 console.log('Expiry Date:', this.expiryDate);
 console.log('CVV:', this.cvv);
 } else {
 // If the form is invalid, do nothing or display a general error message.
 console.log('Form has errors');
 }
 }
}

Here’s what changed:

  • validateForm() method: This method is responsible for validating the form fields.
  • Error Reset: this.errors = {}; resets the error object at the beginning of the validation to clear any previous errors.
  • Required Field Checks: We check if each field is empty. If it is, we set an error message.
  • Regular Expressions: We use regular expressions (regex) to validate the format of the card number, cardholder name, expiry date, and CVV. For example, /^[0-9]{13,19}$/.test(this.cardNumber.replace(/s/g, '')) checks if the card number consists of 13-19 digits (removing spaces). The expiry date uses a regex to match the MM/YY format.
  • isValid flag: A boolean flag to track whether the form is valid.
  • handleSubmit() modification: Now, the handleSubmit method calls validateForm() first. If the form is valid (validateForm() returns true), the data is submitted (currently logged to the console). If the form is invalid, we log a message (you could display a general error message to the user).

Refresh your browser. Now, when you submit the form, you should see error messages displayed next to the invalid fields.

Enhancing the User Experience

Let’s add some features to enhance the user experience:

1. Real-time Input Formatting

To improve usability, we can format the card number and expiry date as the user types. This makes it easier for the user to enter the information correctly. We’ll use computed properties for this.


computed: {
 formattedCardNumber() {
 if (!this.cardNumber) return '';
 const cardNumber = this.cardNumber.replace(/D/g, ''); // Remove non-digits
 const formatted = cardNumber.match(/.{1,4}/g)?.join(' ') || '';
 return formatted;
 },
 formattedExpiryDate() {
 if (!this.expiryDate) return '';
 const expiryDate = this.expiryDate.replace(/D/g, ''); // Remove non-digits
 if (expiryDate.length <= 2) {
 return expiryDate;
 } else {
 return `${expiryDate.substring(0, 2)}/${expiryDate.substring(2, 4)}`;
 }
 }
},
watch: {
 cardNumber(newVal) {
 this.cardNumber = this.formattedCardNumber;
 },
 expiryDate(newVal) {
 this.expiryDate = this.formattedExpiryDate;
 }
}

Here’s what this code does:

  • formattedCardNumber computed property: This computed property takes the raw cardNumber, removes any non-digit characters, and then formats it into groups of four digits separated by spaces. The match(/.{1,4}/g)?.join(' ') part is crucial; it splits the number into groups of four and joins them with spaces. The optional chaining operator (?.) handles cases where the number is empty or null, preventing errors.
  • formattedExpiryDate computed property: This computed property formats the expiry date. It removes non-digit characters. If the input is less than or equal to two characters, it returns the input as is. Otherwise, it formats the input into MM/YY format.
  • watch: The watch section is used to observe changes in the cardNumber and expiryDate data properties. When these properties change, the corresponding computed property is called to format the input. The formatted value is then assigned back to the original cardNumber and expiryDate, updating the input fields.

Update your template with the formatted values. Replace the v-model binding in your template for the card number and expiry date with the computed property.


 <input type="text" id="cardNumber" :value="formattedCardNumber" @input="cardNumber = $event.target.value" placeholder="XXXX-XXXX-XXXX-XXXX" />
 <input type="text" id="expiryDate" :value="formattedExpiryDate" @input="expiryDate = $event.target.value" placeholder="MM/YY" />

In the above code:

  • We use :value to bind the formatted value to the input field, which will update the input field’s display.
  • We use @input to listen to the input event, which is triggered every time the input value changes.
  • We assign the new input value to the corresponding data property.

2. Card Type Detection (Optional)

You can add card type detection to automatically identify the credit card type (Visa, Mastercard, American Express, etc.) as the user types the card number. This provides visual feedback and improves the user experience. You will need to use a library for this, as it involves complex regex patterns to match the card number prefixes.

Here’s how you might incorporate a card type detection library (e.g., card-validator):

  1. Install the library:
npm install card-validator
  1. Import the library and add a cardType data property:
import * as cardValidator from 'card-validator';

data() {
 return {
 // ... other data properties
 cardType: null,
 };
},
  1. Update the cardNumber watcher to detect card type:

watch: {
 cardNumber(newVal) {
 this.cardNumber = this.formattedCardNumber;
 const { card } = cardValidator.number(this.cardNumber.replace(/s/g, ''));
 this.cardType = card ? card.type : null;
 },
 expiryDate(newVal) {
 this.expiryDate = this.formattedExpiryDate;
 }
}
  1. Display the card type (e.g., an icon): Add a conditional rendering in your template to display an icon based on the cardType.

 <div class="form-group">
 <label for="cardNumber">Card Number</label>
 <div style="display: flex; align-items: center;">
 <input type="text" id="cardNumber" :value="formattedCardNumber" @input="cardNumber = $event.target.value" placeholder="XXXX-XXXX-XXXX-XXXX" />
 <img v-if="cardType" :src="`/images/${cardType}.svg`" :alt="cardType" style="width: 30px; margin-left: 10px;" />
 </div>
 <span class="error" v-if="errors.cardNumber">{{ errors.cardNumber }}</span>
 </div>

You’ll need to create SVG images (e.g., visa.svg, mastercard.svg, etc.) and place them in an images directory. This is a simplified example; you might need to adjust the paths based on your project structure.

3. CVV Field Security

For the CVV field, consider using type="password" to mask the input. This is a basic security measure to prevent the CVV from being easily visible.


 <input type="password" id="cvv" v-model="cvv" placeholder="XXX" />

Remember that the CVV should never be stored on your server. It’s only used for the transaction and should be passed directly to the payment gateway.

Common Mistakes and How to Fix Them

  • Incorrect Regular Expressions: Regular expressions can be tricky. Test them thoroughly to ensure they accurately validate the input. Use online regex testers to help.
  • Missing Validation: Always validate all the required fields. Leaving out validation can lead to security vulnerabilities and data integrity issues.
  • Storing Sensitive Data: Never store credit card details directly in your application. Always use a secure payment gateway.
  • Ignoring PCI DSS Compliance: If you handle credit card data, you must comply with PCI DSS standards. This includes secure coding practices, data encryption, and regular security audits.
  • Not Using a Payment Gateway: Do not attempt to process payments directly. Always use a reputable payment gateway like Stripe, PayPal, or Braintree.
  • Poor User Experience: A clunky or confusing form can lead to abandoned transactions. Focus on a clear and intuitive design with real-time feedback.
  • Insufficient Error Handling: Provide clear and helpful error messages to guide the user.

Summary / Key Takeaways

Building a credit card form in Vue.js is a practical project that can enhance your front-end development skills. This guide has covered the fundamental steps, from setting up the project to implementing basic validation and improving the user experience. Remember these key takeaways:

  • Security First: Always prioritize security. Never store sensitive credit card data directly. Use a secure payment gateway.
  • Validation is Crucial: Validate all user inputs to ensure data integrity and prevent errors.
  • User Experience Matters: Design a user-friendly form with clear instructions and real-time feedback.
  • Real-time Input Formatting: Use input formatting to improve the user experience.
  • Compliance is Essential: If you handle credit card data, adhere to PCI DSS compliance requirements.
  • Keep it Simple: Start with a basic form and gradually add more features.

By following these guidelines and continuously learning, you can create secure and efficient credit card forms that integrate seamlessly into your web applications. Remember, building a credit card form is a responsible task, and it’s essential to prioritize security and user experience at every step. This project offers a valuable opportunity to hone your Vue.js skills while contributing to a safer online environment. By using a framework like Vue.js, you can build dynamic, interactive forms that feel modern and responsive, improving the overall user experience and increasing the likelihood of successful transactions. Always stay updated with the latest security practices and payment gateway requirements to ensure your forms remain secure and compliant.