Accepting payments with Stripe, Nuxt. js and vercel
Tags: webdev, javascript, programming, vue
Published: 2021-04-21 05:52:35

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.
```html
<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>
```

The above code will be rendered and look something like this.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tjme19qzngi5qwxwx94z.jpg)

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.
```HTML
<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>
```
This will make a page looking like this, not very fancy, but looks good (not responsive).

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/xy6hq9zbojgnmg8r03yy.jpg)

We need to add the stripe checkout script in the `nuxt.config.js` file, add this in the head object.
```javascript
script: [{src: "https://js.stripe.com/v3/"}]
```

Let's focus on the script and see what's going on.
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nwnqq12ts7h0rebz0pbh.png)

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](https://dashboard.stripe.com/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.
```javascript
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);
};

```
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.

4. Now that we have received a session id, just pass it the stripe redirect method
```javascript
this.stripe.redirectToCheckout({ sessionId: data.id });
```

Here's the [code](https://github.com/fayazara/nuxt-stripe) and here's the live [demo](https://nuxt-stripe-amber.vercel.app/).

If you like my work and want to get updates, please subscribe to my [newsletter](https://www.getrevue.co/profile/fayaz) or if you'd like to buy me some coffee, you can donate [here](https://fayazz.co/coffee), we could have a online session over coffee.