Stripe + Next.js — دليل شامل لتكامل المدفوعات 2026

AI Bot
بواسطة AI Bot ·

جاري تحميل مشغل تحويل النص إلى كلام الصوتي...

المتطلبات الأساسية

قبل البدء في هذا الدليل، تأكد من توفر:

  • Node.js 20+ مثبت
  • خبرة مع Next.js 15 (App Router)
  • حساب Stripe (مجاني الإنشاء على stripe.com)
  • معرفة أساسية بـ TypeScript و React
  • محرر أكواد (يُنصح بـ VS Code)

لا تحتاج إلى كيان تجاري لاختبار المدفوعات — يوفر Stripe وضع اختبار كامل مع بطاقات محاكاة.

ما ستبنيه

بنهاية هذا الدليل، سيكون لديك نظام دفع كامل يشمل:

  • مدفوعات لمرة واحدة عبر Stripe Checkout
  • اشتراكات متكررة مع مستويات تسعير متعددة
  • معالجة الويب هوكس لأحداث الدفع في الوقت الفعلي
  • بوابة العميل لإدارة الاشتراكات ذاتيًا
  • التحقق من جانب الخادم لحالة الدفع
  • مسارات API آمنة النوع باستخدام Next.js App Router

الخطوة 1: إعداد المشروع

أنشئ مشروع Next.js جديد وثبّت التبعيات:

npx create-next-app@latest stripe-payments --typescript --tailwind --app --src-dir
cd stripe-payments

ثبّت حزم Stripe:

npm install stripe @stripe/stripe-js
  • stripe — حزمة Stripe من جانب الخادم لـ Node.js
  • @stripe/stripe-js — محمّل Stripe.js من جانب العميل

الخطوة 2: تكوين متغيرات البيئة

أنشئ ملف .env.local في جذر المشروع:

# مفاتيح Stripe (استخدم مفاتيح الاختبار أثناء التطوير)
STRIPE_SECRET_KEY=sk_test_...
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_...
STRIPE_WEBHOOK_SECRET=whsec_...
 
# رابط التطبيق
NEXT_PUBLIC_APP_URL=http://localhost:3000

ابحث عن مفاتيحك في لوحة تحكم Stripe تحت Developers → API keys. استخدم دائمًا المفاتيح المسبوقة بـ sk_test_ و pk_test_ للتطوير.

مهم: لا تكشف أبدًا مفتاحك السري من جانب العميل. فقط المتغيرات المسبوقة بـ NEXT_PUBLIC_ متاحة في المتصفح.

الخطوة 3: تهيئة عميل Stripe

أنشئ نسخة Stripe مشتركة للاستخدام من جانب الخادم:

// src/lib/stripe.ts
import Stripe from "stripe";
 
export const stripe = new Stripe(process.env.STRIPE_SECRET_KEY!, {
  apiVersion: "2025-12-18.acacia",
  typescript: true,
});

أنشئ محمّلًا من جانب العميل:

// src/lib/stripe-client.ts
import { loadStripe } from "@stripe/stripe-js";
 
let stripePromise: ReturnType<typeof loadStripe>;
 
export function getStripe() {
  if (!stripePromise) {
    stripePromise = loadStripe(
      process.env.NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY!
    );
  }
  return stripePromise;
}

الخطوة 4: إنشاء المنتجات والأسعار في Stripe

يمكنك إنشاء المنتجات عبر لوحة تحكم Stripe أو برمجيًا. في هذا الدليل، أنشئها عبر لوحة التحكم:

  1. اذهب إلى Products في لوحة تحكم Stripe
  2. اضغط على Add product
  3. أنشئ منتجًا باسم "خطة Pro" بسعرين:
    • شهري: 19$/شهر (متكرر)
    • سنوي: 190$/سنة (متكرر)
  4. أنشئ منتجًا آخر باسم "خطة Enterprise":
    • شهري: 49$/شهر (متكرر)
    • سنوي: 490$/سنة (متكرر)

دوّن معرّفات الأسعار (تبدأ بـ price_). أضفها إلى بيئتك:

# أضف إلى .env.local
STRIPE_PRO_MONTHLY_PRICE_ID=price_...
STRIPE_PRO_YEARLY_PRICE_ID=price_...
STRIPE_ENTERPRISE_MONTHLY_PRICE_ID=price_...
STRIPE_ENTERPRISE_YEARLY_PRICE_ID=price_...

الخطوة 5: بناء صفحة التسعير

أنشئ مكون تسعير يعرض خططك:

// src/app/pricing/page.tsx
import { CheckoutButton } from "@/components/checkout-button";
 
const plans = [
  {
    name: "Pro",
    description: "للفرق النامية",
    monthlyPrice: 19,
    yearlyPrice: 190,
    monthlyPriceId: process.env.STRIPE_PRO_MONTHLY_PRICE_ID!,
    yearlyPriceId: process.env.STRIPE_PRO_YEARLY_PRICE_ID!,
    features: [
      "مشاريع غير محدودة",
      "دعم ذو أولوية",
      "تحليلات متقدمة",
      "وصول API",
    ],
  },
  {
    name: "Enterprise",
    description: "للمؤسسات الكبيرة",
    monthlyPrice: 49,
    yearlyPrice: 490,
    monthlyPriceId: process.env.STRIPE_ENTERPRISE_MONTHLY_PRICE_ID!,
    yearlyPriceId: process.env.STRIPE_ENTERPRISE_YEARLY_PRICE_ID!,
    features: [
      "كل ما في Pro",
      "مصادقة SSO",
      "تكاملات مخصصة",
      "دعم مخصص",
      "ضمان SLA",
    ],
  },
];
 
export default function PricingPage() {
  return (
    <div className="max-w-4xl mx-auto py-16 px-4">
      <h1 className="text-4xl font-bold text-center mb-4">
        تسعير بسيط وشفاف
      </h1>
      <p className="text-gray-600 text-center mb-12">
        اختر الخطة التي تناسب احتياجاتك
      </p>
 
      <div className="grid md:grid-cols-2 gap-8">
        {plans.map((plan) => (
          <div
            key={plan.name}
            className="border rounded-2xl p-8 shadow-sm hover:shadow-md transition"
          >
            <h2 className="text-2xl font-bold">{plan.name}</h2>
            <p className="text-gray-500 mt-2">{plan.description}</p>
            <p className="text-4xl font-bold mt-6">
              {plan.monthlyPrice}$
              <span className="text-base font-normal text-gray-500">
                /شهر
              </span>
            </p>
 
            <ul className="mt-6 space-y-3">
              {plan.features.map((feature) => (
                <li key={feature} className="flex items-center gap-2">
                  <span className="text-green-500"></span>
                  {feature}
                </li>
              ))}
            </ul>
 
            <CheckoutButton priceId={plan.monthlyPriceId} planName={plan.name} />
          </div>
        ))}
      </div>
    </div>
  );
}

الخطوة 6: إنشاء مسار API للدفع

ابنِ مسار API من جانب الخادم ينشئ جلسة Stripe Checkout:

// src/app/api/checkout/route.ts
import { NextRequest, NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
 
export async function POST(req: NextRequest) {
  try {
    const { priceId, mode = "subscription" } = await req.json();
 
    if (!priceId) {
      return NextResponse.json(
        { error: "معرّف السعر مطلوب" },
        { status: 400 }
      );
    }
 
    const session = await stripe.checkout.sessions.create({
      mode: mode as "subscription" | "payment",
      payment_method_types: ["card"],
      line_items: [
        {
          price: priceId,
          quantity: 1,
        },
      ],
      success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
      billing_address_collection: "required",
      allow_promotion_codes: true,
    });
 
    return NextResponse.json({ url: session.url });
  } catch (error) {
    console.error("خطأ في الدفع:", error);
    return NextResponse.json(
      { error: "فشل في إنشاء جلسة الدفع" },
      { status: 500 }
    );
  }
}

الخطوة 7: بناء مكون زر الدفع

أنشئ مكون العميل الذي يعيد توجيه المستخدمين إلى Stripe Checkout:

// src/components/checkout-button.tsx
"use client";
 
import { useState } from "react";
 
interface CheckoutButtonProps {
  priceId: string;
  planName: string;
}
 
export function CheckoutButton({ priceId, planName }: CheckoutButtonProps) {
  const [loading, setLoading] = useState(false);
 
  async function handleCheckout() {
    setLoading(true);
    try {
      const response = await fetch("/api/checkout", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ priceId }),
      });
 
      const data = await response.json();
 
      if (data.url) {
        window.location.href = data.url;
      } else {
        throw new Error(data.error || "فشل في إنشاء جلسة الدفع");
      }
    } catch (error) {
      console.error("خطأ في الدفع:", error);
      alert("حدث خطأ ما. يرجى المحاولة مرة أخرى.");
    } finally {
      setLoading(false);
    }
  }
 
  return (
    <button
      onClick={handleCheckout}
      disabled={loading}
      className="w-full mt-8 bg-black text-white py-3 rounded-lg font-medium
                 hover:bg-gray-800 disabled:opacity-50 disabled:cursor-not-allowed
                 transition"
    >
      {loading ? "جاري التحويل..." : `الاشتراك في ${planName}`}
    </button>
  );
}

الخطوة 8: معالجة الويب هوكس

الويب هوكس ضرورية — فهي تُخطر تطبيقك عند نجاح المدفوعات أو تجديد الاشتراكات أو فشل المدفوعات. هذا هو العمود الفقري لنظام دفع موثوق.

// src/app/api/webhooks/stripe/route.ts
import { NextRequest, NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
import Stripe from "stripe";
 
export async function POST(req: NextRequest) {
  const body = await req.text();
  const signature = req.headers.get("stripe-signature")!;
 
  let event: Stripe.Event;
 
  try {
    event = stripe.webhooks.constructEvent(
      body,
      signature,
      process.env.STRIPE_WEBHOOK_SECRET!
    );
  } catch (err) {
    console.error("فشل التحقق من توقيع الويب هوك:", err);
    return NextResponse.json(
      { error: "توقيع غير صالح" },
      { status: 400 }
    );
  }
 
  try {
    switch (event.type) {
      case "checkout.session.completed": {
        const session = event.data.object as Stripe.Checkout.Session;
        await handleCheckoutComplete(session);
        break;
      }
 
      case "invoice.paid": {
        const invoice = event.data.object as Stripe.Invoice;
        await handleInvoicePaid(invoice);
        break;
      }
 
      case "invoice.payment_failed": {
        const invoice = event.data.object as Stripe.Invoice;
        await handlePaymentFailed(invoice);
        break;
      }
 
      case "customer.subscription.deleted": {
        const subscription = event.data.object as Stripe.Subscription;
        await handleSubscriptionCancelled(subscription);
        break;
      }
 
      case "customer.subscription.updated": {
        const subscription = event.data.object as Stripe.Subscription;
        await handleSubscriptionUpdated(subscription);
        break;
      }
 
      default:
        console.log(`نوع حدث غير معالج: ${event.type}`);
    }
 
    return NextResponse.json({ received: true });
  } catch (error) {
    console.error("خطأ في معالج الويب هوك:", error);
    return NextResponse.json(
      { error: "فشل معالج الويب هوك" },
      { status: 500 }
    );
  }
}
 
async function handleCheckoutComplete(session: Stripe.Checkout.Session) {
  const customerId = session.customer as string;
  const subscriptionId = session.subscription as string;
 
  console.log(
    `اكتمل الدفع للعميل ${customerId}، الاشتراك ${subscriptionId}`
  );
 
  // TODO: حفظ في قاعدة البيانات
  // await db.user.update({
  //   where: { stripeCustomerId: customerId },
  //   data: { subscriptionId, subscriptionStatus: "active" },
  // });
}
 
async function handleInvoicePaid(invoice: Stripe.Invoice) {
  const customerId = invoice.customer as string;
  console.log(`تم دفع الفاتورة للعميل ${customerId}`);
 
  // TODO: تحديث فترة الاشتراك
}
 
async function handlePaymentFailed(invoice: Stripe.Invoice) {
  const customerId = invoice.customer as string;
  console.log(`فشل الدفع للعميل ${customerId}`);
 
  // TODO: إرسال بريد إلكتروني للعميل
}
 
async function handleSubscriptionCancelled(subscription: Stripe.Subscription) {
  const customerId = subscription.customer as string;
  console.log(`تم إلغاء الاشتراك للعميل ${customerId}`);
 
  // TODO: إلغاء الوصول في قاعدة البيانات
}
 
async function handleSubscriptionUpdated(subscription: Stripe.Subscription) {
  const customerId = subscription.customer as string;
  const status = subscription.status;
  console.log(
    `تم تحديث الاشتراك للعميل ${customerId}: ${status}`
  );
 
  // TODO: تحديث تفاصيل الخطة
}

مهم: مسار الويب هوك يقرأ نص الطلب الخام كنص (وليس JSON) لأن Stripe يحتاج الحمولة الخام للتحقق من التوقيع.

الخطوة 9: إعداد اختبار الويب هوكس محليًا

ثبّت Stripe CLI لاختبار الويب هوكس محليًا:

# macOS
brew install stripe/stripe-cli/stripe
 
# تسجيل الدخول إلى حساب Stripe
stripe login
 
# تحويل الويب هوكس إلى خادمك المحلي
stripe listen --forward-to localhost:3000/api/webhooks/stripe

سيعرض CLI مفتاح توقيع الويب هوك (whsec_...). انسخه إلى .env.local كـ STRIPE_WEBHOOK_SECRET.

الآن عند إكمال عملية دفع تجريبية، ستُحوَّل الأحداث إلى معالج الويب هوكس المحلي.

الخطوة 10: بناء صفحة النجاح

أنشئ صفحة تؤكد الدفع وتعرض تفاصيل الاشتراك:

// src/app/success/page.tsx
import { stripe } from "@/lib/stripe";
import { redirect } from "next/navigation";
import Link from "next/link";
 
interface SuccessPageProps {
  searchParams: Promise<{ session_id?: string }>;
}
 
export default async function SuccessPage({ searchParams }: SuccessPageProps) {
  const { session_id } = await searchParams;
 
  if (!session_id) {
    redirect("/pricing");
  }
 
  const session = await stripe.checkout.sessions.retrieve(session_id, {
    expand: ["subscription", "line_items"],
  });
 
  if (session.payment_status !== "paid") {
    redirect("/pricing");
  }
 
  const subscription = session.subscription as Stripe.Subscription | null;
 
  return (
    <div className="max-w-lg mx-auto py-16 px-4 text-center">
      <div className="text-6xl mb-6">🎉</div>
      <h1 className="text-3xl font-bold mb-4">تم الدفع بنجاح!</h1>
      <p className="text-gray-600 mb-8">
        شكرًا لاشتراكك. تم ترقية حسابك.
      </p>
 
      {subscription && (
        <div className="bg-gray-50 rounded-lg p-6 mb-8 text-right">
          <h2 className="font-semibold mb-2">تفاصيل الاشتراك</h2>
          <p className="text-sm text-gray-600">
            الحالة: <span className="capitalize">{subscription.status}</span>
          </p>
          <p className="text-sm text-gray-600">
            تنتهي الفترة الحالية في:{" "}
            {new Date(
              subscription.current_period_end * 1000
            ).toLocaleDateString("ar-SA")}
          </p>
        </div>
      )}
 
      <Link
        href="/dashboard"
        className="inline-block bg-black text-white px-6 py-3 rounded-lg
                   hover:bg-gray-800 transition"
      >
        الذهاب إلى لوحة التحكم
      </Link>
    </div>
  );
}

الخطوة 11: إضافة بوابة العميل للإدارة الذاتية

تتيح بوابة عميل Stripe للمستخدمين إدارة اشتراكاتهم بأنفسهم — الترقية والتخفيض والإلغاء وتحديث طرق الدفع.

أولًا، كوّن البوابة في لوحة تحكم Stripe تحت Settings → Billing → Customer portal.

ثم أنشئ مسار API لتوليد جلسات البوابة:

// src/app/api/portal/route.ts
import { NextRequest, NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
 
export async function POST(req: NextRequest) {
  try {
    const { customerId } = await req.json();
 
    if (!customerId) {
      return NextResponse.json(
        { error: "معرّف العميل مطلوب" },
        { status: 400 }
      );
    }
 
    const session = await stripe.billingPortal.sessions.create({
      customer: customerId,
      return_url: `${process.env.NEXT_PUBLIC_APP_URL}/dashboard`,
    });
 
    return NextResponse.json({ url: session.url });
  } catch (error) {
    console.error("خطأ في البوابة:", error);
    return NextResponse.json(
      { error: "فشل في إنشاء جلسة البوابة" },
      { status: 500 }
    );
  }
}

أنشئ مكون زر للوصول إلى البوابة:

// src/components/manage-subscription-button.tsx
"use client";
 
import { useState } from "react";
 
export function ManageSubscriptionButton({
  customerId,
}: {
  customerId: string;
}) {
  const [loading, setLoading] = useState(false);
 
  async function handleManage() {
    setLoading(true);
    try {
      const response = await fetch("/api/portal", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ customerId }),
      });
 
      const data = await response.json();
      if (data.url) {
        window.location.href = data.url;
      }
    } catch (error) {
      console.error("خطأ في البوابة:", error);
    } finally {
      setLoading(false);
    }
  }
 
  return (
    <button
      onClick={handleManage}
      disabled={loading}
      className="bg-gray-100 text-gray-700 px-4 py-2 rounded-lg
                 hover:bg-gray-200 disabled:opacity-50 transition"
    >
      {loading ? "جاري التحميل..." : "إدارة الاشتراك"}
    </button>
  );
}

الخطوة 12: التحقق من حالة الاشتراك من جانب الخادم

أنشئ أداة مساعدة للتحقق من حالة الاشتراك قبل منح الوصول للميزات المميزة:

// src/lib/subscription.ts
import { stripe } from "./stripe";
 
export type SubscriptionStatus =
  | "active"
  | "trialing"
  | "past_due"
  | "cancelled"
  | "none";
 
export async function getSubscriptionStatus(
  customerId: string
): Promise<SubscriptionStatus> {
  try {
    const subscriptions = await stripe.subscriptions.list({
      customer: customerId,
      status: "all",
      limit: 1,
    });
 
    if (subscriptions.data.length === 0) {
      return "none";
    }
 
    const subscription = subscriptions.data[0];
 
    switch (subscription.status) {
      case "active":
        return "active";
      case "trialing":
        return "trialing";
      case "past_due":
        return "past_due";
      case "canceled":
        return "cancelled";
      default:
        return "none";
    }
  } catch (error) {
    console.error("خطأ في جلب الاشتراك:", error);
    return "none";
  }
}
 
export function hasActiveSubscription(status: SubscriptionStatus): boolean {
  return status === "active" || status === "trialing";
}

استخدمه في صفحاتك لحماية المحتوى المميز:

// src/app/dashboard/page.tsx
import { getSubscriptionStatus, hasActiveSubscription } from "@/lib/subscription";
import { redirect } from "next/navigation";
 
export default async function DashboardPage() {
  // في تطبيق حقيقي، احصل على customerId من جلسة المصادقة
  const customerId = "cus_...";
 
  const status = await getSubscriptionStatus(customerId);
 
  if (!hasActiveSubscription(status)) {
    redirect("/pricing");
  }
 
  return (
    <div className="max-w-4xl mx-auto py-16 px-4">
      <h1 className="text-3xl font-bold">لوحة التحكم</h1>
      <p className="text-gray-600 mt-2">
        مرحبًا بعودتك! حالة اشتراكك {status}.
      </p>
      {/* المحتوى المميز هنا */}
    </div>
  );
}

الخطوة 13: التعامل مع المدفوعات لمرة واحدة

ليس كل شيء يحتاج اشتراكًا. إليك كيفية التعامل مع المدفوعات لمرة واحدة للمنتجات الرقمية أو الخدمات:

// src/app/api/checkout/one-time/route.ts
import { NextRequest, NextResponse } from "next/server";
import { stripe } from "@/lib/stripe";
 
export async function POST(req: NextRequest) {
  try {
    const { productName, amount, currency = "usd" } = await req.json();
 
    const session = await stripe.checkout.sessions.create({
      mode: "payment",
      payment_method_types: ["card"],
      line_items: [
        {
          price_data: {
            currency,
            product_data: {
              name: productName,
            },
            unit_amount: amount, // المبلغ بالسنتات (مثال: 2999 = 29.99$)
          },
          quantity: 1,
        },
      ],
      success_url: `${process.env.NEXT_PUBLIC_APP_URL}/success?session_id={CHECKOUT_SESSION_ID}`,
      cancel_url: `${process.env.NEXT_PUBLIC_APP_URL}/pricing`,
    });
 
    return NextResponse.json({ url: session.url });
  } catch (error) {
    console.error("خطأ في الدفع لمرة واحدة:", error);
    return NextResponse.json(
      { error: "فشل في إنشاء جلسة الدفع" },
      { status: 500 }
    );
  }
}

الخطوة 14: الاختبار ببطاقات Stripe التجريبية

يوفر Stripe أرقام بطاقات تجريبية لسيناريوهات مختلفة:

رقم البطاقةالسيناريو
4242 4242 4242 4242دفع ناجح
4000 0000 0000 3220يتطلب مصادقة 3D Secure
4000 0000 0000 9995دفع مرفوض
4000 0000 0000 0341فشل ربط البطاقة

استخدم أي تاريخ انتهاء مستقبلي وأي CVC من 3 أرقام وأي رمز بريدي.

شغّل تطبيقك واختبر التدفق الكامل:

npm run dev
  1. زُر http://localhost:3000/pricing
  2. اضغط على "الاشتراك في Pro"
  3. استخدم بطاقة الاختبار 4242 4242 4242 4242
  4. تأكد من وصولك إلى صفحة النجاح
  5. تحقق من لوحة تحكم Stripe للاشتراك الجديد

الخطوة 15: نشر الويب هوك في الإنتاج

عند النشر، تحتاج إلى تسجيل رابط الويب هوك الإنتاجي في Stripe:

  1. اذهب إلى Developers → Webhooks في لوحة تحكم Stripe
  2. اضغط على Add endpoint
  3. أدخل رابط الإنتاج: https://yourdomain.com/api/webhooks/stripe
  4. اختر الأحداث للاستماع إليها:
    • checkout.session.completed
    • invoice.paid
    • invoice.payment_failed
    • customer.subscription.deleted
    • customer.subscription.updated
  5. انسخ مفتاح التوقيع الجديد وأضفه إلى متغيرات بيئة الإنتاج

استكشاف الأخطاء وإصلاحها

فشل التحقق من توقيع الويب هوك

تأكد من قراءة نص الطلب الخام كنص وليس تحليله كـ JSON قبل التحقق. طريقة req.text() في Next.js App Router تتعامل مع هذا بشكل صحيح.

جلسة الدفع تُرجع رابط فارغ

تأكد من صحة مفتاح Stripe السري ومن وجود معرّف السعر. مفاتيح وضع الاختبار تعمل فقط مع أسعار وضع الاختبار.

حالة الاشتراك لا تتحدث

تأكد من إمكانية الوصول إلى نقطة نهاية الويب هوك وأنها تُرجع رموز حالة 200. استخدم stripe listen محليًا أو تحقق من سجلات الويب هوكس في لوحة تحكم Stripe للتسليمات الفاشلة.

أخطاء CORS عند إعادة التوجيه

لا تحتاج إلى ترويسات CORS — Stripe Checkout هو إعادة توجيه من جانب الخادم وليس استدعاء API من جانب العميل. تأكد من استدعاء مسار API الخاص بك وليس API Stripe مباشرة من المتصفح.

الخطوات التالية

  • إضافة المصادقة — اربط عملاء Stripe بحسابات المستخدمين باستخدام Auth.js أو Better Auth
  • تخزين البيانات في قاعدة بيانات — استخدم Drizzle ORM أو Supabase لحفظ بيانات الاشتراك
  • إضافة الفوترة حسب الاستخدام — قم بالفوترة بناءً على الاستخدام مع تسعير Stripe القائم على الاستخدام
  • تطبيق فترات تجريبية — قدّم تجارب مجانية قبل الفوترة
  • دعم عملات متعددة — اقبل المدفوعات بعملات متعددة للعملاء الدوليين

الخلاصة

لقد بنيت نظام دفع كامل باستخدام Stripe و Next.js يتعامل مع الاشتراكات والمدفوعات لمرة واحدة والويب هوكس والإدارة الذاتية عبر بوابة العميل. البنية المبنية على الويب هوكس تضمن بقاء تطبيقك متزامنًا مع Stripe بغض النظر عن مصدر التغييرات — سواء من تطبيقك أو لوحة تحكم Stripe أو بوابة العميل.

المبادئ الأساسية التي يجب تذكرها:

  1. لا تثق أبدًا بالعميل — تحقق دائمًا من حالة الدفع من جانب الخادم
  2. الويب هوكس هي مصدر الحقيقة — لا تعتمد على callbacks إعادة التوجيه وحدها
  3. اختبر بدقة — استخدم بطاقات اختبار Stripe و CLI لمحاكاة كل سيناريو
  4. تعامل مع الإخفاقات بأناقة — المدفوعات قد تفشل، الاشتراكات قد تنتهي، البطاقات قد تنتهي صلاحيتها

مع هذا الأساس، يمكنك توسيع نظام الدفع للتعامل مع أي نموذج فوترة يتطلبه تطبيقك.


هل تريد قراءة المزيد من الدروس التعليمية؟ تحقق من أحدث درس تعليمي لدينا على ضبط دقيق لـ GPT مع OpenAI و Next.js و Vercel AI SDK.

ناقش مشروعك معنا

نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.

دعنا نجد أفضل الحلول لاحتياجاتك.

مقالات ذات صلة

بناء واجهة GraphQL آمنة الأنواع مع Next.js App Router و Yoga و Pothos

تعلم كيفية بناء واجهة GraphQL API آمنة الأنواع بالكامل باستخدام Next.js 15 App Router و GraphQL Yoga و Pothos schema builder. يغطي هذا الدليل العملي تصميم المخططات والاستعلامات والتحولات والمصادقة وعميل React باستخدام urql.

30 د قراءة·

بناء مشروع SaaS متكامل باستخدام Next.js 15 و Stripe و Auth.js v5

تعلم كيفية بناء تطبيق SaaS جاهز للإنتاج باستخدام Next.js 15 مع نظام اشتراكات Stripe ومصادقة Auth.js v5. يغطي هذا الدليل التفصيلي إعداد المشروع وتسجيل الدخول عبر OAuth وخطط الأسعار ومعالجة Webhooks والمسارات المحمية.

35 د قراءة·

بناء نظام بريد إلكتروني للمعاملات باستخدام Resend و React Email في Next.js

تعلم كيفية بناء رسائل بريد إلكتروني جميلة وآمنة النوع للمعاملات باستخدام React Email و Resend في تطبيق Next.js. يغطي هذا الدرس تصميم قوالب البريد الإلكتروني وسير عمل المعاينة والإرسال عبر مسارات API والنشر في بيئة الإنتاج.

25 د قراءة·