DEV Community

fullStackMafia🀄
fullStackMafia🀄

Posted on • Edited on

Creating a basic online payment flow with Rave and Vue

JavaScript frameworks and libraries have enabled developers to create optimal solutions when building web apps. Single and progressive web applications have improved features such as loading time, SEO and accessibility. Lightweight frameworks such as Vue - which is incrementally adoptable and manages state seamlessly, are go-to options for anyone who wants to create a web app without the burden of structuring your application the "right” way. In this tutorial, we'll build a car rental application which will depict a basic payment process with Vue and Rave - a payment gateway used by businesses and freelancers to receive payments online. Let's begin.

Getting started

When we are done building this application, our payment flow should be something like this:

  • A user sees a car they want to rent from the list of cars available and clicks the Book Now button
  • The user gets redirected to a payment modal where they make payment (preferably via credit card)
  • When payment is made, Rave sends the user a receipt but still verifies the authenticity of the transaction. If the transaction is found to be valid, a unique authentication code is shown to the user, else an error message telling the user to retry the payment is displayed.

To build this application, we'll use the following:

  • Vue CLI: Vue's command line tool used for scaffolding Vue projects.
  • Vuetify: A material component design framework for Vue.
  • Axios: A lightweight, promise based HTTP client used for making API calls.
  • MarketCheck: An API that provides access to a database of new, used and certified vehicles.

Let's begin by installing Vue CLI. Navigate to your terminal and input the following command:


    npm install -g @vue/cli-service-global
    # or
    yarn global add @vue/cli-service-global

    #create a new Vue project
    vue create car-sales-app

    #navigate into the newly created app
    cd car-sales-app

Enter fullscreen mode Exit fullscreen mode

For styling our markup, we'll install Vuetify. In your terminal, navigate to your project's folder and input the following command to install Vuetify:


vue add vuetify
#choose default when prompted

#start a development server on localhost:8080
npm run serve
Enter fullscreen mode Exit fullscreen mode

On your browser, navigate to localhost:8080 to view the app:

Alt Text

Next, we'll install Axios. Navigate to your project's folder and input the following command:


    npm install axios

Enter fullscreen mode Exit fullscreen mode

After installing Axios, we need to obtain an API key from MarketCheck. To do this, head on to MarketCheck and create an account:

Alt Text

At this point, the main.js file, located in the root folder of our project should look like this:


    import Vue from "vue";
    import App from "./App.vue";
    import vuetify from "./plugins/vuetify";
    Vue.config.productionTip = false;
    new Vue({
      vuetify,
      render: h => h(App)
    }).$mount("#app");

Enter fullscreen mode Exit fullscreen mode

Fetching data from our API

By default, our project has a HelloWorld component. Let's delete that component and modify our App component to make requests to MarketCheck's API using Axios. Using Vue's mounted() lifecycle method, Axios makes a GET request to the API:


// App.vue
<script>
  export default {
    name: "app",
    mounted() {
      axios
        .get('https://marketcheck-prod.apigee.net/v1/search?&year=2016&make=toyota&api_key=INSERT-YOUR-API-KEY-HERE&Content-Type=application/json')
        .then(response => {
            this.carResponse = response.data.listings;
        })
        .catch(error => {
            console.log(error);
        });
    }
  }
</script>
Enter fullscreen mode Exit fullscreen mode

carResponse is a data property which is responsible for handling the data received from our API and displaying it on our browser. Let's use UI components from Vuetify and Vue directives to structure our App component:


    // App.vue
    <template>
      <div id="app">
        <header>
          <h2>
            RENT A CAR. CALL 080-RENT-A-CAR
          </h2>
        </header>
        <v-container grid-list-xl>
          <v-layout wrap>
            <v-flex xs4 v-for="car in carResponse" :key="car[0]" mb-2>
              <v-card>
                <v-img :src="car.media.photo_links[0]" aspect-ratio="2"></v-img>
                <v-card-title primary-title>
                  <div>
                    <h3>{{ car.build.make }} {{ car.build.model }}</h3>
                    <div>Year: {{ car.build.year }}</div>
                    <div>Type: {{ car.build.vehicle_type }}</div>
                    <div>Mileage: {{ car.miles }} miles</div>
                    <div>NGN {{ car.price }} / Day</div>
                  </div>
                </v-card-title>
                <v-card-actions class="justify-center">
                </v-card-actions>
              </v-card>
            </v-flex>
          </v-layout>
        </v-container>
      </div>
    </template>
    <script>
    import axios from "axios";
    export default {
      name: "app",
      data() {
        return {
          carResponse: [],
          }    
      },
      mounted() {
        axios
          .get('https://marketcheck-prod.apigee.net/v1/search?&year=2016&make=toyota&api_key=INSERT-YOUR-API-KEY-HERE&Content-Type=application/json')
          .then(response => {
            this.carResponse = response.data.listings;
          })
          .catch(error => {
            console.log(error);
          });
      }
    };
    </script>
    <style>
    #app {
      font-family: "Avenir", Helvetica, Arial, sans-serif;
      -webkit-font-smoothing: antialiased;
      -moz-osx-font-smoothing: grayscale;
      text-align: justify;
      background-color: hsla(0, 0%, 75%, 0.1);
    }
    header {
      margin-bottom: 60px;
      text-align: center;
    }
    </style>

Enter fullscreen mode Exit fullscreen mode

Here's a view of the current state of the app in our browser:

Implementing payments with Rave

For now, we are unable to receive payments for any of the displayed vehicles. Let's change that by implementing Rave's payment gateway in our app. First, we need to sign up with Rave and create a merchant account which enables us to receive payment for goods and services:

Alt Text

Once we are through with the sign-up process, we should see a dashboard similar to this:

On the dashboard navigate to Settings and then the API tab to retrieve the API keys. As this is a tutorial, switch to Test mode on the dashboard and make payment using a test card supplied by Rave to avoid disclosing sensitive information either by displaying our real API keys or credit card numbers. Below is a screenshot of your test API keys:

Alt Text

In the src/components folder, let's create a new component and name it RaveModal. Using Vue's create() hook, In our newly created component, we'll create an instance of Rave's inline script and append it to the DOM:


    // src/components/RaveModal.vue
    created() {
        const script = document.createElement("script");
        script.src = !this.isProduction
          ? "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/flwpbf-inline.js"
          : "https://api.ravepay.co/flwv3-pug/getpaidx/api/flwpbf-inline.js";
        document.getElementsByTagName("head")[0].appendChild(script);
      }

Enter fullscreen mode Exit fullscreen mode

Using Vue's method property, we'll embed a payment modal in our component via Rave's getPaidSetup function:


    // src/components/RaveModal.vue
     methods: {
        payWithRave() {
          window.getpaidSetup({
            customer_email: this.email,
            amount: this.amount,
            txref: this.reference,
            PBFPubKey: this.raveKey,
            onclose: () => this.close(),
            callback: response => this.callback(response),
            currency: this.currency,
            country: this.country,
            custom_title: this.custom_title,
            custom_logo: this.custom_logo,
            payment_method: this.payment_method,
          });
        }
      }

Enter fullscreen mode Exit fullscreen mode

Our next step will be to specify what each value in getPaidSetup should be. We'll do this by using Vue prop types:


    // src/components/RaveModal.vue 
    props: {
        isProduction: {
          type: Boolean,
          required: false,
          default: false //set to true if you are going live
        },
        email: {
          type: String,
          required: true
        },
        amount: {
          type: Number,
          required: true
        },
        raveKey: {
          type: String,
          required: true
        },
        callback: {
          type: Function,
          required: true,
          default: response => {}
        },
        close: {
          type: Function,
          required: true,
          default: () => {}
        },
        currency: {
          type: String,
          default: "NGN"
        },
        country: {
          type: String,
          default: "NG"
        },
        custom_title: {
          type: String,
          default: ""
        },
        custom_logo: {
          type: String,
          default: ""
        },
        payment_method: {
          type: String,
          default: ""
        }
      }

Enter fullscreen mode Exit fullscreen mode

The template in our RaveModal component won't hold much, just a button that activates the payment modal when clicked:


 // src/components/RaveModal.vue

<template>
  <div class="rave">
    <button class="button" @click="payWithRave">Book Now</button>
  </div>
</template>
Enter fullscreen mode Exit fullscreen mode

Finally, we'll import RaveModal into our App component and specify all the values of paywithRave() :


    // src/App.vue

    <script>
    import Rave from "./components/RaveModal.vue";
    export default {
      name: "app",
      components: {
        Rave
      },
     data() {
        return {
          carResponse: [],
          isProduction: false,
          raveKey: raveKey,
          email: "ugwuraphael@gmail.com",
          amount: "",
          currency: "NGN",
          country: "NG",
          custom: {
            title: "Car Shop",
            logo: carLogo
          },
          paymentMethod: ""
        };
      }
    }
    </script>

Enter fullscreen mode Exit fullscreen mode

To include the payment button on our app, embed it in our template:


// src/App.vue

<template>
  <v-card-actions class="justify-center">
              <rave
                :isProduction="isProduction"
                :email="email"
                :amount="car.price"
                :reference="reference"
                :rave-key="raveKey"
                :callback="callback"
                :close="close"
                :currency="currency"
                :country="country"
                :custom_title="custom.title"
                :custom_logo="custom.logo"
                :payment_method="paymentMethod"
              />
            </v-card-actions>
</template
Enter fullscreen mode Exit fullscreen mode

Finally, let's protect our API keys by storing them in a .env file. In the root folder of your project, create a .env file and store both MarketCheck and Rave APIs:


    // .env

    VUE_APP_CAR_API_KEY='YOUR MARKETCHECK API HERE'
    VUE_APP_RAVE_TEST_KEY='YOUR RAVE API KEY HERE'

Enter fullscreen mode Exit fullscreen mode

When you are done, save the .env file and refer to the stored values in App like this:


// src/App.vue
<script>
  const carKey = process.env.VUE_APP_CAR_API_KEY;
  const raveKey = process.env.VUE_APP_RAVE_TEST_KEY;
</script>
Enter fullscreen mode Exit fullscreen mode

Restart the development server on your terminal, navigate to your browser and try to make a payment for one of the vehicles:

Upon payment, Rave emails the customer a receipt:

Handling payment authentication

Although we can confirm that a customer made payment by checking our Rave dashboard for details of the transaction, it's still important to perform an authentication check for each transaction to detect issues such as reversed or fraudulent transactions. To achieve this, we'll define the callback property in Rave's getPaidSetup function to check for the authenticity of every transaction and return its transaction ID to the customer:


    <script>
      import Rave from "./components/RaveModal.vue";
      export default {
        name: "app",
        components: {
            Rave
        }
        methods: {
            callback: function(response) {
                if (
                    response.data.tx.status == "successful" &&
                    response.data.tx.chargeResponseCode === "00"
                ) {
                    if (response.data.tx.fraud_status === "ok") {
                        alert(
                            `Authenticate your payment via our mobile app with this code: ${response.data.tx.txRef}`
                        );
                    }
                } else {
                    alert("Your payment could not be processed. Please try again later");
                }
            }
        } 
    </script>

Enter fullscreen mode Exit fullscreen mode

Now a customer can pay for an item and get an identifier such as an authentication code as an additional layer of authenticity:

Summary

Optimizing performance when building web apps is only going to get more important. Javascript developers have a lot of frameworks and tools to choose from and Vue is an awesome option. As for implementing payment options seamlessly, Rave gets the job done. To check out the source code of this application, head on to GitHub.

Top comments (0)