Accepting payments with Stripe, Nuxt. js and vercel

fayaz

Fayaz Ahmed

Posted on April 21, 2021

Accepting payments with Stripe, Nuxt. js and vercel

It's been a long time since my last post and I wanted to write a small article on how to accept payments with Stripe, as I was integrating Stripe into my SaaS project, which I am currently building.

Accepting payments is not that difficult and you don't even need a server.

I will be building this app with Nuxt.js, Tailwindcss and host it on vercel.

TLDR; the code and the live demo can be found at the bottom of this post

The site I have made is not complete and not responsive, but if someone wants to raise a PR and get it working, please go ahead.

  1. Scaffold a new Nuxt project with yarn create nuxt-app stripe-nuxt and you can select a CSS framework of your choice, I chose Tailwindcss, choose axios and I have also used nuxt-content for this, for storing the products database.

Clear the index.vue page and remove styles from default.vue files.

Add this markup and the script in index.vue, this will show a grid of products on the home page.

<template>
  <main class="min-h-screen">
    <section class="p-8 max-w-4xl mx-auto">
      <div class="grid grid-cols-1 lg:grid-cols-3 xl:grid-cols-3 gap-6">
        <nuxt-link
          :to="product.slug"
          class="overflow-hidden text-center"
          v-for="(product, p) in products"
          :key="p"
        >
          <img :src="product.images[0]" alt="product.name" class="mb-4" />
          <p class="font-semibold text-gray-700 mb-1">
            {{ product.name }}
          </p>
          <p class="text-sm">$ {{ product.amount }}</p>
        </nuxt-link>
      </div>
    </section>
  </main>
</template>

<script>
export default {
  transition: "fade",
  async asyncData({ $content }) {
    const products = await $content("products").fetch();
    return { products };
  },
};
</script>
Enter fullscreen mode Exit fullscreen mode

The above code will be rendered and look something like this.
Alt Text

Make a new file and name it _slug.vue in the same directory as index.vue, this will act as our product page and fill it with the below code.

<template>
   <main>
      <div class="flex">
         <div class="w-1/2 h-screen flex items-center justify-center">
            <img :src="product.images[0]" :alt="product.name" />
         </div>
         <div
            class="w-1/2 h-screen text-white flex items-center justify-center p-8 relative"
            :style="{ backgroundColor: `#${product.color.hex}` }"
            >
            <nuxt-link
               to="/"
               class="flex items-center space-x-2 absolute top-8 left-8"
               >
               <svg
                  class="w-5 h-5"
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
                  xmlns="http://www.w3.org/2000/svg"
                  >
                  <path
                     stroke-linecap="round"
                     stroke-linejoin="round"
                     stroke-width="2"
                     d="M7 16l-4-4m0 0l4-4m-4 4h18"
                     ></path>
               </svg>
               <p>Home</p>
            </nuxt-link>
            <div class="space-y-4">
               <p class="text-2xl font-bold">{{ product.name }}</p>
               <p>$ {{ product.amount }}</p>
               <p class="text-gray-100 text-sm">{{ product.description }}</p>
               <button
                  @click="buy()"
                  class="w-full py-3 bg-white text-gray-800 font-semibold flex items-center justify-center space-x-2"
                  :class="{ 'opacity-50 cursor-not-allowed': loading }"
                  >
                  <btn-loader v-if="loading" />
                  <p>Buy Now</p>
               </button>
            </div>
         </div>
      </div>
   </main>
</template>
<script>
   export default {
     transition: "fade",
     async asyncData({ $content, params }) {
       const product = await $content("products", params.slug).fetch();
       return { product };
     },
     data() {
       return {
         stripe: null,
         loading: false,
       };
     },
     methods: {
       async buy() {
         try {
           this.loading = true;
           const { data } = await this.$axios.post("/api/checkout", {
             order: {
               name: this.product.name,
               description: this.product.description,
               images: this.product.images,
               amount: this.product.amount * 100,
               currency: this.product.currency,
               quantity: 1,
             },
             slug: this.$route.params.slug,
           });
           this.stripe.redirectToCheckout({ sessionId: data.id });
         } catch (err) {
           alert(err);
           this.loading = false;
         }
       },
     },
     mounted() {
       this.stripe = Stripe("pk_test_ZaFKDdkCzVR4hCmDsUKWodm200fZIzrcmf");
     },
   };
</script>
Enter fullscreen mode Exit fullscreen mode

This will make a page looking like this, not very fancy, but looks good (not responsive).

Alt Text

We need to add the stripe checkout script in the nuxt.config.js file, add this in the head object.

script: [{src: "https://js.stripe.com/v3/"}]
Enter fullscreen mode Exit fullscreen mode

Let's focus on the script and see what's going on.
Alt Text

  1. Create an empty stripe object, this is where we will initialize the stripe object.

  2. Now pass the stripe public key to the Stripe method(the one we added in our head tag), you can get your public key from stripe dashboard

  3. Let's make a checkout API and use Vercels serverless functions. Any js file we add under a folder called api will act as a serverless function in Vercel, pretty cool right. So, I made one called checkout.js and wrote a small script.

const stripe = require("stripe")(process.env.STRIPE_TEST_SK);
const hostUrl = "http://localhost:3000";

export default async (req, res) => {
  const session = await stripe.checkout.sessions.create({
    payment_method_types: ["card"],
    line_items: [req.body.order],
    success_url: `${hostUrl}/${req.body.slug}?session_id={CHECKOUT_SESSION_ID}`,
    cancel_url: `${hostUrl}/${req.body.slug}?failed=true`
  });

  return res.status(200).json(session);
};

Enter fullscreen mode Exit fullscreen mode

You need to install the stripe package and import it and this is all you need to create a checkout session (the secret key can be found in the stripe dashboard).
The success URL and the cancel URL as the name suggest, tell stripe where to redirect respectively.

  1. Now that we have received a session id, just pass it the stripe redirect method
this.stripe.redirectToCheckout({ sessionId: data.id });
Enter fullscreen mode Exit fullscreen mode

Here's the code and here's the live demo.

If you like my work and want to get updates, please subscribe to my newsletter or if you'd like to buy me some coffee, you can donate here, we could have a online session over coffee.

💖 💪 🙅 🚩
fayaz
Fayaz Ahmed

Posted on April 21, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related