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.
- 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>
The above code will be rendered and look something like this.
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>
This will make a page looking like this, not very fancy, but looks good (not responsive).
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/"}]
Let's focus on the script and see what's going on.
Create an empty stripe object, this is where we will initialize the stripe object.
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
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);
};
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.
- Now that we have received a session id, just pass it the stripe redirect method
this.stripe.redirectToCheckout({ sessionId: data.id });
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.
Top comments (9)
Not working for me.
Says 'Stripe is undefined'
And if I do
import Stripe from 'stripe'
it fires an alert: TypeError: _this4.stripe.redirectToCheckout is not a function.Then I cloned the repo, tried to run it and upon
npm run dev
I got an error:Then I tried
npm run generate
so I could runnpm run start
, and I had the same error. I guess your tutorial is way outta date.// eslint-disable-next-line no-undef
stripe.value = Stripe(
'pk_test_ZaFKDdkCzVR4hCmDsUKWodm200fZIzrcmf'
)
OR
check you nuxt.config.js file whether the script is inside the head.
I had the same problem. but I fixed it through my above solution.
Hey there, looks like a race condition, your mounted function is running before the script in head is loaded or it hasn't been called.
Care to show the code?
awesome!
Awesome article Fayaz
Thanks
Great article, thanks for posting.
You're welcome Ali
Amazing work Fayaz. Well written.