دليل Polar.sh لعام 2026: بناء SaaS مع نظام تاجر التسجيل في Next.js

إذا حاولت يوماً إطلاق تطبيق SaaS عالمي بمفردك، فأنت تعرف الفخ جيداً. يستغرق بناء المنتج عطلتين أسبوعيتين، لكن الفوترة تستغرق ثلاثة أشهر. يتعامل Stripe مع البطاقات، لكنك لا تزال بحاجة للتسجيل في ضريبة القيمة المضافة في خمس عشرة ولاية قضائية، وتقديم ضريبة المبيعات الفصلية في كاليفورنيا وتكساس، وإرسال نماذج 1099-K إلى مصلحة الضرائب الأمريكية، والاحتفاظ بمحامي امتثال على الاتصال السريع. يقلب Polar.sh هذا الوضع رأساً على عقب. بدلاً من أن يكون مجرد معالج دفع، يعمل Polar كتاجر تسجيل لمبيعاتك. هم البائع القانوني. يجمعون الأموال، ويحسبون الضريبة الصحيحة في كل بلد، ويحولونها إلى الحكومة المناسبة، ويدفعون لك مبلغاً صافياً بعملتك المحلية. مهمتك الوحيدة هي إطلاق المنتج.
في هذا الدليل، ستقوم بدمج Polar.sh في تطبيق SaaS مبني بـ Next.js 15 من الصفر. ستبني صفحة تسعير بثلاث فئات، ونظام دفع مستضاف، وبوابة عملاء، وحاجز اشتراك باستخدام middleware، ومعالجات webhook مدعومة بقاعدة بيانات Postgres، وقياس الاستخدام لمكالمات API، وتدفق مفتاح ترخيص للخطط ذاتية الاستضافة. في النهاية، ستحصل على طبقة فوترة قابلة للتوسع من عميلك الأول في تونس إلى عميلك المئة في طوكيو دون الحاجة لتقديم إقرار ضريبي أجنبي.
المتطلبات الأساسية
قبل البدء، تأكد من توفر:
- Node.js إصدار 20 أو أحدث
- مشروع Next.js 15 يستخدم App Router (أو اتبع خطوة الإعداد أدناه)
- حساب على polar.sh (مجاني، يستغرق حوالي ثلاث دقائق)
- قاعدة بيانات Postgres (Neon أو Supabase أو Docker محلي)
- معرفة أساسية بـ TypeScript و React Server Components و webhooks
- اختياري: ngrok أو نفق مماثل لاختبار webhooks محلياً
ما الذي ستبنيه
في نهاية هذا الدليل، ستحصل على:
- منظمة Polar.sh مع ثلاثة منتجات اشتراك ومقياس دفع حسب الاستخدام
- تطبيق Next.js 15 مع صفحة تسعير تستخدم Polar Checkout للترقيات
- نقطة نهاية webhook تزامن الاشتراكات والطلبات مع قاعدة بياناتك الخاصة
- مسار بوابة عميل يسمح للمستخدمين بتحديث طرق الدفع والإلغاء
- Middleware يحمي المسارات المتميزة بناءً على حالة الاشتراك
- تدفق فوترة حسب الاستخدام يقيس مكالمات API ويفرض رسوماً لكل وحدة
- نظام مفاتيح ترخيص للعملاء الذين يشترون خطة مدى الحياة ذاتية الاستضافة
لنبدأ بفهم لماذا تاجر التسجيل مهم قبل كتابة سطر واحد من الكود.
لماذا تاجر التسجيل أفضل من Stripe المباشر لمعظم تطبيقات SaaS
عندما تأخذ المدفوعات عبر Stripe مباشرة، أنت البائع. هذا يعني أن الالتزامات القانونية والضريبية تقع عليك. في الاتحاد الأوروبي، تدين بضريبة القيمة المضافة على كل عملية بيع B2C وتحتاج لتقديم إقرار OSS فصلي. في الولايات المتحدة، تدين بضريبة المبيعات في كل ولاية لديك فيها صلة اقتصادية، والتي تبدأ بمئة معاملة فقط في بعض الولايات. في المملكة المتحدة وأستراليا والهند واليابان، توجد قواعد مماثلة. يتجاهل معظم المؤسسين المنفردين كل هذا حتى يصلهم إشعار ضريبي بعد عامين، وعندها تتفوق العقوبات على الإيرادات.
يتولى تاجر التسجيل مثل Polar تلك المسؤولية. يسجلون في كل ولاية قضائية، ويحسبون السعر الصحيح لكل سلة، ويفرضون الرسوم على العميل، ويحولون إلى السلطة الضريبية، ويتحملون عمليات الاسترداد. أنت تبيع لـ Polar. Polar يبيع لعميلك. تحصل على نموذج 1099 واحد من كيان أمريكي واحد ودفعة نظيفة. المفاضلة هي الرسوم: يفرض Polar حوالي أربعة بالمئة بالإضافة إلى أربعين سنتاً، مقارنة بحوالي ثلاثة بالمئة لـ Stripe. بالنسبة لمعظم تطبيقات SaaS الصغيرة، تكون هذه الزيادة أرخص من ساعة واحدة من المحاسب.
يستهدف Polar تحديداً المطورين. واجهة API مفتوحة المصدر، وصفحات التسعير قائمة على المكونات، والمنتج بأكمله مبني على Stripe من الداخل، لذا فإن الموثوقية متطابقة. يدعمون الاشتراكات والمنتجات لمرة واحدة وقياس الاستخدام ومفاتيح الترخيص والتنزيلات الرقمية بشكل افتراضي. إذا كنت تحت حوالي مليوني دولار سنوياً وتطلق SaaS أو منتجاً رقمياً، فإن Polar هو الخيار الصحيح دائماً تقريباً.
الخطوة 1: إعداد مشروع Next.js 15
إذا كان لديك مشروع Next.js بالفعل، فتخطى هذا. وإلا، أنشئ مشروعاً جديداً بـ TypeScript و Tailwind و App Router.
npx create-next-app@latest polar-saas \
--typescript --tailwind --app --src-dir --import-alias "@/*"
cd polar-saasثبت التبعيات التي ستحتاجها عبر الدليل. Polar SDK هو العميل الرسمي لـ TypeScript، و Drizzle هو ORM خفيف يناسب هذا النمط من المخطط المدفوع بـ webhook، و pg هو محرك Postgres.
npm install @polar-sh/sdk @polar-sh/nextjs zod
npm install drizzle-orm pg
npm install --save-dev drizzle-kit @types/pg tsxأنشئ ملف .env.local في الجذر مع متغيرات بديلة. ستملأها في الخطوة التالية.
POLAR_ACCESS_TOKEN=
POLAR_ORGANIZATION_ID=
POLAR_WEBHOOK_SECRET=
POLAR_ENVIRONMENT=sandbox
DATABASE_URL=postgres://user:pass@localhost:5432/polar_saas
NEXT_PUBLIC_BASE_URL=http://localhost:3000علم POLAR_ENVIRONMENT مهم. يدير Polar صندوق رمل كاملاً على sandbox.polar.sh يعكس الإنتاج. طور دائماً مقابل sandbox حتى يكون التدفق متيناً، ثم اقلب المتغير إلى production.
الخطوة 2: تكوين منظمة Polar الخاصة بك
سجل على polar.sh واضغط على Create Organization. اختر معرفاً مثل your-company؛ سيظهر في عنوان URL الخاص بـ checkout. انتقل إلى وضع المطور تحت الإعدادات، ثم انتقل إلى Sandbox على sandbox.polar.sh وأنشئ نفس المنظمة هناك. ستعمل في sandbox لبقية هذا الدليل.
داخل Sandbox، انتقل إلى Settings ثم Tokens، وأنشئ Personal Access Token جديداً مع نطاقات products:read و products:write و subscriptions:read و orders:read و customers:read و customer_sessions:write و webhooks:write. انسخ الرمز إلى POLAR_ACCESS_TOKEN. احصل على معرف المنظمة من شريط URL (UUID بعد /dashboard/) والصقه في POLAR_ORGANIZATION_ID.
الآن أنشئ ثلاثة منتجات اشتراك. انتقل إلى Products، اضغط New، وأضف:
- Starter بتسعة دولارات شهرياً، متكرر
- Pro بتسعة وعشرين دولاراً شهرياً، متكرر، مع متغير سنوي بخصم خمسة وعشرين بالمئة
- Lifetime بأربعمئة وتسعة وتسعين دولاراً، لمرة واحدة، مع وضع علامة كترخيص ذاتي الاستضافة
لكل منتج، اضغط على علامة التبويب API داخل صفحة المنتج وانسخ معرف المنتج. ستشير إلى هذه المعرفات في الكود بدلاً من كتابة الأسعار بشكل ثابت، مما يبقيك مرناً إذا غيرت التسعير لاحقاً.
الخطوة 3: إعداد مخطط قاعدة البيانات
أنشئ src/db/schema.ts. الهدف هو عكس أجزاء Polar التي يهتم بها تطبيقك: العملاء والاشتراكات وجدول استخدام للقياس. لست بحاجة لعكس المنتجات، لأنها تُقرأ مباشرة من Polar عبر SDK.
import { pgTable, text, timestamp, integer, jsonb } from "drizzle-orm/pg-core";
export const customers = pgTable("customers", {
id: text("id").primaryKey(),
polarCustomerId: text("polar_customer_id").notNull().unique(),
email: text("email").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
});
export const subscriptions = pgTable("subscriptions", {
id: text("id").primaryKey(),
customerId: text("customer_id").references(() => customers.id).notNull(),
productId: text("product_id").notNull(),
status: text("status").notNull(),
currentPeriodEnd: timestamp("current_period_end").notNull(),
metadata: jsonb("metadata"),
});
export const usageEvents = pgTable("usage_events", {
id: text("id").primaryKey(),
customerId: text("customer_id").notNull(),
meterId: text("meter_id").notNull(),
units: integer("units").notNull(),
ingestedAt: timestamp("ingested_at").defaultNow().notNull(),
});
export const licenseKeys = pgTable("license_keys", {
id: text("id").primaryKey(),
key: text("key").notNull().unique(),
customerId: text("customer_id").notNull(),
productId: text("product_id").notNull(),
activations: integer("activations").default(0).notNull(),
maxActivations: integer("max_activations").default(3).notNull(),
});اربط عميل Drizzle في src/db/index.ts حتى تتمكن مكونات الخادم ومعالجات المسارات من استيراد مثيل واحد.
import { drizzle } from "drizzle-orm/node-postgres";
import { Pool } from "pg";
import * as schema from "./schema";
const pool = new Pool({ connectionString: process.env.DATABASE_URL });
export const db = drizzle(pool, { schema });أضف تكوين Drizzle في جذر المشروع وقم بتشغيل الترحيل الأولي.
npx drizzle-kit push --dialect postgresql --schema ./src/db/schema.tsالخطوة 4: بناء صفحة التسعير
صفحات التسعير هي أكبر تسرب في معظم قمع SaaS، لذا يستحق الأمر القيام بهذا بشكل صحيح. النمط أدناه يجلب المنتجات مباشرة من Polar في وقت الطلب، مما يعني أن تغيير السعر في لوحة التحكم يُشحن فوراً دون إعادة نشر.
أنشئ src/lib/polar.ts لتمركز عميل Polar.
import { Polar } from "@polar-sh/sdk";
export const polar = new Polar({
accessToken: process.env.POLAR_ACCESS_TOKEN!,
server: process.env.POLAR_ENVIRONMENT === "production" ? "production" : "sandbox",
});الآن ابن صفحة التسعير كمكون خادم في src/app/pricing/page.tsx. نقطة نهاية قائمة المنتجات تعرض الصفحات، لذا مرر دائماً معرف المنظمة وحداً للحفاظ على سرعة الاستجابة.
import { polar } from "@/lib/polar";
import { CheckoutButton } from "./checkout-button";
export default async function PricingPage() {
const { result } = await polar.products.list({
organizationId: process.env.POLAR_ORGANIZATION_ID!,
isArchived: false,
limit: 20,
});
return (
<main className="mx-auto max-w-5xl px-6 py-16">
<h1 className="text-4xl font-semibold mb-12 text-center">Pricing</h1>
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
{result.items.map((product) => (
<div key={product.id} className="rounded-2xl border p-6">
<h2 className="text-xl font-semibold">{product.name}</h2>
<p className="text-sm text-gray-600 mt-2">{product.description}</p>
<p className="text-3xl font-bold mt-4">
${(product.prices[0].priceAmount ?? 0) / 100}
<span className="text-base text-gray-500">
{product.prices[0].type === "recurring" ? "/mo" : ""}
</span>
</p>
<CheckoutButton productId={product.id} />
</div>
))}
</div>
</main>
);
}زر الدفع هو مكون عميل يفتح نظام دفع Polar المستضاف في iframe مدمج. هذه أكبر مكاسب UX مقارنة بإعادة التوجيه بعيداً عن نطاقك، لأن معدلات التخلي تنخفض بحوالي عشرين بالمئة عندما يبقى المستخدمون على الصفحة.
"use client";
import { useState } from "react";
export function CheckoutButton({ productId }: { productId: string }) {
const [loading, setLoading] = useState(false);
async function handleClick() {
setLoading(true);
const res = await fetch("/api/checkout", {
method: "POST",
body: JSON.stringify({ productId }),
});
const { url } = await res.json();
window.location.href = url;
}
return (
<button
onClick={handleClick}
disabled={loading}
className="mt-6 w-full rounded-lg bg-black text-white py-2 font-medium"
>
{loading ? "Loading..." : "Subscribe"}
</button>
);
}الخطوة 5: إنشاء معالج مسار الدفع
ينشئ مسار الدفع جلسة Polar Checkout ويعيد عنوان URL المستضاف. مرر customerExternalId بحيث عندما يتم إطلاق webhook لاحقاً يمكنك مطابقة عميل Polar مع سجل المستخدم الخاص بك دون بحث إضافي.
أنشئ src/app/api/checkout/route.ts.
import { polar } from "@/lib/polar";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const { productId } = await req.json();
const checkout = await polar.checkouts.create({
products: [productId],
successUrl: `${process.env.NEXT_PUBLIC_BASE_URL}/dashboard?checkout_id={CHECKOUT_ID}`,
customerExternalId: "user_abc123",
metadata: { source: "pricing_page" },
});
return NextResponse.json({ url: checkout.url });
}في الإنتاج، استبدل customerExternalId المكتوب يدوياً بمعرف المستخدم المصادق عليه من مزود المصادقة الخاص بك، سواء كان Clerk أو Better Auth أو NextAuth. متغير القالب {CHECKOUT_ID} يستبدله Polar بعد الدفع، مما يسمح لصفحة النجاح بتأكيد الطلب دون الثقة في سلسلة استعلام يتحكم فيها المستخدم.
الخطوة 6: التعامل مع Webhooks لمزامنة الاشتراكات
Webhooks هي الطريقة الموثوقة الوحيدة للحفاظ على قاعدة بياناتك متزامنة مع Polar. الاستطلاع هش وعرضة لحالات السباق. يأتي Polar SDK مع مساعد بنمط Express يتحقق من التوقيعات باستخدام HMAC، لذا لا تحتاج لإعادة تنفيذ ذلك بنفسك.
أنشئ src/app/api/webhooks/polar/route.ts.
import { Webhooks } from "@polar-sh/nextjs";
import { db } from "@/db";
import { customers, subscriptions } from "@/db/schema";
export const POST = Webhooks({
webhookSecret: process.env.POLAR_WEBHOOK_SECRET!,
onSubscriptionCreated: async (payload) => {
const sub = payload.data;
await db.insert(customers).values({
id: sub.customer.externalId ?? sub.customer.id,
polarCustomerId: sub.customer.id,
email: sub.customer.email,
}).onConflictDoNothing();
await db.insert(subscriptions).values({
id: sub.id,
customerId: sub.customer.externalId ?? sub.customer.id,
productId: sub.productId,
status: sub.status,
currentPeriodEnd: new Date(sub.currentPeriodEnd!),
});
},
onSubscriptionUpdated: async (payload) => {
const sub = payload.data;
await db.update(subscriptions)
.set({
status: sub.status,
currentPeriodEnd: new Date(sub.currentPeriodEnd!),
})
.where((s, { eq }) => eq(s.id, sub.id));
},
onSubscriptionCanceled: async (payload) => {
await db.update(subscriptions)
.set({ status: "canceled" })
.where((s, { eq }) => eq(s.id, payload.data.id));
},
onOrderCreated: async (payload) => {
console.log("One-time order received:", payload.data.id);
},
});لتسجيل عنوان URL هذا مع Polar، انتقل إلى Settings ثم Webhooks، وأضف https://your-domain.com/api/webhooks/polar مع تحديد جميع أحداث الاشتراكات والطلبات. انسخ سر التوقيع إلى POLAR_WEBHOOK_SECRET. للتطوير المحلي، شغل ngrok ووجهه إلى المنفذ 3000، ثم سجل عنوان URL الخاص بـ ngrok مؤقتاً.
ngrok http 3000يعيد Polar محاولة تسليم webhook الفاشلة مع تراجع أسي لمدة تصل إلى أربع وعشرين ساعة، لكن لا يزال يجب عليك التعامل مع كل معالج كمحايد. بند onConflictDoNothing ونمط update أعلاه آمنان للتشغيل مرتين.
الخطوة 7: حماية المسارات المتميزة باستخدام Middleware
قاعدة بيانات الاشتراكات عديمة الفائدة إذا لم تتحقق منها فعلاً. يعمل Next.js middleware قبل كل طلب، مما يجعله المكان المناسب لفرض جدران الدفع دون نشر الفحوصات عبر الصفحات. أنشئ src/middleware.ts.
import { NextRequest, NextResponse } from "next/server";
import { db } from "@/db";
import { subscriptions } from "@/db/schema";
import { eq, and, gte } from "drizzle-orm";
const PROTECTED_PATHS = ["/dashboard/pro", "/api/pro"];
export async function middleware(req: NextRequest) {
if (!PROTECTED_PATHS.some((p) => req.nextUrl.pathname.startsWith(p))) {
return NextResponse.next();
}
const userId = req.cookies.get("user_id")?.value;
if (!userId) return NextResponse.redirect(new URL("/login", req.url));
const active = await db.select().from(subscriptions).where(
and(
eq(subscriptions.customerId, userId),
eq(subscriptions.status, "active"),
gte(subscriptions.currentPeriodEnd, new Date()),
)
);
if (active.length === 0) {
return NextResponse.redirect(new URL("/pricing", req.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/dashboard/pro/:path*", "/api/pro/:path*"],
};لاحظ أن middleware يعمل على بيئة Edge افتراضياً، مما يعني أنك لا تستطيع استخدام محرك pg القياسي. لتوافق Edge، استبدل تجمع Drizzle بمحرك Neon HTTP أو ادفع فحص المصادقة إلى معالج مسار. الإصلاح الأبسط أثناء التطوير هو إضافة export const runtime = "nodejs" إذا كنت تشغل middleware على Node 20.
الخطوة 8: تضمين بوابة العميل
عندما يريد العميل تحديث بطاقته أو الإلغاء، لست مضطراً لبناء أي من تلك الواجهة. يأتي Polar مع بوابة عميل مستضافة تتعامل مع طرق الدفع والفواتير وتغييرات الخطة والإلغاءات. أنشئ عنوان URL موقعاً لمرة واحدة وأعد التوجيه.
أنشئ src/app/api/portal/route.ts.
import { polar } from "@/lib/polar";
import { NextRequest, NextResponse } from "next/server";
export async function POST(req: NextRequest) {
const { customerId } = await req.json();
const session = await polar.customerSessions.create({
customerExternalId: customerId,
});
return NextResponse.json({ url: session.customerPortalUrl });
}عنوان URL للجلسة لاستخدام واحد وينتهي خلال ساعة. أنشئه دائماً عند الطلب من جانب الخادم بدلاً من تخزينه. على العميل، يكفي زر واحد.
"use client";
export function PortalButton({ customerId }: { customerId: string }) {
return (
<button onClick={async () => {
const res = await fetch("/api/portal", {
method: "POST",
body: JSON.stringify({ customerId }),
});
const { url } = await res.json();
window.open(url, "_blank");
}}>
Manage subscription
</button>
);
}الخطوة 9: إضافة فوترة قائمة على الاستخدام مع Meters
بالنسبة لمنتجات الذكاء الاصطناعي وواجهات API، تترك الاشتراكات الثابتة المال على الطاولة. تتيح لك Polar Meters فرض رسوم لكل وحدة، مع طبقة مجانية ومعدل تجاوز، تتم تسوية الكل في الفاتورة التالية. في Sandbox، انتقل إلى Meters، اضغط New Meter، سمه api_calls، وحدد التجميع إلى sum من units. أرفق المقياس بمنتج Pro كسعر مقاس بسنت واحد لكل مكالمة بعد أول عشرة آلاف مكالمة مجانية شهرياً.
الآن استوعب الأحداث من API الخاص بك. أنشئ src/lib/meter.ts.
import { polar } from "@/lib/polar";
export async function recordUsage(
customerExternalId: string,
units: number,
) {
await polar.events.ingest({
events: [
{
name: "api_calls",
externalCustomerId: customerExternalId,
metadata: { units },
},
],
});
}استدع recordUsage من أي مسار يستهلك مورداً قابلاً للفوترة. يقوم Polar بإلغاء التكرار بمعرف الحدث لمدة ساعة، لذا فإن إعادة المحاولة من عميل غير مستقر لن تفرض رسوماً مزدوجة. تتضمن الفاتورة التالية تلقائياً بند تجاوز الاستخدام؛ ليس عليك حساب أو فوترة أي شيء بنفسك.
الخطوة 10: إصدار مفاتيح ترخيص لخطط مدى الحياة
يريد بعض المشترين دفعة واحدة مع ثنائي ذاتي الاستضافة أو مكتبة. يصدر Polar مفاتيح ترخيص بشكل أصلي. عندما تنشئ منتج Lifetime، فعل License Keys واضبط حد التفعيل على ثلاثة أجهزة لكل مفتاح.
في معالج webhook الخاص بك، استمع لـ onOrderCreated واحفظ المفتاح المُصدر.
onOrderCreated: async (payload) => {
const order = payload.data;
const benefit = order.items[0]?.benefits.find((b) => b.type === "license_keys");
if (!benefit?.licenseKey) return;
await db.insert(licenseKeys).values({
id: benefit.licenseKey.id,
key: benefit.licenseKey.key,
customerId: order.customer.externalId ?? order.customer.id,
productId: order.productId,
maxActivations: 3,
});
},ثم اعرض نقطة نهاية تحقق يمكن لـ CLI الخاص بك أو تطبيقك ذاتي الاستضافة الوصول إليها. يتعامل Polar مع الأعمال الثقيلة عبر licenseKeys.validate.
import { polar } from "@/lib/polar";
export async function POST(req: NextRequest) {
const { key, instanceId } = await req.json();
try {
const result = await polar.licenseKeys.validate({
key,
activationId: instanceId,
});
return NextResponse.json({ valid: true, expiresAt: result.expiresAt });
} catch {
return NextResponse.json({ valid: false }, { status: 403 });
}
}اختبار التنفيذ الخاص بك
شغل خادم التطوير وامش عبر التدفق الكامل من البداية إلى النهاية.
npm run devافتح /pricing واضغط Subscribe على فئة Starter. استخدم بطاقة اختبار Polar sandbox 4242 4242 4242 4242 مع أي تاريخ انتهاء مستقبلي و CVC. أكمل الدفع. يجب إعادة توجيهك إلى /dashboard، ويجب أن تظهر طرفية النظام وصول webhook إلى onSubscriptionCreated. استعلم Postgres المحلي للتأكد من وصول صف إلى كل من customers و subscriptions.
الآن اضغط /dashboard/pro مباشرة. يجب أن يسمح لك middleware بالمرور لأن اشتراكك نشط. افتح لوحة Polar sandbox، ابحث عن العميل، وألغ الاشتراك. خلال ثوانٍ، يحدث صف قاعدة البيانات المحلي إلى canceled، ويعيد الطلب التالي إلى /dashboard/pro التوجيه إلى التسعير.
للقياس، شغل سكريبت يستدعي recordUsage عشرة آلاف ومرة واحدة. تحقق من لوحة Polar، اعرض العميل، وتأكد أن المقياس يظهر سنتاً واحداً في الرسوم المعلقة. ستتضمن الفاتورة التالية بند الخط هذا تلقائياً.
استكشاف الأخطاء
فشل التحقق من توقيع webhook. تحقق مرتين من أنك نسخت سر التوقيع من نفس البيئة التي ترسل منها. لدى Sandbox والإنتاج أسرار مختلفة، وخلطها هو السبب الأكثر شيوعاً.
يعيد Checkout 422. يتطلب Polar أن يكون للمنتج سعر منشور واحد على الأقل. إذا أنشأت منتجاً ولم تضغط Publish أبداً، ترفض نقطة نهاية الدفع ذلك. تأكد أن المنتج محدد كنشط في لوحة التحكم.
بريد العميل الإلكتروني مفقود في webhook. يحدث هذا عندما يكتمل الدفع دون جمع بريد إلكتروني، عادةً بسبب تكامل مخصص تجاوز النموذج. اضبط دائماً customerEmail على إنشاء الدفع إذا كان لديك من نظام المصادقة الخاص بك.
انتهاء مهلة Middleware عند Edge. يجب أن تستخدم استعلامات قاعدة البيانات من middleware محركات قائمة على HTTP مثل Neon أو Turso. محركات تجميع الاتصال مثل pg تعمل فقط في وقت تشغيل Node، الذي لا يستخدمه middleware افتراضياً.
الخطوات التالية
- اربط Polar مع Better Auth باستخدام إضافة Polar Better Auth لتخطي بناء تعيين المستخدم-العميل الخاص بك
- أضف برنامج إحالة باستخدام ميزات الانتساب في Polar للعمولات المتكررة
- استضف ذاتياً متجر مجتمع باستخدام بداية Polar Next.js مفتوحة المصدر
- انتقل من sandbox إلى الإنتاج بقلب
POLAR_ENVIRONMENTوتسجيل عنوان webhook المباشر - اقرأ دليل تكامل Stripe الخاص بنا للمقارنة مع النهج المباشر إذا تجاوزت إيراداتك مليوني دولار
الخاتمة
لديك الآن طبقة فوترة كاملة لتطبيق SaaS عالمي دون لمس نموذج ضريبي. يتعامل Polar.sh مع العبء القانوني والامتثال بينما يكشف SDK ودوداً للمطورين يتناسب بشكل طبيعي مع أنماط Next.js 15: مكونات الخادم لصفحة التسعير، ومعالجات المسارات للدفع و webhooks، و middleware لجدران الدفع. نفس النمط يتوسع من مشروع جانبي بعميل واحد إلى عمل حقيقي بعشرة آلاف. الشيء الوحيد المتبقي للبناء هو المنتج نفسه.
ناقش مشروعك معنا
نحن هنا للمساعدة في احتياجات تطوير الويب الخاصة بك. حدد موعدًا لمناقشة مشروعك وكيف يمكننا مساعدتك.
دعنا نجد أفضل الحلول لاحتياجاتك.
مقالات ذات صلة

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

PostHog مع Next.js: تحليلات المنتج، أعلام الميزات، وإعادة عرض الجلسات (دليل 2026)
تعلم كيفية دمج PostHog مع Next.js 15 لتتبع الأحداث، وإطلاق الميزات خلف أعلام، وتسجيل جلسات المستخدمين، وإجراء تجارب A/B، والتقاط الأخطاء — كل ذلك من منصة واحدة مفتوحة المصدر.

بناء وكيل ذكاء اصطناعي مستقل باستخدام Agentic RAG و Next.js
تعلم كيف تبني وكيل ذكاء اصطناعي يقرر بشكل مستقل متى وكيف يسترجع المعلومات من قواعد البيانات المتجهية. دليل عملي شامل باستخدام Vercel AI SDK و Next.js مع أمثلة قابلة للتنفيذ.