NextJS Templates
Учебник по созданию приложения для выставления счетов с Next.js, Vercel и Back4app
26 мин
в этом учебном пособии мы создадим полное invoicing app с использованием 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 и создайте новый проект перейдите в data browser и создайте следующие классы 2 2 модели данных (классы) 2 2 1 пользователь (автоматически обрабатывается clerk) идентификатор объекта имя пользователя электронная почта 2 2 2 организация создайте класс под названием организация добавьте следующие столбцы имя (строка) имя организации идентификатор владельца (указатель< пользователь>) указывает на класс пользователь (создан автоматически clerk) участники (массив<указатель< пользователь>>) массив пользователей, принадлежащих этой организации 2 2 3 клиент создайте класс под названием клиент добавьте эти столбцы имя (строка) имя клиента электронная почта (строка) электронная почта клиента идентификатор организации (указатель) организация, к которой принадлежит этот клиент 2 2 4 счет фактура создайте класс под названием счет фактура добавьте эти столбцы номерсчета (строка) уникальный идентификатор счета идентификаторклиента (указатель) ссылка на клиента идентификаторорганизации (указатель) ссылка на организацию сумма (число) общая сумма счета статус (строка) опции включают 'черновик', 'отправлено', 'оплачено' и 'просрочено' срокплатежа (дата) дата платежа по счету позиции (массив) список позиций в счете 2 2 5 платеж создайте класс под названием платеж добавьте эти столбцы идентификаторсчета (указатель) ссылка на связанный счет сумма (число) сумма платежа идентификаторсессииstripe (строка) идентификатор сессии stripe статус (строка) статус платежа (например, 'ожидание', 'успешно', 'неудачно') 3\ настройка фронтенда (next js) 3 1 создание проекта next js начните с настройки вашего проекта next js с typescript, tailwind css и компонентами shadcn/ui npx create next app\@latest invoicing app typescript cd invoicing app npm install tailwindcss postcss autoprefixer npx tailwindcss init 3 2 настройка tailwind css настройте tailwind, обновив tailwind config js и добавив стили в globals css в 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 инициализация parse sdk в next js в pages/ app tsx , инициализируйте parse sdk с вашими учетными данными back4app 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 ключи обновите свой env local файл с ключами clerk 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 настройте переменные окружения для clerk, stripe, resend и back4app в панели управления vercel перед развертыванием 10\ заключение в этом учебном пособии мы создали полное invoicing app с использованием next js, интегрировали back4app для бэкенда, clerk для аутентификации, stripe для платежей и resend для уведомлений по электронной почте мы рассмотрели управление пользователями и организациями, создание счетов, обработку платежей и развертывание