
Stripe Checkout With Next.js - The Complete Guide
Hello world
My name is Ashik Chapagain.
- A full-stack web developer
- Content Creator
- College student
- Nepalese
I'll walk you through the entire process of usingStripe Checkout withNext.js in this article.
Index
Introduction
Let's see the brief introduction ofStripe andStripe Checkout.
Stripe:Stripe is an online payment processing and credit card processing platform for businesses.
Stripe Checkout:Stripe Checkout is prebuilt, hosted payment page optimized for conversion.
Demo
Live Preview:https://stripe-checkout-next-js-demo.vercel.app/
Github:https://github.com/projectashik/stripe-checkout-next-js-demo
Let's Build
Installation
As mentioned in the title, we'll be usingNext.js for building this.
ForNext.js to work, we need to haveNode.js
andnpm
installed.
So, first, installNode.js if you haven't yet.
You can follow the following guides to installNode.js.
Optional:
If you want to useyarn
, install it usingnpm
.
npminstall--global yarn
Now, create a new Next.js app.
npx create-next-app stripe-checkoutyarn create next-app stripe-checkout
I have usedtailwindcss for styling the components. So, let's installtailwindcss too.
yarn add--dev tailwindcss@latest postcss@latest autoprefixer@latestnpminstall-D tailwindcss@latest postcss@latest autoprefixer@latest
Then run the following command to create tailwindcss config file and postcss config file.
npx tailwindcss init -p
Now open the project in your favorite text editor or IDE (Integrated Development Environment).
For me, it'sVisual Studio Code .
# Run the following command to open the project in VS Code.code.
Now, opentailwind.config.js
and updatepurge
option and addmode: 'jit'
.
// tailwind.config.jsmodule.exports = { purge: ['./pages/**/*.{js,ts,jsx,tsx}', './components/**/*.{js,ts,jsx,tsx}'], mode: 'jit', ...};
Then, openstyles/globals.css
and remove all the content and add the following content.
@tailwindbase;@tailwindutilities;@tailwindcomponents;
*Short Method: *
If you want to use the short process.
Just run the following command.
npx create-next-app-e with-tailwindcss stripe-checkoutyarn create next-app-e with-tailwindcss stripe-checkout
Now, start the dev server and start coding.
yarn devnpm run dev
Cleaning
Remove themain
andfooter
tags frompages/index.js
.
If you want to edit thetitle
anddescription
in the<Head>
tag. Then you can.
// pages/index.jsimportHeadfrom'next/head';importImagefrom'next/image';importstylesfrom'../styles/Home.module.css';exportdefaultfunctionHome(){return(<divclassName={styles.container}><Head><title>StripeCheckoutwithNext.js</title><metaname='description'content='Complete Step By Step Tutorial for integrating Stripe Checkout with Next.js'/><linkrel='icon'href='/favicon.ico'/></Head></div>);}
And remove all the styles except for.container
instyles/Home.module.css
.
/* styles/Home.module.css */.container{min-height:100vh;padding:00.5rem;display:flex;flex-direction:column;justify-content:center;align-items:center;height:100vh;}
Making UI
First, we'll build an index page in which we'll have one item that users can purchase.
Let's add details of an item to an object.
// pages/index.jsconst[item,setItem]=useState({name:'Apple AirPods',description:'Latest Apple AirPods.',image:'https://images.unsplash.com/photo-1572569511254-d8f925fe2cbb?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1400&q=80',quantity:0,price:999,});
Don't forget to import theuseState
hook.
import{useState}from'react';
Since we're using Unsplash for images, so we need to configureimages.unsplash.com
under theimages
section innext.config.js
.
module.exports={reactStrictMode:true,images:{domains:['images.unsplash.com'],},};
Now, let's display the following details in the UI and add buttons to increase and decrease the quantity users want to purchase.
In,pages/index.js
...<main><divclassName='shadow-lg border rounded p-2 '><Imagesrc={item.image}width={300}height={150}alt={item.name}/><h2className='text-2xl'>$ {item.price}</h2><h3className='text-xl'>{item.name}</h3><pclassName='text-gray-500'>{item.description}</p><pclassName='text-sm text-gray-600 mt-1'>Quantity:</p><divclassName='border rounded'><buttonclassName='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'> -</button><inputtype='number'className='p-2'defaultValue={item.quantity}/><buttonclassName='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'> +</button></div><p>Total: ${item.quantity * item.price}</p><buttondisabled={item.quantity===0}className='bg-blue-500 hover:bg-blue-600 text-white block w-full py-2 rounded mt-2 disabled:cursor-not-allowed disabled:bg-blue-100'> Buy</button></div></main>...
Now your page should look like this.
Now, let's make the button to increase and decrease the quantity working.
Now, we'll create theonQuantityPlus
andonQuantityMinus
functions to increase and decrease the item quantity on button clicks.
constchangeQuantity=(value:number)=>{// Don't allow the quantity less than 0, if the quantity is greater than value entered by user then the user entered quantity is used, else 0setItem({...item,quantity:Math.max(0,value)});};constonQuantityPlus=()=>{changeQuantity(item.quantity+1);};constonQuantityMinus=()=>{changeQuantity(item.quantity-1);};
Now, add this function in the onClick event of the respective buttons.
...<buttononClick={onQuantityMinus}className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'> -</button>...<buttononClick={onQuantityPlus}className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'> +</button>...
Now, let's add theonChange
event on the input field to update the value of the item quantity and also don't forget to changedefaultValue
tovalue
.
...<inputtype='number'className='p-2'onChange={onInputChange}value={item.quantity}/>...
Now, let's createonInputChange
function.
...//constonInputChange=(e)=>{changeQuantity(parseInt(e.target.value));};...
Creating the Backend
First, we'll start by installing thestripe
library.
yarn add stripenpm install stripe
Now, let's add the requiredenvironment variables
.
Create a new file.env.local
in the root directory with the following data.
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=YOUR_STRIPE_PUBLISHABLE_KEYSTRIPE_SECRET_KEY=YOUR_STRIPE_SECRET_KEY
You can get these credentials fromDashboard -> Developers -> API Keys
.
Now, we need to build an API to get the session id that is required for redirecting the user to the checkout page.
Create a new file inapi/create-stripe-session.js
. And add the following.
conststripe=require("stripe")(process.env.STRIPE_SECRET_KEY);exportdefaultasync(req,res)=>{const{item}=req.body;};
Here, we are getting item details via a post method that we will call on the front end.
Creating the shape for the item needed by Stripe.
There is a particular type of object which Stripe expects to get, this is the object. You should use your local currency instead of "usd" if you want.
consttransformedItem={price_data:{currency:'usd',product_data:{images:[item.image],name:item.name,},unit_amount:item.price*100,},description:item.description,quantity:item.quantity,};
*Creating Stripe Session in the backend: *
You will need to create a stripe session object where you need to define some data.
constsession=awaitstripe.checkout.sessions.create({payment_method_types:['card'],line_items:[transformedItem],mode:'payment',success_url:redirectURL+'?status=success',cancel_url:redirectURL+'?status=cancel',metadata:{images:item.image,},});res.json({id:session.id});
payment_method_type
: In this, we add the payment methods to pay the price of the product.Click here to know more payment methods.success_url
: Insuccess_url, you define where the user will go after the payment is successful.cancel_url
: In thecancel_url, you define where the user will go if the user clicks the back button. It can be a cancel page or the checkout page as well.metadata
: Inmetadata, we will add images of the product, if you want you can add other options too.
For other options,click here to know.
Now, the finalcreate-stripe-session.js
file should look like this.
conststripe=require('stripe')(process.env.STRIPE_SECRET_KEY);asyncfunctionCreateStripeSession(req,res){const{item}=req.body;constredirectURL=process.env.NODE_ENV==='development'?'http://localhost:3000':'https://stripe-checkout-next-js-demo.vercel.app';consttransformedItem={price_data:{currency:'usd',product_data:{images:[item.image],name:item.name,},unit_amount:item.price*100,},description:item.description,quantity:item.quantity,};constsession=awaitstripe.checkout.sessions.create({payment_method_types:['card'],line_items:[transformedItem],mode:'payment',success_url:redirectURL+'?status=success',cancel_url:redirectURL+'?status=cancel',metadata:{images:item.image,},});res.json({id:session.id});}exportdefaultCreateStripeSession;
Now, our backend is ready, now we have to send a POST request to API to get the session.
Redirecting to Stripe Checkout Page
For redirecting to Stripe Checkout Page. We have to install the following libraries.
yarn add @stripe/stripe-js axiosnpm install @stripe/stripe-js axios
First, we'll first create astripePromise
variable.
constpublishableKey=process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;conststripePromise=loadStripe(publishableKey);
Now, we'll createcreateCheckoutSession
function to get the Stripe Session for the checkout.
constcreateCheckOutSession=async()=>{conststripe=awaitstripePromise;constcheckoutSession=awaitaxios.post('/api/create-stripe-session',{item:item,});constresult=awaitstripe.redirectToCheckout({sessionId:checkoutSession.data.id,});if(result.error){alert(result.error.message);}};
Don't forget to importloadStripe
andaxios
.
import{loadStripe}from'@stripe/stripe-js';importaxiosfrom'axios';
Now, we have to call this function while the user clicks theBuy
button.
AndonClick={createCheckoutSession}
<buttondisabled={item.quantity===0}onClick={createCheckOutSession}className='bg-blue-500 hover:bg-blue-600 text-white block w-full py-2 rounded mt-2 disabled:cursor-not-allowed disabled:bg-blue-100'> Buy</button>
Now, let's test the checkout.
Since we have updatednext.config.js
and added the.env.local
file, so restart the dev server.
yarn devnpm run dev
Now, the result should look like this.
Displaying Success and Cancel Message
If you guys remember, then we have used?status=
query parameter insuccess_url
andcancel_url
.
// pages/api/create-stripe-session.js...const session = await stripe.checkout.sessions.create({ ... success_url: redirectURL + '?status=success', cancel_url: redirectURL + '?status=cancel', ... });...
So, with this query parameter, we'll know, whether the payment is successful or canceled. And show the respective message.
First, let's fetch the status.
// pages/index.jsimport { useRouter } from 'next/router';...const router = useRouter();const { status } = router.query;
Now, add the following below the starting main tag, to display the message.
// pages/index.js{status && status === 'success' && ( <div className='bg-green-100 text-green-700 p-2 rounded border mb-2 border-green-700'> Payment Successful </div>)}{status && status === 'cancel' && ( <div className='bg-red-100 text-red-700 p-2 rounded border mb-2 border-red-700'> Payment Unsuccessful </div>)}
Now, the result should be like this.
Our app took some time to assign a stripe session. So, during that time, let's displayProcessing...
instead ofBuy
inside the button as shown in the demo.
For that,
Create a new state named loading with default valuefalse
.
const [loading, setLoading] = useState(false);
Then, updateloading
value totrue
, while creating Stripe Session. and again updateloading
value tofalse
when the stripe session is created.
const createCheckOutSession = async () => { setLoading(true); ... setLoading(false);};
And now, update theBuy
Button.
<button disabled={item.quantity === 0 || loading} onClick={createCheckOutSession} className='bg-blue-500 hover:bg-blue-600 text-white block w-full py-2 rounded mt-2 disabled:cursor-not-allowed disabled:bg-blue-100'> {loading ? 'Processing...' : 'Buy'}</button>
Here, we're disabling the button while loading and displayingProcessing...
while loading.
Now, the result should be following:
All done.
Files
At last, yourpages/index.js
file should look like below:
importHeadfrom'next/head';importImagefrom'next/image';importstylesfrom'../styles/Home.module.css';import{useState}from'react';import{loadStripe}from'@stripe/stripe-js';importaxiosfrom'axios';import{useRouter}from'next/router';exportdefaultfunctionHome(){constrouter=useRouter();const{status}=router.query;const[loading,setLoading]=useState(false);const[item,setItem]=useState({name:'Apple AirPods',description:'Latest Apple AirPods.',image:'https://images.unsplash.com/photo-1572569511254-d8f925fe2cbb?ixid=MnwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8&ixlib=rb-1.2.1&auto=format&fit=crop&w=1400&q=80',quantity:0,price:999,});constchangeQuantity=(value)=>{// Don't allow the quantity less than 0, if the quantity is greater than value entered by user then the user entered quantity is used, else 0setItem({...item,quantity:Math.max(0,value)});};constonInputChange=(e)=>{changeQuantity(parseInt(e.target.value));};constonQuantityPlus=()=>{changeQuantity(item.quantity+1);};constonQuantityMinus=()=>{changeQuantity(item.quantity-1);};constpublishableKey=process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY;conststripePromise=loadStripe(publishableKey);constcreateCheckOutSession=async()=>{setLoading(true);conststripe=awaitstripePromise;constcheckoutSession=awaitaxios.post('/api/create-stripe-session',{item:item,});constresult=awaitstripe.redirectToCheckout({sessionId:checkoutSession.data.id,});if(result.error){alert(result.error.message);}setLoading(false);};return(<divclassName={styles.container}><Head><title>Stripe Checkout with Next.js</title><metaname='description'content='Complete Step By Step Tutorial for integrating Stripe Checkout with Next.js'/><linkrel='icon'href='/favicon.ico'/></Head><main>{status&&status==='success'&&(<divclassName='bg-green-100 text-green-700 p-2 rounded border mb-2 border-green-700'> Payment Successful</div>)}{status&&status==='cancel'&&(<divclassName='bg-red-100 text-red-700 p-2 rounded border mb-2 border-red-700'> Payment Unsuccessful</div>)}<divclassName='shadow-lg border rounded p-2 '><Imagesrc={item.image}width={300}height={150}alt={item.name}/><h2className='text-2xl'>${item.price}</h2><h3className='text-xl'>{item.name}</h3><pclassName='text-gray-500'>{item.description}</p><pclassName='text-sm text-gray-600 mt-1'>Quantity:</p><divclassName='border rounded'><buttononClick={onQuantityMinus}className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'> -</button><inputtype='number'className='p-2'onChange={onInputChange}value={item.quantity}/><buttononClick={onQuantityPlus}className='bg-blue-500 py-2 px-4 text-white rounded hover:bg-blue-600'> +</button></div><p>Total: ${item.quantity*item.price}</p><buttondisabled={item.quantity===0||loading}onClick={createCheckOutSession}className='bg-blue-500 hover:bg-blue-600 text-white block w-full py-2 rounded mt-2 disabled:cursor-not-allowed disabled:bg-blue-100'>{loading?'Processing...':'Buy'}</button></div></main></div>);}
Andpages/api/create-stripe-sessoin.js
should look like this.
conststripe=require('stripe')(process.env.STRIPE_SECRET_KEY);
asyncfunctionCreateStripeSession(req,res){
const{item}=req.body;
constredirectURL=
process.env.NODE_ENV==='development'
?'http://localhost:3000'
:'https://stripe-checkout-next-js-demo.vercel.app';
consttransformedItem={
price_data:{
currency:'usd',
product_data:{
images:[item.image],
name:item.name,
},
unit_amount:item.price*100,
},
description:item.description,
quantity:item.quantity,
};
constsession=awaitstripe.checkout.sessions.create({
payment_method_types:['card'],
line_items:[transformedItem],
mode:'payment',
success_url:redirectURL+'?status=success',
cancel_url:redirectURL+'?status=cancel',
metadata:{
images:item.image,
},
});
res.json({id:session.id});
}
exportdefaultCreateStripeSession;
Conclusion
So, we have came to end. Hope you like this article. Please consider giving feedback on my writing.
Important Links
Credits
- Fireship Stripe Course
- @Avneesh Agarwal Next.js Payment Article -https://blog.avneesh.tech/payments-in-next
Connect with me:
Top comments(2)
For further actions, you may consider blocking this person and/orreporting abuse