برنامه نویسی

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

در این مقاله، صفحه ای با سطوح قیمت گذاری متعدد می سازیم.
بازدیدکنندگان می توانند دکمه را فشار دهند “خرید” را فشار دهید و به صفحه پرداخت بروید.

پس از تکمیل، ما مشتریان را به یک صفحه موفقیت ارسال می کنیم و آنها را در پایگاه داده خود ذخیره می کنیم.

این مورد استفاده می تواند در موارد زیر مفید باشد:

  • خرید دوره
  • خرید اشتراک
  • یک کالای فیزیکی بخرید
  • پول اهدا کنید
  • براتون یه قهوه بخرم😉

و بسیاری دیگر.

قیمت گذاری


می توانید به من کمک کنید؟ ❤️

من عاشق ساختن پروژه های متن باز و به اشتراک گذاری آنها با همه هستم.

اگر می توانید به من کمک کنید و ستاره پروژه من فوق العاده، فوق العاده سپاسگزار خواهم بود!

(این نیز کد منبع این آموزش است)
https://github.com/github-20k/growchief

stargif

گربه


بیا راه اندازیش کنیم🔥

بیایید با ایجاد یک پروژه 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 />
   </>
   )
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

قیمت گذاری


راه اندازی ارائه دهنده پرداخت خود را 🤑 💰

اکثر ارائه دهندگان پرداخت به همین روش کار می کنند.

  1. یک تماس API با مبلغی که می‌خواهید شارژ کنید و صفحه موفقیت برای ارسال کاربر پس از پرداخت به ارائه‌دهنده پرداخت ارسال کنید.

  2. شما یک URL از تماس API با پیوندی به صفحه پرداخت دریافت می کنید و کاربر را هدایت می کنید (کاربر که وب سایت شما را ترک می کند).

  3. پس از اتمام خرید، کاربر را به صفحه موفقیت هدایت می کند.

  4. ارائه‌دهنده پرداخت یک تماس API را به مسیر خاصی که انتخاب می‌کنید ارسال می‌کند تا به شما اطلاع دهد خرید انجام شده است (ناهمزمان)

من از Stripe استفاده می کنم – در درجه اول در همه جا قابل دسترسی است، اما با خیال راحت از ارائه دهنده پرداخت خود استفاده کنید.

به Stripe بروید، روی برگه توسعه‌دهنده کلیک کنید، به «کلیدهای API» بروید و کلیدهای عمومی و مخفی را از بخش توسعه‌دهنده کپی کنید.

راه راه

به ریشه پروژه خود بروید و یک فایل جدید به نام ایجاد کنید .env و دو کلید را به این صورت قرار دهید:

PAYMENT_PUBLIC_KEY=pk_test_....
PAYMENT_SECRET_KEY=sk_test_....
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

به خاطر دارید که گفتیم Stripe بعداً در مورد پرداخت موفقیت آمیز با یک درخواست HTTP به ما اطلاع می دهد؟

خوب … ما نیاز داریم

  1. مسیر دریافت درخواست از پرداخت را تنظیم کنید
  2. این مسیر را با یک کلید محافظت کنید

بنابراین در حالی که در داشبورد 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});
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

اینجا چه خبر است:

  1. ما یک نمونه Stripe جدید را با کلید SECRET از ما تنظیم کردیم .env فایل.
  2. ما مطمئن می شویم که METHOD از مسیر است GET.
  3. ما بررسی می کنیم که یک رشته پرس و جو دریافت کنیم price بالاتر از 0.
  4. ما یک تماس Stripe برای ایجاد URL پرداخت خط خطی برقرار می کنیم. ما 1 مورد خریدیم. شما احتمالا می توانید ببینید که unit_amount در 100 ضرب می شود. اگر 1 بفرستیم 0.01 دلار می شود. ضرب در صد به 1 دلار تبدیل می شود.
  5. ما ارسال می کنیم 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
}
وارد حالت تمام صفحه شوید

از حالت تمام صفحه خارج شوید

این کد برای تایید درخواست است. بیایید ببینیم اینجا چه خبر است:

  1. ما با تایپ واردات شروع می کنیم (به یاد داشته باشید که نصب کردیم stripe-event-types قبل از).
  2. ما یک نمونه جدید Stripe را با کلید مخفی خود تنظیم کردیم.
  3. ما به مسیر می گوییم که آن را به JSON تجزیه نکند زیرا Stripe درخواست را در قالب دیگری برای ما ارسال می کند.
  4. را استخراج می کنیم stripe-signature از سربرگ و استفاده از constructEvent تابعی برای تأیید اعتبار درخواست و اطلاع از رویدادی که Stripe برای ما ارسال کرده است.
  5. ما بررسی می کنیم که رویداد را دریافت کنیم checkout.session.async_payment_succeeded; اگر چیز دیگری دریافت کنیم، درخواست را نادیده می گیریم.
  6. اگر موفق شدیم، اما مشتری پرداخت نکرد، ما نیز به درخواست توجه نکردیم.
  7. ما جایی داریم که منطق خرید را بنویسیم.

پس از این بخش، این شانس شماست که منطق سفارشی خود را اضافه کنید. می تواند هر یک از موارد زیر باشد:

  • ثبت نام کاربر در یک خبرنامه
  • کاربر را در پایگاه داده ثبت کنید
  • اشتراک کاربر را فعال کنید
  • یک لینک با آدرس دوره برای کاربر ارسال کنید

و بسیاری دیگر.

برای مورد ما، کاربر را به 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

stargif

توضیحات تصویر

نوشته های مشابه

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

دکمه بازگشت به بالا