
Отправка счета и добавление напоминания об оплате в Next.js с помощью Courier API
15 февраля 2023 г.Многие приложения для управления счетами с открытым исходным кодом созданы с помощью Laravel. Как разработчик Javascript, я хотел создать «Решение React» для разработчиков, знакомых с React и Javascript.
Проблема, которую я обнаружил при сборке с помощью сервисов в Node.js, заключается в отсутствии встроенного почтового модуля. Итак, мне пришлось найти сторонний сервис, чтобы сделать это за меня. В этой статье я буду интегрировать Courier для отправки электронных писем для этого проекта https://github.com/fazzaamiarso/invoys.
Предварительные условия
Поскольку эта статья не является вашим типичным продолжением (больше похоже на "пожалуйста, присядьте и посмотрите, как я это делаю"), не обязательно быть знакомым со всеми используемыми технологиями. Однако знакомство с Typescript и Next.js будет полезно для более быстрого понимания.
Технологии в этом блоге:
* Typescript: безопасность типов и автодополнение лучше, верно? * Next.js: готовая к работе среда для создания полнофункционального приложения даже для новичков. * Prisma: отличная ORM для работы с базами данных. Мы используем Prisma из-за ее безопасности типов и автоматического завершения, что обеспечивает отличный опыт разработчика с добавлением машинописного текста. * Trpc: позволяет нам легко создавать сквозную безопасность типов между нашим клиентом Next.js и сервером. * Courier API: отличный сервис/платформа для обработки наших уведомлений, таких как электронная почта, SMS и многое другое.
Вы можете найти полный исходный код здесь для справки.
Цели
Прежде чем создавать функции, давайте определим наши цели.
- Отправить ссылку на счет на электронную почту клиента.
- Отправить напоминание за день до даты оплаты счета.
- Отменить напоминание о сроке оплаты счета, когда счет уже оплачен.
- Обработка сетевых ошибок.
Часть 1. Настройка Courier Platform
Перейдем к панели управления Courier. По умолчанию это производственная среда. Поскольку я хочу все проверить, я перейду к тестовой среде, щелкнув раскрывающийся список в правом верхнем углу.
<цитата>Мы можем позже скопировать все шаблоны в рабочую среду или наоборот.
Теперь я создам бренд для своей электронной почты. уведомления.
Я просто добавлю логотип (учтите, что ширина логотипа фиксирована и составляет 140 пикселей) в шапке и ссылки на социальные сети в футере. Пользовательский интерфейс конструктора довольно прост, поэтому вот окончательный результат.
Не забудьте опубликовать изменения.
Часть 2. Отправка счета на электронную почту
В настоящее время кнопка отправки электронной почты в пользовательском интерфейсе ничего не делает.
Я собираюсь создать файл courier.ts
в src/lib/
, чтобы сохранить весь код, связанный с Courier. Кроме того, я буду использовать клиентскую библиотеку courier node.js, которая уже абстрагировала все конечные точки Courier API в функции.
Прежде чем я создам функциональность, давайте создадим дизайн уведомлений по электронной почте в Courier’s Designer и настроим провайдера Gmail.
На странице конструктора писем мы увидим, что созданный бренд уже интегрирован. После этого давайте разработаем шаблон в соответствии с необходимыми данными. Вот окончательный результат.
Обратите внимание на значение с {}
, которое становится зеленым, это означает, что это переменная, которую можно вставлять динамически. Я также установил кнопку "Просмотреть счет" (или действие) с помощью переменной.
Прежде чем я смогу использовать шаблон, мне нужно создать тестовое событие, щелкнув вкладку предварительного просмотра. Затем появится приглашение назвать событие и установить data
в формате JSON. Это поле данных — это то, что будет заполнять значение зеленых переменных {}
(данные также можно установить из кода). Поскольку это тестовое событие, я заполню его произвольными значениями.
Далее я опубликую шаблон, чтобы я мог его использовать. Затем перейдите на вкладку «Отправить». Он покажет необходимый код для отправки электронной почты программным способом, а данные
будут заполнены предыдущим тестовым событием, которое я создал.
Бэкенд
Я скопирую тестовый AUTH_TOKEN
в файл .env
, а фрагмент скопирую в src/lib/courier.ts
.
const authToken = process.env.COURIER_AUTH_TOKEN;
// email to receive all sent notifications in DEVELOPMENT mode
const testEmail = process.env.COURIER_TEST_EMAIL;
const INVOICE_TEMPLATE_ID = <TEMPLATE_ID>;
const courierClient = CourierClient({
authorizationToken: authToken,
});
Создайте функцию sendInvoice
, которая будет отвечать за отправку электронной почты. Чтобы отправить электронное письмо из кода, я использую функцию courierClient.send()
.
// src/lib/courier.ts
export const sendInvoice = async ({
customerName,
invoiceNumber,
invoiceViewUrl,
emailTo,
productName,
dueDate,
}: SendInvoice) => {
const recipientEmail = process.env.NODE_ENV === "production" ? emailTo : testEmail;
const { requestId } = await courierClient.send({
message: {
to: {
email: recipientEmail,
},
template: INVOICE_TEMPLATE_ID,
// Data for courier template designer
data: {
customerName,
invoiceNumber,
invoiceViewUrl,
productName,
dueDate,
},
},
});
return requestId
};
Определите типы для функции sendInvoice
.
// src/lib/courier.ts
interface SendInvoice {
productName: string;
dueDate: string;
customerName: string;
invoiceNumber: string;
invoiceViewUrl: string;
emailTo: string;
}
Теперь, когда я могу отправить электронное письмо, я вызову его в конечной точке trpc sendEmail
, которая находится в src/server/trpc/router/invoice.ts
.< /p>
Помните, что конечная точка trpc — это маршрут API Next.js. В этом случае sendEmail
будет то же самое, что и вызов /api/trpc/sendEmail
маршрута с fetch
под капотом. Подробнее см. https://trpc.io/docs/quickstart.
// src/server/trpc/router/invoice.ts
import { sendInvoice } from '@lib/courier';
import { dayjs } from '@lib/dayjs';
// .....SOMEWHERE BELOW
sendEmail: protectedProcedure
.input(
z.object({
customerName: z.string(),
invoiceNumber: z.string(),
invoiceViewUrl: z.string(),
emailTo: z.string(),
invoiceId: z.string(),
productName: z.string(),
dueDate: z.date(),
})
)
.mutation(async ({ input }) => {
const invoiceData = {
...input,
dueDate: dayjs(input.dueDate).format('D MMMM YYYY'),
};
await sendInvoice(invoiceData);
}),
Для тех, кто не знаком с trpc, то, что я сделал, это то же самое, что и обработка запроса POST
. Давайте разберемся.
- Trpc способ определения запроса входных данных от клиента путем проверки с помощью Zod. Здесь я определяю все данные, необходимые для функции
sendInvoice
.
.input(
z.object({
customerName: z.string(),
invoiceNumber: z.string(),
invoiceViewUrl: z.string(),
emailTo: z.string(),
invoiceId: z.string(),
productName: z.string(),
dueDate: z.date(),
})
)
- Определить обработчик запроса
POST
(мутацию).
// input from before
.mutation(async ({ input }) => {
const invoiceData = {
...input,
// format a date to string with a defined format.
dueDate: dayjs(input.dueDate).format('D MMMM YYYY'), // ex.'2 January 2023'
};
// send the email
await sendInvoice(invoiceData);
}),
Внешний интерфейс
Теперь я могу начать добавлять функциональность к кнопке отправки электронной почты. Я собираюсь использовать функцию trpc.useMutation()
, которая является тонкой оболочкой функции tanstack-query
useMutation`.
Добавим функцию мутации. При успешном ответе я хочу отправить всплывающее уведомление об успехе в пользовательском интерфейсе.
//src/pages/invoices/[invoiceId]/index.tsx
import toast from 'react-hot-toast';
const InvoiceDetail: NextPage = () => {
// calling the `sendEmail` trpc endpoint with tanstack-query.
const sendEmailMutation = trpc.invoice.sendEmail.useMutation({
onSuccess() {
toast.success('Email sent!');
}
});
}
Я могу просто использовать функцию как встроенный обработчик, но я хочу создать новый обработчик для кнопки.
//src/pages/invoices/[invoiceId]/index.tsx
// still inside the InvoiceDetail component
const sendInvoiceEmail = () => {
const hostUrl = window.location.origin;
// prevent a user from spamming when the API call is not done.
if (sendEmailMutation.isLoading) return;
// send input data to `sendEmail` trpc endpoint
sendEmailMutation.mutate({
customerName: invoiceDetail.customer.name,
invoiceNumber: `#${invoiceDetail.invoiceNumber}`,
invoiceViewUrl: `${hostUrl}/invoices/${invoiceDetail.id}/preview`,
emailTo: invoiceDetail.customer.email,
invoiceId: invoiceDetail.id,
dueDate: invoiceDetail.dueDate,
productName: invoiceDetail.name,
});
};
Теперь я могу прикрепить обработчик к кнопке отправки электронной почты.
//src/pages/invoices/[invoiceId]/index.tsx
<Button
variant="primary"
onClick={sendInvoiceEmail}
isLoading={sendEmailMutation.isLoading}>
Send to Email
</Button>
Вот рабочий интерфейс.
Часть 3. Отправка напоминания об оплате
Чтобы запланировать напоминание, которое будет отправлено за день до даты оплаты счета, я буду использовать Courier's Automation API< /а>.
Перед добавлением функции определите типы для параметра и реорганизуйте типы.
Теперь я добавляю функцию scheduleReminder
в src/lib/courier
Часть 4. Отмена напоминания
Добавьте cancelation_token в scheduleReminder
, я использую идентификатор счета в качестве токена.
Часть 5. Обработка ошибок
Теперь я могу обрабатывать ошибки, добавляя блок try-catch
ко всем функциям в src/lib/courier.ts
.
Давайте рассмотрим пример обработки на конечной точке sendEmail
trpc.
Часть 6. Переход к производству
Заключение
Начните прямо сейчас: https://app.courier.com/signup
Об авторе
Меня зовут Фазза Разак Амиарсо, я веб-разработчик полного цикла из Индонезии. Я также являюсь энтузиастом Open Source. Я люблю делиться своими знаниями и знаниями в своем блоге. Время от времени я помогаю другим разработчикам в FrontendMentor в свободное время.
Свяжитесь со мной на LinkedIn.
Быстрые ссылки
:::информация Также опубликовано здесь.
:::
Оригинал