נתקלנו שוב ושוב בפרויקטים שבהם מנויים נשברו עקב webhooks שלא עובדו: תשלומים נכשלו, הגישה נשארה גם לאחר ביטול, ודיווחים לא היו קיימים. כתוצאה מכך, החברה הפסידה עד 30% מההכנסות רק כתוצאה מנשירה. הניסיון שלנו בתחום זה — 8+ שנים, שבמהלכן יישמנו מעל 30 מערכות מנויים עבור מדיה, SaaS ופלטפורמות חינוכיות. פרויקט אחרון אחד — שירות מדיה עם 50,000 מנויים משלמים: לאחר המעבר לארכיטקטורה שלנו, הנשירה ירדה ב-35% ברבעון.
מערכת המנויים פותרת בעיות קריטיות
הראשונה והחשובה ביותר — נשירת לקוחות עקב כשלי תשלום. אם תשלום נכשל והמשתמש לא מקבל הודעה, הם מאבדים גישה ועוזבים. אנו מיישמים תקופת חסד וניסיונות חוזרים אוטומטיים. לפי תיעוד Stripe, ללא תקופת חסד עד 40% מהלקוחות עוזבים לאחר שגיאת התשלום הראשונה; עם היישום שלנו נתון זה יורד ל-5%, מה שהופך את הפתרון שלנו לטוב פי 8 בשמירה על לקוחות. הבעיה השנייה — חיוב שגוי בעת שינוי תוכניות. חישוב יחסי דרך Stripe פותר זאת. השלישית — ניהול גישה ברמת תכונה: לדוגמה, מתן גישה ל-4K רק למנויי תוכנית שנתית. הכל מובנה בסכמת הנתונים.
בעיה נפוצה נוספת — idempotency של webhook. ללא מפתח ייחודי, מסירת אירועים כפולה מובילה לחיוב כפול. אנו מיישמים webhook_idempotency_key ברמת מסד הנתונים, ומבטלים כפילויות.
למה מחזור חיים נכון של מנוי חשוב
מחזור החיים של המנוי — trialing → active → past_due → expired או cancelled — חייב להישמר בקפדנות. כל סטטוס מאומת בקצה האחורי: SubscriptionAccessGuard בודק לא רק סטטוס אלא גם past_due. זה מונע מתן גישה למנוי שפג תוקפו. אנו משתמשים בגישה מונעת אירועים: כל שינוי סטטוס יוצר אירוע שמערכת ההודעות (אימייל/טלגרם) רשומה אליו.
הפחתת נשירה עם תקופת חסד
כאשר תשלום נכשל, המנוי עובר ל-PaymentProviderFactory, ותקופת חסד (עד 7 ימים) מתחילה. במהלך תקופה זו הגישה נשמרת במלואה, והמשתמש מקבל אימייל המבקש לעדכן פרטי תשלום. אם התשלום לא מתקבל, הגישה נחסמת, אך כל הנתונים נשמרים: הלקוח יכול לחדש את המנוי מבלי לאבד היסטוריה. יישום תקופת החסד שלנו יעיל פי שניים מהסטנדרטי בזכות הודעות אקטיביות: אנו שולחים עד 3 אימיילים בתזמונים שונים.
ארכיטקטורת היישום והתהליך
אנו משתמשים ב-Laravel (PHP 8.3+) ו-PostgreSQL. ספק התשלום — Stripe או YooKassa דרך trialing → active → past_due → expired. כל שינויי הסטטוס מגיעים דרך webhook — זהו מקור האמת היחיד. מחזור החיים של המנוי: cancelled או Schema::create('subscription_plans', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('slug')->unique(); $table->decimal('price_monthly', 10, 2)->nullable(); $table->decimal('price_yearly', 10, 2)->nullable(); $table->integer('trial_days')->default(0); $table->jsonb('features'); $table->boolean('is_active')->default(true); $table->timestamps(); }); Schema::create('subscriptions', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained(); $table->foreignId('plan_id')->constrained('subscription_plans'); $table->enum('billing_period', ['monthly', 'yearly']); $table->enum('status', ['trialing', 'active', 'past_due', 'cancelled', 'expired']); $table->string('payment_provider'); $table->string('provider_subscription_id')->nullable(); $table->string('provider_customer_id')->nullable(); $table->timestamp('trial_ends_at')->nullable(); $table->timestamp('current_period_start'); $table->timestamp('current_period_end'); $table->timestamp('grace_period_ends_at')->nullable(); $table->timestamp('cancelled_at')->nullable(); $table->timestamp('ends_at')->nullable(); $table->timestamps(); $table->index(['user_id', 'status']); $table->index('current_period_end'); }); .
דוגמת סכמת מסד נתונים
Schema::create('subscription_plans', function (Blueprint $table) { $table->id(); $table->string('name'); $table->string('slug')->unique(); $table->decimal('price_monthly', 10, 2)->nullable(); $table->decimal('price_yearly', 10, 2)->nullable(); $table->integer('trial_days')->default(0); $table->jsonb('features'); $table->boolean('is_active')->default(true); $table->timestamps(); }); Schema::create('subscriptions', function (Blueprint $table) { $table->id(); $table->foreignId('user_id')->constrained(); $table->foreignId('plan_id')->constrained('subscription_plans'); $table->enum('billing_period', ['monthly', 'yearly']); $table->enum('status', ['trialing', 'active', 'past_due', 'cancelled', 'expired']); $table->string('payment_provider'); $table->string('provider_subscription_id')->nullable(); $table->string('provider_customer_id')->nullable(); $table->timestamp('trial_ends_at')->nullable(); $table->timestamp('current_period_start'); $table->timestamp('current_period_end'); $table->timestamp('grace_period_ends_at')->nullable(); $table->timestamp('cancelled_at')->nullable(); $table->timestamp('ends_at')->nullable(); $table->timestamps(); $table->index(['user_id', 'status']); $table->index('current_period_end'); }); דוגמה ליצירת מנוי דרך CreateSubscriptionAction — טרנזקציה המסנכרנת בין הספק למסד הנתונים המקומי. מטפל ה-webhook StripeWebhookHandler מעבד אירועים invoice.paid, invoice.payment_failed, customer.subscription.updated, customer.subscription.deleted. בקרת גישה דרך SubscriptionAccessGuard, שבודקת סטטוס, תקופת חסד ותכונות תוכנית.
מודלים נתמכים של תעריפים
כל סוג: מחיר קבוע לתקופה, מנוי מבוסס יחידות (חיוב לפי מספר אובייקטים/משתמשים), freemium עם תקופת ניסיון, ומשולב — שכבה חינמית עם אפשרויות שדרוג. כל המודלים ניתנים להגדרה דרך פאנל הניהול ללא שינויי קוד.
שלבי העבודה
| שלב | משך | תיאור |
|---|---|---|
| אנליטיקה | 2–3 ימים | לימוד הלוגיקה העסקית, מודל תעריפים ומגבלות |
| עיצוב | 3–5 ימים | סכמות מסד נתונים, נקודות קצה API, אינטגרציית ספק תשלומים |
| יישום | 7–10 ימים | תכנות מנויים, webhook, ניהול גישה |
| בדיקות | 2–3 ימים | כיסוי כל התרחישים: תשלום מוצלח, כשל, שדרוג, ביטול |
| פריסה וניטור | 1–2 ימים | הגדרת התראות שגיאה, יומני webhook |
מה כלול בעבודה?
| רכיב | תיאור |
|---|---|
| סכמת נתונים | טבלאות plans, subscriptions, events |
| אינטגרציית תשלומים | Stripe/YooKassa: מנויים, webhook, טיפול בשגיאות |
| ניהול גישה | Middleware, guard לבדיקות תכונות |
| חשבון אישי | ממשק משתמש לשינוי תוכנית, ביטול, צפייה בהיסטוריה |
| הודעות | אימייל/טלגרם על חידוש, כשל, פקיעת תקופת חסד |
| תיעוד | תיאור API, הוראות בדיקה |
טעויות נפוצות שאנו מונעים: טיפול לא נכון באזור זמן (פקיעת תקופת חסד באמצע היום), חוסר ביומני webhook (קשה לנפות באגים), התעלמות ממצבי בדיקה של הספק.
לוח זמנים ואחריות
מחזור מלא — בין 16 ל-20 ימי עסקים. אנו מספקים אחריות על עיבוד תשלומים נכון וטיפול ב-webhook למשך 30 יום לאחר המסירה. כל השינויים דרך git, עם סקירת קוד על ידי מפתח בכיר. מידע נוסף על שיטות עבודה מומלצות — ב-תיעוד Stripe. צרו קשר לייעוץ — נדון במודל העסקי שלכם ונבחר את הפתרון האופטימלי. הזמינו יישום של מערכת מנויים לפרויקט שלכם.







