在本教程中,您將學習如何建立電子商務商店,客戶可以在其中透過 Stripe 購買產品並付款。成功付款後,將向客戶發送電子郵件通知,並向管理員用戶發送應用程式內通知。管理員用戶也可以在應用程式中建立和刪除產品。

為了建立這個應用程式,我們將使用以下工具:

  • Appwrite - 用於驗證使用者身份,以及保存和檢索產品詳細資訊。

  • Next.js - 用於建立應用程式的使用者介面和後端。

  • Novu - 用於發送電子郵件和應用程式內通知。

  • React Email - 用於建立電子郵件範本。

  • Stripe - 用於將付款結帳整合到應用程式中。

應用程式


使用 Next.js 建立應用程式介面

應用程式頁面根據指派給使用者的角色分為兩部分。客戶可以在付款前存取主頁並登入應用程式。管理員使用者可以存取所有頁面,包括登入頁面和儀表板頁面,他們可以在其中新增和刪除產品。

現在,讓我們建立應用程式。

https://media1.giphy.com/media/iopxsZtW2QVRs4poEC/giphy.gif?cid=7941fdc6aot3qt7vvq4voh5c1iagyusdpuga713m8ljqcqmd&ep=v1_gifs_searchiagyusdpuga713m8ljqcqmd&ep=v1_gifs_searchiagyusdpugagif&ct

透過執行以下程式碼片段來建立一個新的 Next.js Typescript 專案:

npx create-next-app novu-store

接下來,安裝React IconsHeadless UI包。 React Icons 允許我們在應用程式中使用各種圖標,而 Headless UI 則提供易於使用的現代 UI 元件。

npm install react-icons @headlessui/react

將此程式碼片段從GitHub 儲存庫複製到app/page.tsx檔案中。它在螢幕上呈現產品列表,並允許用戶選擇購物車中的商品,類似於下圖。

1

建立登入路由,使用戶能夠使用其 GitHub 帳戶進行簽署。將下面的程式碼片段複製到app/login/page.tsx檔案中。

//👉🏻 create a login folder containing a page.tsx file
export default function Home() {
    const handleGoogleSignIn = async () => {};

    return (
        <main className='w-full min-h-screen flex flex-col items-center justify-center'>
            <h2 className='font-semibold text-3xl mb-2'>Customer Sign in</h2>
            <p className='mb-4 text-sm text-red-500'>
                You need to sign in before you can make a purchase
            </p>
            <button
                className='p-4 border-[2px] border-gray-500 rounded-md hover:bg-black hover:text-white w-2/3'
                onClick={() => handleGoogleSignIn()}
            >
                Sign in with GitHub
            </button>
        </main>
    );
}

客戶登入

當使用者點擊「登入」按鈕時,會將他們重新導向到 GitHub 驗證頁面並提示他們登入應用程式。您很快就會了解如何使用Appwrite執行此操作。

接下來,讓我們建立管理頁面。在app資料夾中新增包含logindashboard路由的admin資料夾。

cd app
mkdir admin && cd admin
mkdir dashboard login

dashboardlogin資料夾中新增page.tsx文件,並將下面的程式碼片段複製到login/page.tsx檔案中。

"use client";
import Link from "next/link";
import { useState } from "react";

export default function Login() {
    const [email, setEmail] = useState<string>("");
    const [password, setPassword] = useState<string>("");

    const handleLogin = async (e: React.FormEvent) => {
        e.preventDefault();
        console.log({ email, password });
    };

    return (
        <main className='w-full min-h-screen flex flex-col items-center justify-center'>
            <h2 className='font-semibold text-3xl mb-4'> Admin Sign in</h2>
            <form className='w-2/3' onSubmit={handleLogin}>
                <label htmlFor='email' className='block'>
                    Email
                </label>
                <input
                    type='email'
                    id='email'
                    className='w-full px-4 py-3 border border-gray-400 rounded-sm mb-4'
                    required
                    value={email}
                    placeholder='[email protected]'
                    onChange={(e) => setEmail(e.target.value)}
                />

                <label htmlFor='password' className='block'>
                    Password
                </label>
                <input
                    type='password'
                    id='password'
                    className='w-full px-4 py-3 border border-gray-400 rounded-sm mb-4'
                    required
                    value={password}
                    placeholder='admin123'
                    onChange={(e) => setPassword(e.target.value)}
                />
                <button className='p-4 text-lg mb-3 bg-blue-600 text-white w-full rounded-md'>
                    Sign in
                </button>
                <p className='text-sm text-center'>
                    Not an Admin?{" "}
                    <Link href='/login' className='text-blue-500'>
                        Sign in as a Customer
                    </Link>
                </p>
            </form>
        </main>
    );
}

上面的程式碼片段呈現一個表單,該表單接受管理員的電子郵件和密碼,驗證憑證,然後將使用者登入應用程式中。

管理員登入

管理儀表板頁面呈現可用的產品,並允許管理員使用者在應用程式中新增和刪除產品。將此程式碼片段複製dashboard/page.tsx檔案中以建立使用者介面。

2

恭喜!您已經建立了應用程式介面。在接下來的部分中,您將了解如何將應用程式連接到 Appwrite 後端並在客戶端和伺服器之間發送資料。


如何將 Appwrite 新增到 Next.js 應用程式

Appwrite 是一項開源後端服務,可讓您建立安全且可擴展的軟體應用程式。它提供多種身份驗證方法、安全性資料庫、文件儲存、雲端訊息傳遞等功能,這些對於建立全端應用程式至關重要。

在本部分中,您將了解如何設定 Appwrite 專案,包括身份驗證、資料庫和檔案儲存等功能。

首先,請造訪Appwrite Cloud ,並為您的專案建立一個帳戶和組織。

接下來,建立一個新專案並選擇您的首選區域來託管該專案。

應用程式寫入1

選擇Web作為應用程式的平台 SDK。

應用程式編寫2

請依照螢幕上顯示的步驟進行操作。由於您目前正在開發模式下建置,因此您可以使用通配符 ( * ) 作為主機名,並在部署應用程式後將其變更為您的網域名稱。

應用程式寫入3

在 Next.js 專案中安裝 Appwrite 用戶端 SDK。

npm install appwrite

最後,在 Next.js 應用程式資料夾中建立一個appwrite.ts文件,並將下面的程式碼片段複製到該文件中以初始化 Appwrite。

import { Client, Account, Databases, Storage } from "appwrite";

const client = new Client();

client
    .setEndpoint("https://cloud.appwrite.io/v1")
    .setProject(<YOUR_PROJECT_ID>);

export const account = new Account(client);

export const db = new Databases(client);

export const storage = new Storage(client);

使用 Appwrite 設定 GitHub 身份驗證

在這裡,您將了解如何使用 Appwrite 設定 GitHub 和電子郵件/密碼驗證。預設已配置電子郵件/密碼身份驗證,因此我們專注於設定 GitHub 身份驗證。

在繼續之前,您需要使用您的 GitHub 帳戶建立GitHub OAuth 應用程式。 Appwrite 將需要客戶端 ID 和金鑰來設定 GitHub 身份驗證。

GitHub 1

透過從側邊欄選單中選擇Auth並導覽至Settings選項卡,啟用 Appwrite 的 GitHub 驗證方法。

應用程式編寫4

將您的 GitHub 用戶端 ID 和金鑰複製到 Appwrite 的 GitHub OAuth 設定中。

最後,確保將 Appwrite 產生的 URI 複製到 GitHub 應用程式設定中。

GitHub 2

設定 Appwrite 資料庫

從側邊欄選單中選擇資料庫並建立新資料庫。您可以將其命名為novu store

應用程式寫入5

接下來,建立products集合。它將包含應用程式中的產品清單。

應用程式寫入 6

將名稱、價格和圖像屬性新增至集合。

應用程式寫入 7

在「設定」標籤下,更新權限以允許每個使用者執行 CRUD 操作。但是,您可以在部署應用程式後變更此設置,以確保只有經過身份驗證的使用者才能執行各種操作。

應用程式寫入 8

最後,將專案、資料庫和集合 ID 複製到.env.local檔案中。這可以確保您的憑證安全,並允許您引用其環境變數中的每個值。

NEXT_PUBLIC_PROJECT_ID=<YOUR_PROJECT_ID>
NEXT_PUBLIC_DB_ID=<YOUR_DATABASE_ID>
NEXT_PUBLIC_PRODUCTS_COLLECTION_ID=<YOUR_DB_COLLECTION_ID>

設定應用程式寫入存儲

從側邊欄選單中選擇Storage ,然後建立新儲存桶來儲存所有產品影像。

應用程式編寫 9

Settings標籤下,更新「權限」以暫時允許任何使用者。

應用程式寫入 10

設定可接受的文件格式。由於我們上傳的是圖像,因此您可以選擇.jpg.png檔案格式。

應用寫入 11

最後,將您的儲存桶 ID 複製到.env.local檔案中。

NEXT_PUBLIC_BUCKET_ID=<YOUR_BUCKET_ID>

恭喜!您已成功配置 Appwrite。我們現在可以開始與其各種功能進行互動。


如何使用Appwrite執行CRUD操作

在本部分中,您將了解如何從 Appwrite 建立、檢索和刪除產品。用戶需要能夠在購買前查看現有產品,而管理員用戶應有權在應用程式中新增和刪除產品。

首先,在 Next.js app資料夾中建立一個utils.ts檔案。該文件將包含所有 Appwrite 資料庫交互,然後您可以將其導入到必要的頁面中。

cd app
touch utils.ts

將產品儲存到 Appwrite

回想一下, products集合有三個屬性:名稱、圖像和價格。因此,在將產品新增至資料庫時,您需要先上傳產品的圖像,從回應中檢索其 URL 和 ID,然後將 URL 作為產品的圖像屬性上傳,使用圖像的儲存 ID 作為產品資料。

這是解釋這一點的程式碼片段:

import { db, storage } from "@/app/appwrite";
import { ID } from "appwrite";

export const createProduct = async (
    productTitle: string,
    productPrice: number,
    productImage: any
) => {
    try {
        //👇🏻 upload the image
        const response = await storage.createFile(
            process.env.NEXT_PUBLIC_BUCKET_ID!,
            ID.unique(),
            productImage
        );
        //👇🏻 get the image's URL
        const file_url = `https://cloud.appwrite.io/v1/storage/buckets/${process.env.NEXT_PUBLIC_BUCKET_ID}/files/${response.$id}/view?project=${process.env.NEXT_PUBLIC_PROJECT_ID}&mode=admin`;

        //👇🏻 add the product to the database
        await db.createDocument(
            process.env.NEXT_PUBLIC_DB_ID!,
            process.env.NEXT_PUBLIC_PRODUCTS_COLLECTION_ID!,
            response.$id, //👉🏻 use the image's ID
            {
                name: productTitle,
                price: productPrice,
                image: file_url,
            }
        );
        alert("Product created successfully");
    } catch (err) {
        console.error(err);
    }
};

上面的程式碼片段將圖像上傳到 Appwrite 的雲端存儲,並使用儲存桶 ID、圖像 ID 和專案 ID 檢索準確的圖像 URL。圖片成功上傳後,其 ID 將用於產品資料中,以便輕鬆檢索和參考。

從 Appwrite 檢索產品

若要從 Appwrite 取得產品,您可以在頁面載入時在 React useEffect掛鉤中執行下列函數。

export const fetchProducts = async () => {
    try {
        const products = await db.listDocuments(
            process.env.NEXT_PUBLIC_DB_ID!,
            process.env.NEXT_PUBLIC_PRODUCTS_COLLECTION_ID!
        );
        if (products.documents) {
            return products.documents;
        }
    } catch (err) {
        console.error(err);
    }
};

fetchProducts函數傳回products集合中的所有資料。

從 Appwrite 中刪除產品

管理員使用者也可以透過產品 ID 刪除產品。 deleteProduct函數接受產品的 ID 作為參數,並從資料庫中刪除所選產品(包括其圖像),因為它們使用相同的 ID 屬性。

export const deleteProduct = async (id: string) => {
    try {
        await db.deleteDocument(
            process.env.NEXT_PUBLIC_DB_ID!,
            process.env.NEXT_PUBLIC_PRODUCTS_COLLECTION_ID!,
            id
        );
        await storage.deleteFile(process.env.NEXT_PUBLIC_BUCKET_ID!, id);

        alert("Product deleted successfully");
    } catch (err) {
        console.error(err);
    }
};

如何使用 Appwrite 驗證使用者身份

在前面的部分中,我們已經設定了 GitHub 身份驗證方法。在這裡,您將了解如何處理使用者登入應用程式。

若要使客戶能夠使用其 GitHub 帳戶登入應用程式,請在按一下Sign in按鈕時執行以下功能。該函數將使用者重定向到 GitHub,在那裡他們可以向應用程式授權或授予權限,然後登入應用程式:


import { account } from "../appwrite"; import { OAuthProvider } from "appwrite"; const handleGoogleSignIn = async () => { try { account.createOAuth2Session( OAuthProvider.Github, "http://localhost:3000", "http://localhost:3000/login" ); } catch (err) { console.error(err); } };

管理員使用者可以使用電子郵件和密碼登入應用程式。 Appwrite 在授予對應用程式儀表板的存取權之前會驗證憑證。

import { account } from "@/app/appwrite";

const handleLogin = async (e: React.FormEvent) => {
    e.preventDefault();
    try {
        await account.createEmailPasswordSession(email, password);
        alert(`Welcome back 🎉`);
        router.push("/admin/dashboard");
    } catch (err) {
        console.error(err);
        alert("Invalid credentials ❌");
    }
};

Appwrite 還允許您取得目前使用者的資料。例如,如果只有經過身份驗證的使用者才能付款,您可以透過執行下面的程式碼片段來完成此操作。它會檢索目前使用者的資料,如果使用者未登錄,則傳回 null。

import { account } from "@/app/appwrite";

useEffect(() => {
    const checkAuthStatus = async () => {
        try {
            const request = await account.get();
            setUser(request);
        } catch (err) {
            console.log(err);
        }
    };
    checkAuthStatus();
}, []);

如何將 Stripe 付款結帳新增至 Next.js

在本節中,您將了解如何在應用程式中實現 Stripe 付款結帳。 Stripe 是一種流行的線上支付處理平台,可讓您建立產品並將一次性和定期支付方式整合到您的應用程式中。

首先,您需要建立一個 Stripe 帳戶。您可以在本教學中使用測試模式帳戶。

條紋1

點擊頂部選單中的Developers ,然後從 API 金鑰選單中複製您的金鑰。

條紋2

將您的 Stripe 金鑰貼到.env.local檔案中。

STRIPE_SECRET_KEY=<your_secret_key>

安裝Stripe Node.js SDK

npm install stripe

接下來,在 Next.js app資料夾中建立一個api資料夾。 api資料夾將包含應用程式的所有 API 路由和端點。

cd app
mkdir api

透過在api資料夾中新增checkout資料夾來建立checkout端點。

cd api
mkdir checkout && cd checkout
touch route.ts

將下面的程式碼片段複製到route.ts檔中。

import { NextRequest, NextResponse } from "next/server";
import Stripe from "stripe";
const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!);

export async function POST(req: NextRequest) {
    //👇🏻 accepts the customer's cart
    const cart = await req.json();

    try {
        //👇🏻 creates a checkout session
        const session = await stripe.checkout.sessions.create({
            payment_method_types: ["card"],
            line_items: cart.map((product: Product) => ({
                price_data: {
                    currency: "usd",
                    product_data: {
                        name: product.name,
                    },
                    unit_amount: product.price * 100,
                },
                quantity: 1,
            })),
            mode: "payment",
            cancel_url: `http://localhost:3000/?canceled=true`,
            success_url: `http://localhost:3000?success=true&session_id={CHECKOUT_SESSION_ID}`,
        });
        //👇🏻 return the session URL
        return NextResponse.json({ session: session.url }, { status: 200 });
    } catch (err) {
        return NextResponse.json({ err }, { status: 500 });
    }
}

上面的程式碼片段建立了一個接受 POST 請求的結帳端點。它為客戶建立結帳會話並傳回會話 URL。

cancel_urlsuccess_url確定完成或取消付款後將用戶重新導向到何處。

最後,當用戶決定為產品付款時,您可以透過執行以下程式碼片段將客戶的購物車發送到/checkout端點:

const processPayment = async (cart: Product[]) => {
    try {
        if (user !== null) {
            //👇🏻 saves cart to local storage
            localStorage.setItem("cart", JSON.stringify(cart));
            //👇🏻 sends cart to /checkout route
            const request = await fetch("/api/checkout", {
                method: "POST",
                body: JSON.stringify(cart),
                headers: { "Content-Type": "application/json" },
            });
            //👇🏻 retrieves the session URL
            const { session } = await request.json();
            //👇🏻 redirects the user to the checkout page
            window.location.assign(session);
        } else {
            //👇🏻 redirects unauthenticated users
            router.push("/login");
        }
    } catch (err) {
        console.error(err);
    }
};

上面的程式碼片段將購物車儲存到瀏覽器的本機儲存體並將其傳送到 API 端點,然後從後端伺服器檢索回應(會話 URL)並將使用者重新導向至 Stripe 結帳頁面。

條紋3


使用 Novu 發送應用程式內通知和電子郵件通知

Novu是第一個提供統一 API 的通知基礎架構,用於透過多種管道(包括應用程式內、推播、電子郵件、簡訊和聊天)發送通知。

在本部分中,您將了解如何將 Novu 加入到您的應用程式,以便您能夠發送電子郵件和應用程式內訊息。

首先,安裝所需的 Novu 軟體包:

npm install @novu/node @novu/echo @novu/notification-center

當用戶進行購買時,他們將收到一封付款確認電子郵件,管理員用戶也會收到一條應用程式內通知。

為此,您需要在 Novu 上建立帳戶並設定主要電子郵件提供者。在本教程中,我們將使用“重新發送”

在 Novu 上建立帳戶後,建立一個重新傳送帳戶,然後從儀表板上的側邊欄選單中選擇API Keys來建立帳戶。

重新發送 1

接下來,回到 Novu 儀表板,從側邊欄選單中選擇Integrations Store ,然後新增 Resend 作為電子郵件提供者。您需要將重新傳送 API 金鑰和電子郵件地址貼到必填欄位中。

新 1

從側邊欄選單中選擇「設定」 ,然後將您的Novu API金鑰和App ID複製到.env.local檔案中,如下所示。另外,將您的subscriber ID複製到其欄位中 - 您可以從Subscribers部分獲取此資訊。

NOVU_API_KEY=<YOUR_API_FOR_NEXT_SERVER>
NEXT_PUBLIC_NOVU_API_KEY=<YOUR_API_FOR_NEXT_CLIENT>

NEXT_PUBLIC_NOVU_APP_ID=<YOUR_API_ID>

NOVU_SUBSCRIBER_ID=<YOUR_API_FOR_NEXT_SERVER>
NEXT_PUBLIC_NOVU_SUBSCRIBER_ID=<YOUR_API_FOR_CLIENT>

新2

最後,將 Novu 通知鈴新增至管理儀表板,以使管理員使用者能夠在應用程式內接收通知。

import {
    NovuProvider,
    PopoverNotificationCenter,
    NotificationBell,
} from "@novu/notification-center";

export default function AdminNav() {
    return (
        <NovuProvider
            subscriberId={process.env.NEXT_PUBLIC_NOVU_SUBSCRIBER_ID!}
            applicationIdentifier={process.env.NEXT_PUBLIC_NOVU_APP_ID!}
        >
            <PopoverNotificationCenter colorScheme='light'>
                {({ unseenCount }) => <NotificationBell unseenCount={unseenCount} />}
            </PopoverNotificationCenter>
        </NovuProvider>
    );
}

儀表板


如何使用 Novu Echo 建立通知工作流程

Novu提供程式碼優先的工作流程引擎,讓您能夠在程式碼庫中建立通知工作流程。它允許您將電子郵件、簡訊、聊天範本和內容產生器(例如React EmailMJML )整合到 Novu 中,以建立高級且強大的通知。

在本部分中,您將了解如何在應用程式中建立通知工作流程、如何使用 Novu 的電子郵件通知範本以及如何使用 Novu 發送應用程式內通知和電子郵件通知。

透過執行以下命令安裝React Email

npm install react-email @react-email/components -E

將以下腳本包含在您的 package.json 檔案中。 --dir標誌使 React Email 能夠存取位於專案內的電子郵件範本。在本例中,電子郵件範本位於src/emails資料夾中。

{
    "scripts": {
        "email": "email dev --dir src/emails"
    }
}

接下來,在 Next.js app資料夾中建立一個包含email.tsxemails資料夾,並將以下程式碼片段複製到該檔案中:

import {
    Body,
    Column,
    Container,
    Head,
    Heading,
    Hr,
    Html,
    Link,
    Preview,
    Section,
    Text,
    Row,
    render,
} from "@react-email/components";
import * as React from "react";

const EmailTemplate = ({
    message,
    subject,
    name,
}: {
    message: string;
    subject: string;
    name: string;
}) => (
    <Html>
        <Head />
        <Preview>{subject}</Preview>
        <Body style={main}>
            <Container style={container}>
                <Section style={header}>
                    <Row>
                        <Column style={headerContent}>
                            <Heading style={headerContentTitle}>{subject}</Heading>
                        </Column>
                    </Row>
                </Section>

                <Section style={content}>
                    <Text style={paragraph}>Hey {name},</Text>
                    <Text style={paragraph}>{message}</Text>
                </Section>
            </Container>

            <Section style={footer}>
                <Text style={footerText}>
                    You&apos;re receiving this email because your subscribed to Newsletter
                    App
                </Text>

                <Hr style={footerDivider} />
                <Text style={footerAddress}>
                    <strong>Novu Store</strong>, &copy;{" "}
                    <Link href='https://novu.co'>Novu</Link>
                </Text>
            </Section>
        </Body>
    </Html>
);

export function renderEmail(inputs: {
    message: string;
    subject: string;
    name: string;
}) {
    return render(<EmailTemplate {...inputs} />);
}

const main = {
    backgroundColor: "#f3f3f5",
    fontFamily: "HelveticaNeue,Helvetica,Arial,sans-serif",
};

const headerContent = { padding: "20px 30px 15px" };

const headerContentTitle = {
    color: "#fff",
    fontSize: "27px",
    fontWeight: "bold",
    lineHeight: "27px",
};

const paragraph = {
    fontSize: "15px",
    lineHeight: "21px",
    color: "#3c3f44",
};

const divider = {
    margin: "30px 0",
};

const container = {
    width: "680px",
    maxWidth: "100%",
    margin: "0 auto",
    backgroundColor: "#ffffff",
};

const footer = {
    width: "680px",
    maxWidth: "100%",
    margin: "32px auto 0 auto",
    padding: "0 30px",
};

const content = {
    padding: "30px 30px 40px 30px",
};

const header = {
    borderRadius: "5px 5px 0 0",
    display: "flex",
    flexDireciont: "column",
    backgroundColor: "#2b2d6e",
};

const footerDivider = {
    ...divider,
    borderColor: "#d6d8db",
};

const footerText = {
    fontSize: "12px",
    lineHeight: "15px",
    color: "#9199a1",
    margin: "0",
};

const footerLink = {
    display: "inline-block",
    color: "#9199a1",
    textDecoration: "underline",
    fontSize: "12px",
    marginRight: "10px",
    marginBottom: "0",
    marginTop: "8px",
};

const footerAddress = {
    margin: "4px 0",
    fontSize: "12px",
    lineHeight: "15px",
    color: "#9199a1",
};

上面的程式碼片段使用 React Email 建立了一個可自訂的電子郵件範本。您可以找到更多易於編輯的靈感或模板。該元件還接受訊息、主題和名稱作為屬性,並將它們填入元素中。

最後,您可以在終端機中執行npm run email來預覽範本。

接下來,讓我們將電子郵件範本整合到 Novu Echo 中。首先,關閉 React Email 伺服器,然後執行下面的程式碼片段。它會在瀏覽器中開啟Novu Dev Studio

npx novu-labs@latest echo

在 Next.js 應用程式資料夾中建立一個包含client.ts檔案的echo資料夾,並將此程式碼片段複製到該檔案中。

import { Echo } from "@novu/echo";
import { renderEmail } from "@/app/emails/email";

interface EchoProps {
    step: any;
    payload: {
        subject: string;
        message: string;
        name: string;
        totalAmount: string;
    };
}
export const echo = new Echo({
    apiKey: process.env.NEXT_PUBLIC_NOVU_API_KEY!,
    devModeBypassAuthentication: process.env.NODE_ENV === "development",
});

echo.workflow(
    "novu-store",
    async ({ step, payload }: EchoProps) => {
        //👇🏻 in-app notification step
        await step.inApp("notify-admin", async () => {
            return {
                body: `${payload.name} just made a new purchase of ${payload.totalAmount} 🎉`,
            };
        });
        //👇🏻 email notification step
        await step.email(
            "email-customer",
            async () => {
                return {
                    subject: `${payload ? payload?.subject : "No Subject"}`,
                    body: renderEmail(payload),
                };
            },
            {
                inputSchema: {
                    type: "object",
                    properties: {},
                },
            }
        );
    },
    {
        payloadSchema: {
            type: "object",
            properties: {
                message: {
                    type: "string",
                    default: "Congratulations! Your purchase was successful! 🎉",
                },
                subject: { type: "string", default: "Message from Novu Store" },
                name: { type: "string", default: "User" },
                totalAmount: { type: "string", default: "0" },
            },
            required: ["message", "subject", "name", "totalAmount"],
            additionalProperties: false,
        },
    }
);

此程式碼片段定義了一個名為novu-store Novu 通知工作流程,該工作流程接受包含電子郵件主題、訊息、客戶姓名和總金額的有效負載。

此工作流程有兩個步驟:應用程式內通知和電子郵件通知。應用程式內通知使用通知鈴聲向管理員發送訊息,而電子郵件則向客戶的電子郵件發送訊息。

接下來,您需要為 Novu Echo 建立 API 路由。在api資料夾中,建立一個包含route.ts檔案的email資料夾,並將下面提供的程式碼片段複製到該檔案中。

import { serve } from "@novu/echo/next";
import { echo } from "@/app/echo/client";

export const { GET, POST, PUT } = serve({ client: echo });

在終端機中執行npx novu-labs@latest echo 。它將自動開啟 Novu Dev Studio,您可以在其中預覽工作流程並將其與雲端同步。

新3

Sync to Cloud按鈕會觸發一個彈出窗口,其中提供有關如何將工作流程推送到 Novu 雲端的說明。

新4

若要繼續,請在終端機中執行以下程式碼片段。這將產生一個唯一的 URL,表示您的開發環境和雲端環境之間的本機隧道。

npx localtunnel --port 3000

將產生的連結與 Echo API 端點一起複製到 Echo Endpoint 欄位中,按一下Create Diff按鈕,然後部署變更。

https://<LOCAL_TUNNEL_URL>/<ECHO_API_ENDPOINT (/api/email)> 

恭喜!您剛剛從程式碼庫建立了 Novu 工作流程。

新5

最後,讓我們建立在用戶付款時發送電子郵件和應用程式內通知的端點。建立一個api/send路由並將下面的程式碼片段複製到檔案中:

import { NextRequest, NextResponse } from "next/server";
import { Novu } from "@novu/node";

const novu = new Novu(process.env.NOVU_API_KEY!);

export async function POST(req: NextRequest) {
    const { email, name, totalAmount } = await req.json();

    const { data } = await novu.trigger("novu-store", {
        to: {
            subscriberId: process.env.NOVU_SUBSCRIBER_ID!,
            email,
            firstName: name,
        },
        payload: {
            name,
            totalAmount,
            subject: `Purchase Notification from Novu Store`,
            message: `Your purchase of ${totalAmount} was successful! 🎉`,
        },
    });
    console.log(data.data);

    return NextResponse.json(
        {
            message: "Purchase Completed!",
            data: { novu: data.data },
            success: true,
        },
        { status: 200 }
    );
}

端點接受客戶的電子郵件、姓名和支付總額,並在付款成功後觸發 Novu 通知工作流程發送所需的通知。


結論

到目前為止,您已經學會如何執行以下操作:

  • 實施多種身份驗證方法,從 Appwrite 儲存和檢索資料和檔案。

  • 使用 React Email 建立電子郵件模板,並使用 Novu 發送應用程式內和電子郵件通知。

如果您希望在應用程式中發送通知,Novu 是您的最佳選擇。使用 Novu,您可以為應用程式加入多個通知管道,包括聊天、簡訊、電子郵件、推播和應用程式內通知。

本教學的源程式碼可在此處取得:

https://github.com/novuhq/ecom-store-with-nextjs-appwrite-novu-and-stripe

感謝您的閱讀!


原文出處:https://dev.to/novu/building-an-e-commerce-store-with-nextjs-49m


共有 0 則留言