NextJS Templates
Invoicing App Tutorial with Next.js, Vercel & Back4app
26 min
in this tutorial, we will build a complete invoicing app using next js as the frontend, back4app as the backend, and additional services like clerk for authentication, stripe for payments, and resend for email notifications this guide will walk through setting up authentication, invoice management, payment processing, and deployment on vercel 1\ prerequisites before starting, ensure you have the following a back4app account ( sign up here https //www back4app com/ ) a clerk account ( clerk io https //clerk dev/ ) for authentication a stripe account ( stripe com https //stripe com/ ) for payment processing a resend account ( resend io https //resend io/ ) for email notifications node js and npm installed ( node js installation https //nodejs org/ ) 2\ setting up the backend (back4app) 2 1 create a new project in back4app log in to back4app and create a new project navigate to the data browser and create the following classes 2 2 data models (classes) 2 2 1 user (automatically handled by clerk) objectid username email 2 2 2 organization create a class called organization add the following columns name (string) the name of the organization ownerid (pointer< user>) points to the user class (created automatically by clerk) members (array\<pointer< user>>) an array of users who belong to this organization 2 2 3 customer create a class called customer add these columns name (string) customer's name email (string) customer's email organizationid (pointer) organization to which this customer belongs 2 2 4 invoice create a class called invoice add these columns invoicenumber (string) unique invoice identifier customerid (pointer) links to the customer organizationid (pointer) links to the organization amount (number) total amount for the invoice status (string) options include 'draft', 'sent', 'paid', and 'overdue' duedate (date) the invoice due date items (array) the list of items in the invoice 2 2 5 payment create a class called payment add these columns invoiceid (pointer) links to the related invoice amount (number) payment amount stripesessionid (string) the id of the stripe session status (string) payment status (e g , 'pending', 'successful', 'failed') 3\ setting up the frontend (next js) 3 1 create a next js project start by setting up your next js project with typescript, tailwind css, and shadcn/ui components npx create next app\@latest invoicing app typescript cd invoicing app npm install tailwindcss postcss autoprefixer npx tailwindcss init 3 2 set up tailwind css configure tailwind by updating the tailwind config js and adding the styles to globals css in tailwind config js module exports = { content \[ ' /pages/ / {js,ts,jsx,tsx}', ' /components/ / {js,ts,jsx,tsx}', ], theme { extend {}, }, plugins \[], }; in styles/globals css @tailwind base; @tailwind components; @tailwind utilities; 3 3 install parse sdk and additional services install the necessary dependencies for parse, clerk, stripe, and resend npm install parse @clerk/clerk next stripe resend 3 4 initialize parse sdk in next js in pages/ app tsx , initialize the parse sdk with your back4app credentials import { clerkprovider } from '@clerk/clerk react'; import parse from 'parse'; parse initialize('your app id', 'your javascript key'); parse serverurl = 'https //parseapi back4app com'; function myapp({ component, pageprops }) { return ( \<clerkprovider> \<component { pageprops} /> \</clerkprovider> ); } export default myapp; replace 'your app id' and 'your javascript key' with your back4app credentials 4\ implementing user authentication 4 1 clerk integration for authentication set up clerk for authentication go to clerk's dashboard, create an application, and grab your api keys update your env local file with clerk keys next public clerk frontend api=your clerk frontend api clerk api key=your clerk api key now create pages/sign in tsx and pages/sign up tsx for sign in and sign up functionality using clerk components in pages/sign in tsx import { signin } from '@clerk/clerk react'; export default function signinpage() { return \<signin />; } in pages/sign up tsx import { signup } from '@clerk/clerk react'; export default function signuppage() { return \<signup />; } 5\ organization and customer management 5 1 create organizations users can create organizations that they own create a page pages/organizations/new\ tsx for creating new organizations import { usestate } from 'react'; import parse from 'parse'; export default function neworganization() { const \[name, setname] = usestate(''); const handlecreateorganization = async () => { const organization = parse object extend('organization'); const organization = new organization(); organization set('name', name); organization set('ownerid', parse user current()); try { await organization save(); alert('organization created successfully!'); } catch (error) { console error('error creating organization ', error); } }; return ( \<div> \<h1>create new organization\</h1> \<input type="text" value={name} onchange={(e) => setname(e target value)} placeholder="organization name" /> \<button onclick={handlecreateorganization}>create\</button> \</div> ); } 5 2 managing customers create a customer management page pages/customers/new\ tsx for adding new customers import { usestate } from 'react'; import parse from 'parse'; export default function newcustomer() { const \[name, setname] = usestate(''); const \[email, setemail] = usestate(''); const handlecreatecustomer = async () => { const customer = parse object extend('customer'); const customer = new customer(); customer set('name', name); customer set('email', email); customer set('organizationid', parse user current() get('organizationid')); try { await customer save(); alert('customer created successfully!'); } catch (error) { console error('error creating customer ', error); } }; return ( \<div> \<h1>create new customer\</h1> \<input type="text" value={name} onchange={(e) => setname(e target value)} placeholder="customer name" /> \<input type="email" value={email} onchange={(e) => setemail(e target value)} placeholder="customer email" /> \<button onclick={handlecreatecustomer}>create\</button> \</div> ); } 6\ invoice management 6 1 create invoices create an invoice creation page pages/invoices/new\ tsx for generating new invoices import { usestate } from 'react'; import parse from 'parse'; export default function newinvoice() { const \[invoicenumber, setinvoicenumber] = usestate(''); const \[amount, setamount] = usestate(0); const handlecreateinvoice = async () => { const invoice = parse object extend('invoice'); const invoice = new invoice(); invoice set('invoicenumber', invoicenumber); invoice set('amount', amount); invoice set('organizationid', parse user current() get('organizationid')); try { await invoice save(); alert('invoice created successfully!'); } catch (error) { console error('error creating invoice ', error); } }; return ( \<div> \<h1>create new invoice\</h1> \<input type="text" value={invoicenumber} onchange={(e) => setinvoicenumber(e target value)} placeholder="invoice number" /> \<input type="number" value={amount} onchange={(e) => setamount(parsefloat(e target value))} placeholder="amount" /> \<button onclick={handlecreateinvoice}>create\</button> \</div> ); } 6 2 viewing invoices create a page pages/invoices/index tsx to view all invoices with sorting and filtering options import { useeffect, usestate } from 'react'; import parse from 'parse'; export default function invoiceslist() { const \[invoices, setinvoices] = usestate(\[]); useeffect(() => { const fetchinvoices = async () => { const invoice = parse object extend('invoice'); const query = new parse query(invoice); const results = await query find(); setinvoices(results); }; fetchinvoices(); }, \[]); return ( \<div> \<h1>invoices\</h1> {invoices map(invoice => ( \<div key={invoice id}> \<h2>invoice #{invoice get('invoicenumber')}\</h2> \<p>amount ${invoice get('amount')}\</p> \<p>status {invoice get('status')}\</p> \</div> ))} \</div> ); } 7\ payment processing (stripe integration) 7 1 set up stripe checkout to process payments, integrate stripe in pages/invoices/\[id] tsx , implement the payment link creation using stripe checkout import { userouter } from 'next/router'; import { useeffect, usestate } from 'react'; import stripe from 'stripe'; export default function invoicedetail() { const router = userouter(); const { id } = router query; const \[invoice, setinvoice] = usestate(null); useeffect(() => { if (!id) return; const fetchinvoice = async () => { const invoice = parse object extend('invoice'); const query = new parse query(invoice); query equalto('objectid', id); const result = await query first(); setinvoice(result); }; fetchinvoice(); }, \[id]); const handlepayinvoice = async () => { const stripe = new stripe(process env stripe secret key); const session = await stripe checkout sessions create({ payment method types \['card'], line items \[ { price data { currency 'usd', product data { name `invoice #${invoice get('invoicenumber')}` }, unit amount invoice get('amount') 100, }, quantity 1, }, ], mode 'payment', success url window\ location origin + '/success', cancel url window\ location origin + '/cancel', }); window\ location href = session url; }; if (!invoice) return \<div>loading \</div>; return ( \<div> \<h1>invoice #{invoice get('invoicenumber')}\</h1> \<p>amount ${invoice get('amount')}\</p> \<button onclick={handlepayinvoice}>pay now\</button> \</div> ); } 8\ email notifications (resend integration) 8 1 sending invoice emails set up resend to send invoice emails to clients import { resend } from 'resend'; export default async function sendinvoiceemail(invoiceid) { const resend = new resend(process env resend api key); const invoice = await fetchinvoicedetails(invoiceid); // function to fetch invoice details const customer = await fetchcustomerdetails(invoice customerid); await resend sendemail({ from 'no reply\@invoicingapp com', to customer email, subject `invoice #${invoice invoicenumber}`, html `\<h1>invoice #${invoice invoicenumber}\</h1>\<p>amount ${invoice amount}\</p>`, }); } 9\ deployment on vercel to deploy the app on vercel, install the vercel cli and deploy npm install g vercel vercel configure environment variables for clerk, stripe, resend, and back4app in the vercel dashboard before deployment 10\ conclusion in this tutorial, we built a complete invoicing app with next js, integrated back4app for the backend, clerk for authentication, stripe for payments, and resend for email notifications we covered user and organization management, invoice creation, payment processing, and deployment