Receiving payment is one of the most important part in any online commerce application. Payment Gateways help to achieve this. Razorpay is one of the most popular payment gateway solution in India and it is very easy to integrate into any application. With a minimum amount of javascript coding, you get a beautiful user interface to accept payments online. In this article on Razorpay Integration, I will show you how to integrate Razorpay with Vue js and Nuxt js application

Before you begin, please ensure you have

  • An active Razorpay account to accept payments.
  • A Vue.js or Nuxt.Js application installed on your local machine.
  • Server-side script. In this article, we are using express js with firebase functions.

That’s all ! You are good to go 🙂

Once your Razorpay account, Vue.js or Nuxt app and firebase are ready we need to use Razorpay checkout.js on your frontend and Razorpay npm packages on your backend.

  • Add the checkout.js library on your vue or nuxt app. If you are using Vue, you can simply add this script in the index.html file in the public directory. But, if your using Nuxt.js add the below code on your root nuxt.config.js.
head: {
   ---------------
    script: [
      { src: 'https://checkout.razorpay.com/v1/checkout.js' },
    ],
    --------------
  },
npm i razorpay express

Let’s start our Razorpay integration by adding the following code on our Vue or Nuxt component.

Add a pay button in our component template

<template>
  <button @click="pay">Pay</button>
</template>

Next step, we need to create an order in Razorpay while clicking the pay button. For creating order, we are using firebase functions. Write the following code on your index.js file in the functions directory.

const functions = require("firebase-functions");
const admin = require("firebase-admin");
const express = require("express");
const cors = require("cors");
const Razorpay = require("razorpay");
var crypto = require("crypto");

admin.initializeApp();
const router = express();
router.use(cors({ origin: true }));

// Add your razorpay key and secret
const KEY_ID = "";
const KEY_SECRET = "";

router.post("/createPayment", (req, res, next) => {
  return admin
    .firestore()
    .collection("payments")
    .add(req.body)
    .then(payment => {
      var instance = new Razorpay({
        key_id: KEY_ID,
        key_secret: KEY_SECRET
      });

      var options = {
        amount: req.body.amount * 100,
        currency: "INR",
        receipt: payment.id,
        payment_capture: 1
      };
      instance.orders.create(options, function(err, order) {
        return res.status(201).send(order);
      });
    })
    .catch(er => {
      return res.status(400).send({ er });
    });
});

In the above code, we are importing firebase-functions, firebase-admin, crypto and razorpay that we installed earlier. Update your Razorpay key id & key secret that you will get from your Razorpay dashboard. Then we are creating an order creation web service, we will use this web service when a user clicking the pay button on the client-side. In this web service first, we are saving all payment details to our firestore database collection. Once that data got saved in our database we are creating the order. For that, you need to pass a few parameters.

  • amount: Will get the amount from the client, covert that into paisa
  • currency: you are free to pass any currency code
  • receipt: It must be unique, we are using the payment collection document id for that.
  • payment_capture: Automatically capture the payment.

Once the order instance got created, passing the order details to the client-side.

Next step write your pay function in Vue component.

<script>
export default {
  methods: {
    async pay() {
      // Customer details and amount
      const data = {
        amount: 1000,
        name: "",
        email: "",
        phone: ""
      };

      this.$axios
        .post("Your Firebase function root url + /razorpay/createPayment", data)
        .then(res => {
          console.log(res);
          let order = res.data;
          var options = {
            key: "Add your razorpay key here",
            amount: order.amount_due,
            currency: order.currency,
            description: "Payment description",
            order_id: order.id,
            prefill: {
              name: data.name,
              email: data.email,
              contact: data.phone
            },
            theme: {
              color: "#000000" // Set your website theme color
            },
            handler: response => {
              this.verifySignature(response);
            }
          };

          var rzp = new Razorpay(options);
          rzp.open();
        })
        .catch(err => {
          console.error(err);
        });
    },
  }
};
</script>

In the pay method, we are passing the amount and user details to the createPayment web service that we created earlier. It will return the order details. For displaying the Razorpay payment form we need to pass a few parameters. That we are passing through options variable.

  • key: The api key you will get from Razorpay dashboard.
  • amount: You will get from order details
  • currency: You will get from order details
  • description: Pass your payment description this will show in your Razorpay dashboard
  • order_id: You will get from order details
  • prefill: Pass user name, email, and phone number. This will auto-fill in Razorpay payment form
  • theme: Pass a theme colour for your payment form
  • handler: This will call once the payment is completed. We will create the verifySignature method in the next step.

Next, we are creating verifySignature method in our component

async verifySignature(response) {
      this.$axios
        .post(
          "Your Firebase function root url + /razorpay/verifyPayment",
          response
        )
        .then(response => {
          console.log(response);
          alert("payment received");
        })
        .catch(err => {
          console.log(err);
        });
    }

Next, we are creating verifyPayment web service in firebase functions

router.post("/verifyPayment", (req, res, next) => {
  const order = req.body;

  const text = order.razorpay_order_id + "|" + order.razorpay_payment_id;
  var signature = crypto
    .createHmac("sha256", KEY_SECRET)
    .update(text)
    .digest("hex");

  if (signature === order.razorpay_signature) {
    // You can update payment details in your database here

    return res.status(201).send({ message: "Successful payment" });
  } else {
    return res.status(400).send({ message: "Payment verification failed" });
  }
});

In the above code, we are verifying the signature and then we pass the result to the client-side. We are using crypto.js to generate the hash. Here, you can also update the payment status in your database.

Finally create your firebase function with router

exports.razorpay = functions.https.onRequest(router);

So there! We have successfully integrated Razorpay in our Vue or Nuxt application. Razorpay Integration in any application is almost similar. You are free to use any server-side script or database. It is not mandatory that you need verifyPayment web service, you can confirm the payment in verifySignature function. But it is good to do this on the server side. Also, you can do this payment confirmation with Razorpay webhooks. Hope this helps you in integrating Razorpay into your application quickly and easily without any fuss.

You can find the complete code in the following git repositories.

Vue.js Git Repo

Nuxt.js Git Repo

If you have any suggestions or a better, secured method, please feel free to post in the Comment section below.

<> Happy Coding </>

42 Comments

Raj · August 29, 2020 at 6:46 am

I’m getting the Cors error,

origin ‘http://localhost:3000’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

How do I resolve this?

Thanks

Raj · August 29, 2020 at 7:34 am

Okay, I managed to resolve this issue by setting cors headers. but now, I’m getting the following error in console.

checkout-frame.js:1 wrong 0th argtype null
i @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
RD.n.$$.update @ checkout-frame.js:1
El @ checkout-frame.js:1
Pl @ checkout-frame.js:1
create:1 “serviceworker” must be a dictionary in your web app manifest.
checkout-frame.js:1 wrong 0th argtype null
i @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
RD.n.$$.update @ checkout-frame.js:1
El @ checkout-frame.js:1
Pl @ checkout-frame.js:1
checkout-frame.js:1 wrong 0th argtype null
i @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
RD.n.$$.update @ checkout-frame.js:1
El @ checkout-frame.js:1
Pl @ checkout-frame.js:1
Jl @ checkout-frame.js:1
t @ checkout-frame.js:1
render @ checkout-frame.js:1
switchTab @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
checkout-frame.js:1 wrong 0th argtype null
i @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
RD.n.$$.update @ checkout-frame.js:1
El @ checkout-frame.js:1
Pl @ checkout-frame.js:1
2api.razorpay.com/v1/payments/create/ajax:1 Failed to load resource: the server responded with a status of 400 (Bad Request)

    Nithin K Varrier · August 29, 2020 at 8:04 am

    Hi Raj,

    Please confirm your values in “instance” & “options” are correct
    admin.firestore().collection(‘payments’).add(req.body.data).then(payment => {

    var instance = new Razorpay({
    key_id: “key_id_from_razorpay”,
    key_secret:”key_secret_from_razorpay”
    })

    var options = {
    amount: req.body.data.amount * 100,
    currency: “INR”,
    receipt: payment.id,
    payment_capture: 1
    };
    instance.orders.create(options, function(err, order) {
    res.status(201).send({ data: order });
    });
    })

    Nithin K Varrier · August 29, 2020 at 8:05 am

    Also, check firebase functions log for any error

Raj · August 29, 2020 at 8:16 pm

Hi Nitin,

It worked. I’m still however seeing the following messages in my console. What do you recommend to do after successful payment? Can you add a section also to handle cases like failed payments? or configure webhooks? And what steps should I alter If I need to configure subscriptions instead of orders?

It was a wonderfully well-written post,

Thanks a lot,
Raj

> “serviceworker” must be a dictionary in your web app manifest.
> wrong 0th argtype null
i @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
RD.n.$$.update @ checkout-frame.js:1
Jl @ checkout-frame.js:1
t @ checkout-frame.js:1
setHomeTab @ checkout-frame.js:1
setSvelteComponents @ checkout-frame.js:1
render @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
callback @ checkout-frame.js:1
s.onreadystatechange @ checkout-frame.js:1
XMLHttpRequest.send (async)
(anonymous) @ VM55:1
call @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
setTimeout (async)
defer @ checkout-frame.js:1
ai @ checkout-frame.js:1
ai @ checkout-frame.js:1
getPrefs @ checkout-frame.js:1
(anonymous) @ checkout-frame.js:1
XT @ checkout-frame.js:1
t @ checkout-frame.js:1

    Nithin K Varrier · August 30, 2020 at 10:36 am

    In our Component pay() method we are calling our handler function verifySignature(). There you can do the client-side response. In this method, we are calling our firebase function verifyPayment(). In verifyPayment function we are doing the server-side validations, based on the result you can update your database.

    exports.verifyPayment = functions.https.onRequest((req, res) => {

    const order = req.body.data;

    const text = order.razorpay_order_id + "|" + order.razorpay_payment_id;
    var signature = crypto.createHmac("sha256", secret_key).update(text).digest("hex");

    if (signature === order.razorpay_signature) {

    // Add your successful payment database query here

    res.status(201).send({ data: { message:"Successfull Payment"} });
    }else {
    res.status(400).send({ data: { message:"Signature mismatch" } });
    }
    })

    Nithin K Varrier · August 30, 2020 at 10:41 am

    For subscription payment, it’s little different. Will write a post soon. Thank you.

Raj · September 22, 2020 at 3:36 pm

Hi how can execute the same code without server side rendering as I keep getting the cors error while using the orders API. Would be grateful if you could help

Shubhang Chourasia · December 17, 2020 at 3:18 pm

After everything when I click Pay button it gives me this error.

Access to fetch at ‘https://us-central1-my-project.cloudfunctions.net/createPayment’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

    Nithin K Varrier · December 17, 2020 at 8:40 pm

    Please confirm that you enabled cors in your functions and check any error coming on your firebase function log.

Shubhang Chourasia · December 17, 2020 at 10:13 pm

I don’t know what’s happening but when I write
const Razorpay = require(“razorpay”);
I am unable to deploy function. And when I comment this line the function gets deployed successfully.
Also I included
const cors = require(‘cors’)({ origin: true })
in my code. My sample code for function is as below.
//Function to Create Payment
exports.createPayment = functions.https.onRequest(async (req, res) => {
cors(req, res, () => {
admin
.firestore()
.collection(‘payments’)
.add(req.body.data)
.then((payment) => {
var instance = new Razorpay({
key_id: ‘rzp_test_my_key’,
key_secret: ‘my_secret_key’,
})

var options = {
amount: req.body.data.amount * 100,
currency: ‘INR’,
receipt: payment.id,
payment_capture: 1,
}
instance.orders.create(options, function (err, order) {
res.status(201).send({ data: order })
})
})
})
})

Shubhang Chourasia · December 17, 2020 at 10:27 pm

The firebase log gave me this. ——— createPayment ReferenceError: Razorpay is not defined

Shubhang Chourasia · December 18, 2020 at 11:05 am

I solved the deployment error.
I changed my directory to functions. And again did npm i razorpay. By doing this the razorpay dependency appeared in package.json folder in functions. And I was able to deploy function to firebase. Also, I have installed the cors and used it in my code. But after everything is correct in my code it still gave me cors error. Here is the error.
Error 1 –

Access to fetch at ‘https://us-central1-my-project-name.cloudfunctions.net/createPayment’ from origin ‘http://localhost:3000’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

Error 2 –

POST https://us-central1-my-project-name.cloudfunctions.net/createPayment net::ERR_FAILED

Any help please ?

    Nithin K Varrier · December 18, 2020 at 12:11 pm

    There may be two reasons for the access control error. 1) Some errors are still in functions log 2) May be u need to change firebase billing plan to Pay as you go

Shubhang Chourasia · December 18, 2020 at 4:22 pm

CORS error is solved. I changed functions.https.onRequest to functions.https.onCall.
The only error which remains is this.
POST https://us-central1-my-project-name.cloudfunctions.net/createPayment 500.

Firebase functions shows this .
Unhandled error TypeError: Cannot read property ‘origin’ of undefined
at /workspace/node_modules/cors/lib/index.js:219:40
at optionsCallback (/workspace/node_modules/cors/lib/index.js:199:9)
at corsMiddleware (/workspace/node_modules/cors/lib/index.js:204:7)
at /workspace/index.js:18:3
at func (/workspace/node_modules/firebase-functions/lib/providers/https.js:273:32)
at processTicksAndRejections (internal/process/task_queues.js:97:5)

Function execution took 693 ms, finished with status code: 500

I have a doubt that this may be due to
const cors = require(‘cors’)({ origin: true })

    Nithin K Varrier · December 18, 2020 at 4:25 pm

    Yea thinks so. Actually, it is working fine for me without cors.

Shubhang Chourasia · December 18, 2020 at 5:23 pm

Removing ({ origin: true }) from const cors = require(‘cors’)({ origin: true }) helped me. It started working.
The only problem which remains is that, on doing
createPayment(data)
.then((res) => {
let order = res.data
console.log(‘Order’, order)
I get null value.
I even entered other values as a string. But the order id has to be passed by order.id only. Order id cannot be entered on our own.

    Nithin K Varrier · December 18, 2020 at 5:30 pm

    Could you please share your functions code. I think the function returns value before generating the order.

Shubhang Chourasia · December 18, 2020 at 5:44 pm

Yes, even I think the same that function returns value before generating the order. Here is my methods code. Also, I entered order id randomly. So it gave me this in console.
Cannot read property ‘name’ of undefined
I think it is also not taking values in prefix by writing this.data.name.
I hope that problem gets solves quickly.

methods: {
async pay() {
const data = {
amount: 1000,
name: ‘Name’,
email: ‘Email’,
phone: ‘Phone’,
status: ‘pending’,
}
var createPayment = firebase.functions().httpsCallable(‘createPayment’)
createPayment(data)
.then((res) => {
let order = res.data
console.log(‘Order’, order)
var options = {
key: ‘rzp_test_key’, // I entered my actual key here.
amount: order.amount_due,
currency: order.currency,
description: ‘Payment description ‘,
order_id: order.id,
prefill: {
name: this.data.name,
email: this.data.email,
contact: this.data.phone,
},
theme: {
color: ‘#ffcc00’,
},
handler: (response) => {
this.verifySignature(response)
},
}
console.log(‘Pay’)
var rzp = new Razorpay(options)
rzp.open()
})
.catch((error) => {
console.log(error)
})
},
async verifySignature(response) {
const verifyPayment = firebase.functions().httpsCallable(‘verifyPayment’)
verifyPayment(response)
.then((res) => {
alert(‘payment received’)
})
.catch((err) => {
console.log(‘error’)
})
},

},

    Nithin K Varrier · December 18, 2020 at 5:49 pm

    I was asking the firebase functions code ( createPayment() )

    Nithin K Varrier · December 18, 2020 at 5:51 pm

    You don’t need to use this, because the data is defined in the same method

Shubhang Chourasia · December 18, 2020 at 5:53 pm

Oh, here it is.
I changed functions.https.onRequest to functions.https.onCall. Because onRequest was not working.

exports.createPayment = functions.https.onCall(async (req, res) => {
cors(req, res, () => {
admin
.firestore()
.collection(‘payments’).doc(‘pay1’)
.add(req.body.data)
.then((payment) => {
var instance = new Razorpay({
key_id: ‘rzp_test_key’, //I entered my key
key_secret: ‘rzp_secret_key’, //I entered my key
})

var options = {
amount: req.body.data.amount * 100,
currency: ‘INR’,
receipt: payment.id,
payment_capture: 1,
}
instance.orders.create(options, function (err, order) {
res.status(201).send({ data: order })
})
})
})
})

    Nithin K Varrier · December 18, 2020 at 5:55 pm

    Try to make these changes


    exports.createPayment = functions.https.onCall(async (req, res) => {
    cors(req, res, () => {
    return admin
    .firestore()
    .collection(‘payments’).doc(‘pay1’)
    .add(req.body.data)
    .then((payment) => {
    var instance = new Razorpay({
    key_id: ‘rzp_test_key’, //I entered my key
    key_secret: ‘rzp_secret_key’, //I entered my key
    })

    var options = {
    amount: req.body.data.amount * 100,
    currency: ‘INR’,
    receipt: payment.id,
    payment_capture: 1,
    }
    return instance.orders.create(options, function (err, order) {
    return { data: order }
    })
    })
    })
    })

Shubhang Chourasia · December 18, 2020 at 6:10 pm

console.log(‘Order’, order) is still showing null. The function runs before the order is created.

Shubhang Chourasia · December 18, 2020 at 6:25 pm

console.log(‘Res’, res)
It shows in console.

Res {data: null}

I console logged this before order and it was showing null.

    Nithin K Varrier · December 18, 2020 at 6:31 pm

    Sorry brother. I tried in my machine it was working fine. You need to debug more in your createPayment function. Try to do more console there.

Shubhang Chourasia · December 18, 2020 at 6:34 pm

Everything works fine when I enter data manually. Even I created an order in Razorpay dashboard and entered the order id as a string. The razorpay worked fine. even I was able to pay in test mode.
The problem here is that the res is returning null data. Therefore, everything stops working.

Shubhang Chourasia · December 21, 2020 at 9:40 am

The actual problem is here. When I run this. It gives me error “No key passed”.
pay(){
var instance = new Razorpay({
key_id: ‘YOUR_KEY_ID’, //I added my live mode key_id here.
key_secret: ‘YOUR_KEY_SECRET’ ////I added my live mode key_secret here.
})
console.log(instance)
}

Shubhang Chourasia · December 21, 2020 at 4:03 pm

It is working only in test mode that too without order id. But I cannot generate orders as the above code is not working. The keys are valid. I even tested everything using Postman.
I don’t know what is the solution. If the initial code is giving error as no key passed. I cannot proceed further.

Shubhang Chourasia · December 21, 2020 at 4:48 pm

var instance = new Razorpay({
key_id: ‘YOUR_KEY_ID’, //I added my live mode key_id here.
key_secret: ‘YOUR_KEY_SECRET’ ////I added my live mode key_secret here.
})
Somehow I managed to run this. But new error at each step.
error TypeError: instance.orders.create is not a function
Still trying to create order.
Everything is not working in one step. It needs a lot of debugging.

    Vishal · December 31, 2020 at 5:50 am

    hey Shubhang, great to hear that you able to make successful payment integration! Will you pls also share/expose the repo with dummy data would be a great help to the community !!

Shubhang Chourasia · December 23, 2020 at 12:16 pm

Hello Sir, I just want to inform you that I finally solved all the problems there in my code. It was not working because I was using res and req with .onCall but it only works with onRequest (I think it also needs express to work). With .onCall we have to use (data,ctx) to access data passed from the frontend.
Finally, everything is working fine in both live and test mode. I was able to do payment with real money.
The error
“serviceworker” must be a dictionary in your web app manifest.
remains, but it does not impact the transaction as of now. I will figure out later the possible solution.
But Thank You So Much for your time and assistance at every step. I could not have started the implementation if the code is not available here for Nuxt.
Thank You.

    Nithin K Varrier · March 1, 2021 at 8:08 am

    HI Shubhang,

    Great to know your code got worked 🙂 . Sorry I got busy with my personal stuff. It’s good to use express in firebase functions.

Ahsan · January 1, 2021 at 4:42 am

Thanks for the post. I’m following the steps, and successfully able to generate the orderId, however, when passing options to Razorpay object rzp getting on const rzp = new Razorpay(options);
throw new Error(‘`key_id` is mandatory’);
I’m don’t get why it’s expecting key_id, even the official Razorpay doc says options object has only a parameter called a key.
const options = {
key: ‘rzp_test_WLq8wudfdfdqKL1uJSS’,
amount: order.amount_due,
currency: order.currency,
description: ‘Payment description’,
order_id: order.id,
prefill: {
name: data.name,
email: data.email,
contact: data.phone,
},

Really appreciate if you can help here.

Ahsan Mumtaz · January 2, 2021 at 3:47 am

Hey, just wanted to give a shout, got it working, I was messing with the serverside and client-side Razorpay object, after calling from the checkout.js it works. thank you, guys, for putting it all togther.

MOZU · January 15, 2021 at 5:10 pm

Hi,
I am facing the “serviceworker” must be a dictionary in your web app manifest. issue with frontend , did you get any solution for this issue.
Please hep to resolve this issue.

Suresh Subramanian · February 17, 2021 at 12:58 pm

Thank you Nithin,
It was clearly explained and I could able to add the RazorPay in a breeze. Appreciated !!

Leave a Reply

Your email address will not be published. Required fields are marked *