🦄 ساخت صفحه قیمت گذاری با NextJS 🤯 🤯

در این مقاله، صفحه ای با سطوح قیمت گذاری متعدد می سازیم.
بازدیدکنندگان می توانند دکمه را فشار دهند “خرید” را فشار دهید و به صفحه پرداخت بروید.
پس از تکمیل، ما مشتریان را به یک صفحه موفقیت ارسال می کنیم و آنها را در پایگاه داده خود ذخیره می کنیم.
این مورد استفاده می تواند در موارد زیر مفید باشد:
- خرید دوره
- خرید اشتراک
- یک کالای فیزیکی بخرید
- پول اهدا کنید
- براتون یه قهوه بخرم😉
و بسیاری دیگر.
می توانید به من کمک کنید؟ ❤️
من عاشق ساختن پروژه های متن باز و به اشتراک گذاری آنها با همه هستم.
اگر می توانید به من کمک کنید و ستاره پروژه من فوق العاده، فوق العاده سپاسگزار خواهم بود!
(این نیز کد منبع این آموزش است)
https://github.com/github-20k/growchief
بیا راه اندازیش کنیم🔥
بیایید با ایجاد یک پروژه NextJS جدید شروع کنیم:
npx create-next-app@latest
برای ایجاد پروژه کافی است چندین بار روی enter کلیک کنید.
من طرفدار بزرگ روتر App جدید Next.JS نیستم – بنابراین از نسخه قدیمی استفاده خواهم کرد pages
پوشه، اما با خیال راحت این کار را به روش خود انجام دهید.
بیایید جلو برویم و بسته های قیمت گذاری را اضافه کنیم.
بیایید یک پوشه جدید به نام اجزا بسازیم و جزء قیمت گذاری خود را اضافه کنیم.
mkdir components
cd components
touch pricing.component.tsx
و مطالب زیر را اضافه کنید:
export const PackagesComponent = () => {
return (
<div className="mt-28">
<h1 className="text-center text-6xl max-sm:text-5xl font-bold">
Packages
</h1>
<div className="flex sm:space-x-4 max-sm:space-y-4 max-sm:flex-col">
<div className="flex-1 text-xl mt-14 rounded-xl border border-[#4E67E5]/25 bg-[#080C23] p-10 w-full">
<div className="text-[#4d66e5]">Package one</div>
<div className="text-6xl my-5 font-light">$600</div>
<div>
Short description
</div>
<button
className="my-5 w-full text-black p-5 max-sm:p-2 rounded-3xl bg-[#4E67E5] text-xl max-sm:text-lg hover:bg-[#8a9dfc] transition-all"
>
Purchase
</button>
<ul>
<li>First feature</li>
<li>Second feature</li>
</ul>
</div>
<div
className="flex-1 text-xl mt-14 rounded-xl border border-[#9966FF]/25 bg-[#120d1d] p-10 w-full"
>
<div className="text-[#9967FF]">Package 2</div>
<div className="text-6xl my-5 font-light">$1500</div>
<div>
Short Description
</div>
<button
className="my-5 w-full text-black p-5 max-sm:p-2 rounded-3xl bg-[#9966FF] text-xl max-sm:text-lg hover:bg-[#BB99FF] transition-all"
>
Purchase
</button>
<ul>
<li>First Feature</li>
<li>Second Feature</li>
<li>Thired Feature</li>
</ul>
</div>
<div
className="flex-1 text-xl mt-14 rounded-xl border border-[#F7E16F]/25 bg-[#19170d] p-10 w-full"
>
<div className="text-[#F7E16F]">Package 3</div>
<div className="text-6xl my-5 font-light">$1800</div>
<div>
Short Description
</div>
<button
className="my-5 w-full text-black p-5 max-sm:p-2 rounded-3xl bg-[#F7E16F] text-xl max-sm:text-lg hover:bg-[#fdf2bb] transition-all"
>
Purchase
</button>
<ul>
<li>First Feature</li>
<li>Second Feature</li>
<li>Thired Feature</li>
<li>Fourth Feature</li>
<li>Fifth Feature</li>
</ul>
</div>
</div>
</div>
);
};
این یک جزء بسیار ساده با Tailwind (CSS) برای نمایش سه نوع بسته (600 دلار، 1500 دلار و 1800 دلار) است. پس از کلیک بر روی هر یک از بسته ها، بازدید کننده را به صفحه خرید منتقل می کنیم تا بتواند بسته را خریداری کند.
به root dir بروید و یک صفحه فهرست جدید ایجاد کنید (اگر وجود نداشته باشد)
cd pages
touch index.tsx
کد زیر را به فایل اضافه کنید:
import React from 'react';
import {PackagesComponent} from '../components/pricing.component';
const Index = () => {
return (
<>
<PackagesComponent />
</>
)
}
راه اندازی ارائه دهنده پرداخت خود را 🤑 💰
اکثر ارائه دهندگان پرداخت به همین روش کار می کنند.
-
یک تماس API با مبلغی که میخواهید شارژ کنید و صفحه موفقیت برای ارسال کاربر پس از پرداخت به ارائهدهنده پرداخت ارسال کنید.
-
شما یک URL از تماس API با پیوندی به صفحه پرداخت دریافت می کنید و کاربر را هدایت می کنید (کاربر که وب سایت شما را ترک می کند).
-
پس از اتمام خرید، کاربر را به صفحه موفقیت هدایت می کند.
-
ارائهدهنده پرداخت یک تماس API را به مسیر خاصی که انتخاب میکنید ارسال میکند تا به شما اطلاع دهد خرید انجام شده است (ناهمزمان)
من از Stripe استفاده می کنم – در درجه اول در همه جا قابل دسترسی است، اما با خیال راحت از ارائه دهنده پرداخت خود استفاده کنید.
به Stripe بروید، روی برگه توسعهدهنده کلیک کنید، به «کلیدهای API» بروید و کلیدهای عمومی و مخفی را از بخش توسعهدهنده کپی کنید.
به ریشه پروژه خود بروید و یک فایل جدید به نام ایجاد کنید .env
و دو کلید را به این صورت قرار دهید:
PAYMENT_PUBLIC_KEY=pk_test_....
PAYMENT_SECRET_KEY=sk_test_....
به خاطر دارید که گفتیم Stripe بعداً در مورد پرداخت موفقیت آمیز با یک درخواست HTTP به ما اطلاع می دهد؟
خوب … ما نیاز داریم
- مسیر دریافت درخواست از پرداخت را تنظیم کنید
- این مسیر را با یک کلید محافظت کنید
بنابراین در حالی که در داشبورد Stripe هستید، به «Webhooks» بروید و یک وب هوک جدید ایجاد کنید.
شما باید یک “URL نقطه پایانی” اضافه کنید. از آنجایی که ما پروژه را به صورت محلی اجرا می کنیم، Stripe تنها زمانی می تواند درخواستی را برای ما ارسال کند که یک شنونده محلی ایجاد کنیم یا وب سایت خود را با ngrok در معرض وب قرار دهیم.
من گزینه ngrok را ترجیح می دهم زیرا، به دلایلی، شنونده محلی همیشه برای من کار نمی کند (گاهی رویدادها را ارسال می کند، گاهی اوقات نه).
بنابراین در حالی که پروژه Next.JS شما اجرا می شود، فقط دستورات زیر را اجرا کنید.
npm install -g ngrok
ngrok http 3000
و خواهید دید که نگروک به وب سایت شما در دامنه خود سرویس می دهد. فقط کپیش کن
و آن را در وب هوک Stripe “Endpoint URL” قرار دهید، همچنین مسیر تکمیل خرید را اضافه کنید /api/purchase
پس از آن، روی “انتخاب رویدادها” کلیک کنید.
“checkout.session.async_payment_succeeded” و “checkout.session.completed” را انتخاب کنید.
روی «افزودن رویداد» و سپس «افزودن نقطه پایانی» کلیک کنید.
روی رویداد ایجاد شده کلیک کنید
روی “Reveal” روی “Signing Key” کلیک کنید.
آن را کپی کرده و باز کنید .env
، و اضافه کنید
PAYMENT_SIGNING_SECRET=key
ارسال کاربران به صفحه پرداخت 🚀
بیایید با نصب Stripe و همچنین برخی از انواع (چون من از تایپ اسکریپت استفاده می کنم) شروع کنیم.
npm install stripe --save
npm install -D stripe-event-types
بیایید یک مسیر API جدید برای ایجاد پیوند URL پرداخت برای کاربران خود، بسته به قیمت ایجاد کنیم.
cd pages/api
touch prepare.tsx
اینم محتوای فایل:
import type { NextApiRequest, NextApiResponse } from 'next'
const stripe = new Stripe(process.env.PAYMENT_SECRET_KEY!, {} as any);
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
if (req.method !== 'GET') {
return res.status(405).json({error: "Method not allowed"});
}
if (!req.query.price || +req.query.price <= 0) {
return res.status(400).json({error: "Please enter a valid price"});
}
const { url } = await stripe.checkout.sessions.create({
payment_method_types: ["card"],
line_items: [
{
price_data: {
currency: "USD",
product_data: {
name: "GrowChief",
description: `Charging you`,
},
unit_amount: 100 * +req.query.price,
},
quantity: 1,
},
],
mode: "payment",
success_url: "http://localhost:3000/success?session_id={CHECKOUT_SESSION_ID}",
cancel_url: "http://localhost:3000",
});
return req.json({url});
}
اینجا چه خبر است:
- ما یک نمونه Stripe جدید را با کلید SECRET از ما تنظیم کردیم
.env
فایل. - ما مطمئن می شویم که
METHOD
از مسیر استGET.
- ما بررسی می کنیم که یک رشته پرس و جو دریافت کنیم
price
بالاتر از 0. - ما یک تماس Stripe برای ایجاد URL پرداخت خط خطی برقرار می کنیم. ما 1 مورد خریدیم. شما احتمالا می توانید ببینید که
unit_amount
در 100 ضرب می شود. اگر 1 بفرستیم 0.01 دلار می شود. ضرب در صد به 1 دلار تبدیل می شود. - ما ارسال می کنیم
URL
برگشت به مشتری
بیایید پشت خود را باز کنیم packages.component.tsx
جزء و فراخوانی api را اضافه کنید.
const purchase = useCallback(async (price: number) => {
const {url} = await (await fetch(`http://localhost:3000/api/prepare?price=${price}`)).json();
window.location.href = url;
}, []);
و برای کد کامل صفحه
export const PackagesComponent = () => {
const purchase = useCallback(async (price: number) => {
const {url} = await (await fetch(`http://localhost:3000/api/prepare?price=${price}`)).json();
window.location.href = url;
}, []);
return (
<div className="mt-28">
<h1 className="text-center text-6xl max-sm:text-5xl font-bold">
Packages
</h1>
<div className="flex sm:space-x-4 max-sm:space-y-4 max-sm:flex-col">
<div className="flex-1 text-xl mt-14 rounded-xl border border-[#4E67E5]/25 bg-[#080C23] p-10 w-full">
<div className="text-[#4d66e5]">Package one</div>
<div className="text-6xl my-5 font-light">$600</div>
<div>
Short description
</div>
<button onClick={() => purchase(600)}
className="my-5 w-full text-black p-5 max-sm:p-2 rounded-3xl bg-[#4E67E5] text-xl max-sm:text-lg hover:bg-[#8a9dfc] transition-all"
>
Purchase
</button>
<ul>
<li>First feature</li>
<li>Second feature</li>
</ul>
</div>
<div
className="flex-1 text-xl mt-14 rounded-xl border border-[#9966FF]/25 bg-[#120d1d] p-10 w-full"
>
<div className="text-[#9967FF]">Package 2</div>
<div className="text-6xl my-5 font-light">$1500</div>
<div>
Short Description
</div>
<button onClick={() => purchase(1200)}
className="my-5 w-full text-black p-5 max-sm:p-2 rounded-3xl bg-[#9966FF] text-xl max-sm:text-lg hover:bg-[#BB99FF] transition-all"
>
Purchase
</button>
<ul>
<li>First Feature</li>
<li>Second Feature</li>
<li>Thired Feature</li>
</ul>
</div>
<div
className="flex-1 text-xl mt-14 rounded-xl border border-[#F7E16F]/25 bg-[#19170d] p-10 w-full"
>
<div className="text-[#F7E16F]">Package 3</div>
<div className="text-6xl my-5 font-light">$1800</div>
<div>
Short Description
</div>
<button onClick={() => purchase(1800)}
className="my-5 w-full text-black p-5 max-sm:p-2 rounded-3xl bg-[#F7E16F] text-xl max-sm:text-lg hover:bg-[#fdf2bb] transition-all"
>
Purchase
</button>
<ul>
<li>First Feature</li>
<li>Second Feature</li>
<li>Thired Feature</li>
<li>Fourth Feature</li>
<li>Fifth Feature</li>
</ul>
</div>
</div>
</div>
);
};
اضافه کرده ایم onClick
در هر دکمه از صفحه با قیمت مناسب برای ایجاد صفحه پرداخت.
تصور ذهنم را به هم ریخت🤯
Notion یک ابزار عالی برای دانش و مستندسازی است.
من بیش از یک سال است که برای Novu کار می کنم و در درجه اول برای تیم خود از Novu استفاده می کنم.
اگر تا به حال با Notion بازی کرده باشید، احتمالا متوجه شده اید که آنها یک ویرایشگر نرم دارند – یکی از بهترین هایی که تا به حال با آن بازی کرده ام (حداقل برای من).
من متوجه شده ام که شما می توانید از محتوای مفهومی با یک API استفاده کنید.
من یک حساب کاربری بدون مفهوم باز کردم و بیرون رفتم تا قیمت آنها را بررسی کنم – مطمئن بودم که آنها API را برای ردیف رایگان خود ارائه نمی دهند. من خیلی اشتباه کردم، آنها این کار را می کنند، و فوق العاده سریع است.
مهم ترین محدودیت آنها این است که آنها به شما اجازه می دهند حداکثر 3 درخواست در ثانیه ارسال کنید – اما اگر وب سایت خود را کش کنید مشکل بزرگی نیست. getStaticProps.
در حال پردازش درخواست خرید و افزودن سرنخ ها به Notion 🙋🏻♂️
به خاطر دارید که ما یک وب هوک برای Stripe تنظیم کردیم تا پس از تکمیل پرداخت، درخواستی را برای ما ارسال کند؟
بیایید این درخواست را بسازیم، آن را تأیید کنیم و مشتری را به Notion اضافه کنیم.
از آنجایی که درخواست بخشی از سفر کاربر نیست و در مسیر دیگری قرار دارد، در معرض دید عموم قرار می گیرد.
این بدان معنی است که ما باید از این مسیر محافظت کنیم – Stripe یک راه عالی برای تأیید اعتبار آن با Express ارائه می دهد، اما از آنجایی که ما از NextJS استفاده می کنیم، باید کمی آن را تغییر دهیم، بنابراین اجازه دهید با نصب Micro شروع کنیم.
npm install micro@^10.0.1
و یک مسیر جدید برای خرید باز کنید:
cd pages
touch purchase.tsx
آن را باز کنید و کد زیر را اضافه کنید:
/// <reference types="stripe-event-types" />
import Stripe from "stripe";
import { buffer } from "micro";
import type { NextApiRequest, NextApiResponse } from "next";
const stripe = new Stripe(process.env.PAYMENT_SECRET_KEY!, {} as any);
export const config = { api: { bodyParser: false } };
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const signature = req.headers["stripe-signature"] as string;
const reqBuffer = await buffer(req);
const event = stripe.webhooks.constructEvent(
reqBuffer,
signature,
process.env.PAYMENT_SIGNING_SECRET!
) as Stripe.DiscriminatedEvent;
if (
event.type !== "checkout.session.async_payment_succeeded" &&
event.type !== "checkout.session.completed"
) {
res.json({invalid: true});
return;
}
if (event?.data?.object?.payment_status !== "paid") {
res.json({invalid: true});
return;
}
/// request is valid, let's add it to notion
}
این کد برای تایید درخواست است. بیایید ببینیم اینجا چه خبر است:
- ما با تایپ واردات شروع می کنیم (به یاد داشته باشید که نصب کردیم
stripe-event-types
قبل از). - ما یک نمونه جدید Stripe را با کلید مخفی خود تنظیم کردیم.
- ما به مسیر می گوییم که آن را به JSON تجزیه نکند زیرا Stripe درخواست را در قالب دیگری برای ما ارسال می کند.
- را استخراج می کنیم
stripe-signature
از سربرگ و استفاده ازconstructEvent
تابعی برای تأیید اعتبار درخواست و اطلاع از رویدادی که Stripe برای ما ارسال کرده است. - ما بررسی می کنیم که رویداد را دریافت کنیم
checkout.session.async_payment_succeeded
; اگر چیز دیگری دریافت کنیم، درخواست را نادیده می گیریم. - اگر موفق شدیم، اما مشتری پرداخت نکرد، ما نیز به درخواست توجه نکردیم.
- ما جایی داریم که منطق خرید را بنویسیم.
پس از این بخش، این شانس شماست که منطق سفارشی خود را اضافه کنید. می تواند هر یک از موارد زیر باشد:
- ثبت نام کاربر در یک خبرنامه
- کاربر را در پایگاه داده ثبت کنید
- اشتراک کاربر را فعال کنید
- یک لینک با آدرس دوره برای کاربر ارسال کنید
و بسیاری دیگر.
برای مورد ما، کاربر را به Notion اضافه می کنیم.
راه اندازی مفهوم ✍🏻
قبل از بازی با Notion، بیایید یک ادغام Notion جدید ایجاد کنیم.
به «ادغامهای من» بروید.
https://www.notion.so/my-integrations
و روی “یکپارچه سازی جدید” کلیک کنید
پس از آن فقط هر نامی را اضافه کنید و روی ارسال کلیک کنید
روی Show کلیک کنید و کلید را کپی کنید
به فایل .env خود بروید و کلید جدید را اضافه کنید
NOTION_KEY=secret_...
بیایید به مفهوم سر بزنیم و یک پایگاه داده جدید ایجاد کنیم
این پایگاه داده در معرض API قرار نمی گیرد مگر اینکه آن را مشخص کنیم، بنابراین روی “…” و سپس “Add connections” کلیک کنید و روی ادغام ایجاد شده جدید کلیک کنید.
پس از انجام این کار، شناسه پایگاه داده را کپی کرده و آن را به خود اضافه کنید .env
فایل.
NOTION_CUSTOMERS_DB=your_id
اکنون می توانید با فیلد موجود در پایگاه داده به هر شکلی که می خواهید بازی کنید.
من فیلد “نام” را نگه می دارم و نام مشتری را از خرید Stripe اضافه می کنم.
اجازه ندهید با اجرا کردن، برنامه مشتری را نصب کنیم
npm install @notionhq/client --save
بیایید منطق اضافه کردن نام مشتری به پایگاه داده خود را بنویسیم.
import { Client } from "@notionhq/client";
const notion = new Client({
auth: process.env.NOTION_KEY,
});
await notion.pages.create({
parent: {
database_id: process.env.NOTION_CUSTOMERS_DB!,
},
properties: {
Name: {
title: [
{
text: {
content: event?.data?.object?.customer_details?.name,
},
},
],
},
},
});
این کد بسیار ساده است.
ما یک نمونه Notion جدید را با کلید مخفی Notion تنظیم می کنیم و سپس یک ردیف جدید در پایگاه داده خود با نام مشتری ایجاد می کنیم.
و کد خرید کامل:
/// <reference types="stripe-event-types" />
import Stripe from "stripe";
import { buffer } from "micro";
import type { NextApiRequest, NextApiResponse } from "next";
import { Client } from "@notionhq/client";
const notion = new Client({
auth: process.env.NOTION_KEY,
});
const stripe = new Stripe(process.env.PAYMENT_SECRET_KEY!, {} as any);
export const config = { api: { bodyParser: false } };
export default async function handler(
req: NextApiRequest,
res: NextApiResponse
) {
const signature = req.headers["stripe-signature"] as string;
const reqBuffer = await buffer(req);
const event = stripe.webhooks.constructEvent(
reqBuffer,
signature,
process.env.PAYMENT_SIGNING_SECRET!
) as Stripe.DiscriminatedEvent;
if (
event.type !== "checkout.session.async_payment_succeeded" &&
event.type !== "checkout.session.completed"
) {
res.json({invalid: true});
return;
}
if (event?.data?.object?.payment_status !== "paid") {
res.json({invalid: true});
return;
}
await notion.pages.create({
parent: {
database_id: process.env.NOTION_CUSTOMERS_DB!,
},
properties: {
Name: {
title: [
{
text: {
content: event?.data?.object?.customer_details?.name,
},
},
],
},
},
});
res.json({success: true});
}
شما باید چیزی مشابه این داشته باشید:
میخکوب کردی 🚀
همین.
می توانید کل کد منبع را در اینجا پیدا کنید:
https://github.com/github-20k/growchief
در آنجا چیزهای بیشتری پیدا خواهید کرد، مانند
- نمایش تجزیه و تحلیل DEV.TO
- جمع آوری اطلاعات از Notion و نمایش آن در وب سایت (سبک CMS)
- کل جریان خرید
می توانید به من کمک کنید؟ ❤️
امیدوارم این آموزش برای شما مفید بوده باشد 🚀
هر ستاره ای که بتوانید به من بدهید کمک بزرگی به من خواهد کرد
https://github.com/github-20k/growchief