NextJS Templates
使用 Next.js、Vercel 和 Back4app 的发票应用程序教程
25 分
在本教程中,我们将构建一个完整的 发票应用程序 ,使用 next js 作为前端, back4app 作为后端,以及像 clerk 这样的额外服务用于身份验证, stripe 用于支付,以及 resend 用于电子邮件通知。本指南将介绍如何设置身份验证、发票管理、支付处理和在 vercel 上部署 1\ 前提条件 在开始之前,请确保您拥有以下内容: 一个 back4app 账户 ( 在这里注册 https //www back4app com/ ) 一个 clerk 账户 ( clerk io https //clerk dev/ ) 用于身份验证 一个 stripe 账户 ( stripe com https //stripe com/ ) 用于支付处理 一个 resend 账户 ( resend io https //resend io/ ) 用于电子邮件通知 安装 node js 和 npm ( node js 安装 https //nodejs org/ ) 2\ 设置后端 (back4app) 2 1 在 back4app 中创建新项目 登录 back4app 并创建一个新项目。 导航到 数据浏览器 并创建以下类: 2 2 数据模型(类) 2 2 1 用户 (由 clerk 自动处理) 对象id 用户名 电子邮件 2 2 2 组织 创建一个名为 组织 的类。 添加以下列: 名称 (字符串):组织的名称。 所有者id (指针< user>):指向 用户 类(由clerk自动创建)。 成员 (数组<指针< user>>):属于该组织的用户数组。 2 2 3 客户 创建一个名为 客户 的类。 添加这些列: 名称 (字符串):客户的名称。 电子邮件 (字符串):客户的电子邮件。 组织id (指针):该客户所属的组织。 2 2 4 发票 创建一个类叫做 发票 添加这些列 invoicenumber (字符串) 唯一的发票标识符 customerid (指针) 链接到客户 organizationid (指针) 链接到组织 amount (数字) 发票的总金额 status (字符串) 选项包括 '草稿', '已发送', '已支付', 和 '逾期' duedate (日期) 发票到期日 items (数组) 发票中的项目列表 2 2 5 付款 创建一个类叫做 付款 添加这些列 invoiceid (指针) 链接到相关的发票 amount (数字) 付款金额 stripesessionid (字符串) stripe会话的id status (字符串) 付款状态 (例如, '待处理', '成功', '失败') 3\ 设置前端 (next js) 3 1 创建一个 next js 项目 首先使用 typescript、tailwind css 和 shadcn/ui 组件设置您的 next js 项目。 npx create next app\@latest invoicing app typescript cd invoicing app npm install tailwindcss postcss autoprefixer npx tailwindcss init 3 2 设置 tailwind css 通过更新 tailwind config js 并将样式添加到 globals css 来配置 tailwind。 在 tailwind config js module exports = { content \[ ' /pages/ / {js,ts,jsx,tsx}', ' /components/ / {js,ts,jsx,tsx}', ], theme { extend {}, }, plugins \[], }; 在 styles/globals css @tailwind base; @tailwind components; @tailwind utilities; 3 3 安装 parse sdk 和其他服务 安装 parse、clerk、stripe 和 resend 的必要依赖项。 npm install parse @clerk/clerk next stripe resend 3 4 在 next js 中初始化 parse sdk 在 pages/ app tsx , 使用您的 back4app 凭据初始化 parse sdk 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; 将 'your app id' 和 'your javascript key' 替换为您的 back4app 凭据。 4\ 实现用户认证 4 1 clerk 集成用于身份验证 设置 clerk 进行身份验证。访问 clerk 的仪表板,创建一个应用程序,并获取您的 api 密钥。使用 clerk 密钥更新您的 env local 文件。 next public clerk frontend api=your clerk frontend api clerk api key=your clerk api key 现在创建 pages/sign in tsx 和 pages/sign up tsx 以使用 clerk 组件实现登录和注册功能。 在 pages/sign in tsx import { signin } from '@clerk/clerk react'; export default function signinpage() { return \<signin />; } 在 pages/sign up tsx import { signup } from '@clerk/clerk react'; export default function signuppage() { return \<signup />; } 5\ 组织和客户管理 5 1 创建组织 用户可以创建他们拥有的组织。创建一个页面 pages/organizations/new\ tsx 用于创建新组织。 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 管理客户 创建一个客户管理页面 pages/customers/new\ tsx 用于添加新客户。 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\ 发票管理 6 1 创建发票 创建一个发票创建页面 pages/invoices/new\ tsx 用于生成新发票。 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 查看发票 创建一个页面 pages/invoices/index tsx 来查看所有发票,并提供排序和过滤选项。 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\ 付款处理(stripe 集成) 7 1 设置 stripe checkout 要处理支付,请集成 stripe。在 pages/invoices/\[id] tsx ,实现使用 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\ 邮件通知(重新发送集成) 8 1 发送发票邮件 设置 resend 以向客户发送发票邮件。 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\ 在 vercel 上部署 要在 vercel 上部署应用程序,请安装 vercel cli 并进行部署。 npm install g vercel vercel 在部署之前,请在 vercel 仪表板中配置 clerk、stripe、resend 和 back4app 的环境变量。 10\ 结论 在本教程中,我们构建了一个完整的 发票应用程序 ,使用 next js,集成了 back4app 作为后端,使用 clerk 进行身份验证,使用 stripe 进行支付,以及使用 resend 进行电子邮件通知。我们涵盖了用户和组织管理、发票创建、支付处理和部署。