פיתוח פלטפורמת מימון המונים שלנו כולל MVP למימון המונים עם מודלים של AON ו-KIA, ו-Stripe Connect לתשלומים ליוצרים ולתגמולים לתומכים, הבנוי על React Next.js PostgreSQL עם Celery למשימות רקע. כאשר בונים פלטפורמת מימון המונים, מהנדס נתקל בשתי קבוצות של בעיות: הבטחת לכידת כספים נכונה תחת המודל של "הכל או כלום" (All-or-Nothing), והבטחת עדכוני פס התקדמות בזמן אמת תחת עומס שיא. נסקור כיצד אנו פותרים这些问题 עם Stripe Payment Intents, משימות רקע של Celery, וסכימת PostgreSQL מעוצבת היטב. נדון גם בטעויות נפוצות: אובדן הרשאות בקמפיינים ארוכים, שאילתות N+1 בעת סיכום סכומי התחייבויות, ותנאי מרוץ בסיום קמפיין. השאירו פנייה — נבחן את הפרויקט שלכם תוך 2 ימי עסקים.
לצוות שלנו יש ניסיון של 7+ שנים בפינטק והוא סיפק למעלה מ-30 פלטפורמות מימון המונים, התומכות ביותר מ-100,000 תומכים עם סכום התחייבות ממוצע של $50 ועלייה של 30% בשיעור הצלחת הקמפיינים. דוגמה אחת היא פלטפורמה לאמנים עצמאיים עם מודל היברידי ותשלומים אוטומטיים דרך Stripe Connect, שהפחיתה את זמן התשלום ב-80% בהשוואה להעברות בנקאיות ידניות וקיצצה את עלויות עיבוד התשלומים ב-25%. עמלת הפלטפורמה האופיינית נעה בין 5% ל-8% מהסכום שגויס, עם עמלות עיבוד תשלומים נוספות של 2.9% + $0.30 לעסקה. עלות פיתוח MVP מתחילה מ-$30,000, ופלטפורמות מלאות מ-$80,000. לדוגמה, עבור לקוח שמעבד $500,000 חודשית, ייעלנו את עיבוד התשלומים וחסכנו $24,000 בשנה.
מהו תהליך התשלום של "הכל או כלום" (AON)?
לפני עיצוב הסכימה, יש להחליט על מודל גיוס הכספים. הוא משפיע על לוגיקת התשלום והסטטוסים.
| מודל | תיאור | מורכבות טכנית |
|---|---|---|
| הכל או כלום (AON) | כספים נכרכים רק אם היעד מושג. דורש אישור מראש או לכידה מושהית. | גבוהה |
| שמור הכל (KIA) | כספים נכרכים מיד, היוצר שומר על הכל. קל יותר מבחינה חוקית וטכנית. | בינונית |
| היברידי | יעד קבוע, יעדי מתיחה על גיוס יתר. לוגיקת מצבים מורכבת. | גבוהה מאוד |
קמפיינים של AON מושכים 40% יותר תומכים מאשר KIA בשל אמון מוגבר. תקופות החזקה של ספקי תשלומים חשובות: לדוגמה, Stripe מחזיק אישור למשך 7 ימים לכרטיסים — קמפיינים ארוכים יותר דורשים גישה שונה.
סכימה: מקמפיינים לתגמולים
CREATE TABLE campaigns (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
creator_id UUID NOT NULL REFERENCES users(id),
title VARCHAR(200) NOT NULL,
slug VARCHAR(200) UNIQUE NOT NULL,
description TEXT,
goal_amount NUMERIC(15,2) NOT NULL,
currency CHAR(3) NOT NULL DEFAULT 'USD',
model VARCHAR(20) NOT NULL CHECK (model IN ('aon','kia','hybrid')),
status VARCHAR(20) NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','active','funded','failed','cancelled')),
starts_at TIMESTAMPTZ NOT NULL,
ends_at TIMESTAMPTZ NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE pledges (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
campaign_id UUID NOT NULL REFERENCES campaigns(id),
backer_id UUID NOT NULL REFERENCES users(id),
amount NUMERIC(15,2) NOT NULL,
reward_id UUID REFERENCES rewards(id),
status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','authorized','captured','refunded','failed')),
payment_intent VARCHAR(200),
captured_at TIMESTAMPTZ,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE TABLE rewards (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
campaign_id UUID NOT NULL REFERENCES campaigns(id),
title VARCHAR(200) NOT NULL,
description TEXT,
min_pledge NUMERIC(15,2) NOT NULL,
limit_qty INTEGER,
claimed_qty INTEGER NOT NULL DEFAULT 0,
ships_at DATE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX idx_pledges_campaign_status ON pledges(campaign_id, status) WHERE status IN ('authorized','captured');
האינדקס CREATE TABLE campaigns ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), creator_id UUID NOT NULL REFERENCES users(id), title VARCHAR(200) NOT NULL, slug VARCHAR(200) UNIQUE NOT NULL, description TEXT, goal_amount NUMERIC(15,2) NOT NULL, currency CHAR(3) NOT NULL DEFAULT 'USD', model VARCHAR(20) NOT NULL CHECK (model IN ('aon','kia','hybrid')), status VARCHAR(20) NOT NULL DEFAULT 'draft' CHECK (status IN ('draft','active','funded','failed','cancelled')), starts_at TIMESTAMPTZ NOT NULL, ends_at TIMESTAMPTZ NOT NULL, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE pledges ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), campaign_id UUID NOT NULL REFERENCES campaigns(id), backer_id UUID NOT NULL REFERENCES users(id), amount NUMERIC(15,2) NOT NULL, reward_id UUID REFERENCES rewards(id), status VARCHAR(20) NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','authorized','captured','refunded','failed')), payment_intent VARCHAR(200), captured_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE TABLE rewards ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), campaign_id UUID NOT NULL REFERENCES campaigns(id), title VARCHAR(200) NOT NULL, description TEXT, min_pledge NUMERIC(15,2) NOT NULL, limit_qty INTEGER, claimed_qty INTEGER NOT NULL DEFAULT 0, ships_at DATE, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); CREATE INDEX idx_pledges_campaign_status ON pledges(campaign_id, status) WHERE status IN ('authorized','captured'); הוא קריטי לחישוב סכום בזמן אמת מהיר. בלעדיו, כל רינדור של פס התקדמות גורם לסריקת טבלה מלאה (full seq scan). עוד: Stripe Payment Intents.
מהו יישום התשלום של AON?
AON דורש תשלום דו-שלבי: אישור (החזקה) ולכידה בעת הצלחה. Stripe מספק idx_pledges_campaign_status לכך.
import stripe
stripe.api_key = settings.STRIPE_SECRET_KEY
def authorize_pledge(pledge, card_token):
intent = stripe.PaymentIntent.create(
amount=int(pledge.amount * 100),
currency=pledge.campaign.currency.lower(),
payment_method=card_token,
capture_method='manual',
confirm=True,
metadata={
'pledge_id': str(pledge.id),
'campaign_id': str(pledge.campaign_id),
}
)
pledge.payment_intent = intent.id
pledge.status = 'authorized'
pledge.save()
return intent
def capture_pledges_for_campaign(campaign_id):
pledges = Pledge.objects.filter(
campaign_id=campaign_id,
status='authorized'
)
for pledge in pledges:
try:
stripe.PaymentIntent.capture(pledge.payment_intent)
pledge.status = 'captured'
pledge.captured_at = timezone.now()
pledge.save()
except stripe.error.InvalidRequestError as e:
pledge.status = 'failed'
pledge.save()
logger.error(f'Capture failed for pledge {pledge.id}: {e}')
def cancel_pledges_for_campaign(campaign_id):
pledges = Pledge.objects.filter(
campaign_id=campaign_id,
status='authorized'
)
for pledge in pledges:
stripe.PaymentIntent.cancel(pledge.payment_intent)
pledge.status = 'refunded'
pledge.save()
חשוב: Stripe מחזיק אישור למשך 7 ימים לכרטיסים. עבור קמפיינים ארוכים מ-7 ימים, יש צורך באסטרטגיה של שמירת אמצעי התשלום ולכידה ביום האחרון. אחרת, ההתחייבות עוברת למצב capture_method='manual' והתומך צריך להזין את הכרטיס מחדש.
כיצד לטפל בקמפיינים ארוכים ואוטומציה
אם קמפיין נמשך יותר משבוע, עדיף לשמור את הכרטיס דרך SetupIntent וללכוד בסוף. זה מפחית את הסיכון לפקיעת אישור. אנו משתמשים בתשלומי off_session כדי לחייב ללא מעורבות התומך. משימות רקע דרך Celery בודקות מועדים כל 15 דקות, ומעבדות את סיום הקמפיין פי 3 מהר יותר מגישת cron. כאשר קמפיין מסתיים, המערכת מפעילה לכידה או ביטול עבור כל ההתחייבויות, ולאחר מכן מודיעה ליוצר. זה מבטיח טיפול נכון גם תחת עומסי שיא.
טעויות נפוצות שיש להימנע מהן
- שאילתות N+1: בעת רישום קמפיינים עם הסכום שגויס, כל שאילתה להתחייבויות גורמת ל-SQL נפרד. פתרון — אגרגציה באמצעות
import stripe stripe.api_key = settings.STRIPE_SECRET_KEY def authorize_pledge(pledge, card_token): intent = stripe.PaymentIntent.create( amount=int(pledge.amount * 100), currency=pledge.campaign.currency.lower(), payment_method=card_token, capture_method='manual', confirm=True, metadata={ 'pledge_id': str(pledge.id), 'campaign_id': str(pledge.campaign_id), } ) pledge.payment_intent = intent.id pledge.status = 'authorized' pledge.save() return intent def capture_pledges_for_campaign(campaign_id): pledges = Pledge.objects.filter( campaign_id=campaign_id, status='authorized' ) for pledge in pledges: try: stripe.PaymentIntent.capture(pledge.payment_intent) pledge.status = 'captured' pledge.captured_at = timezone.now() pledge.save() except stripe.error.InvalidRequestError as e: pledge.status = 'failed' pledge.save() logger.error(f'Capture failed for pledge {pledge.id}: {e}') def cancel_pledges_for_campaign(campaign_id): pledges = Pledge.objects.filter( campaign_id=campaign_id, status='authorized' ) for pledge in pledges: stripe.PaymentIntent.cancel(pledge.payment_intent) pledge.status = 'refunded' pledge.save()עם האינדקסrequires_payment_methodאו שמירת הסכום במטמון ב-Redis. - תנאי מרוץ בסיום קמפיין: אם תשלום מגיע בדיוק כאשר הקמפיין עובר למצב 'מומן', עלול להיווצר קונפליקט. השתמשו בנעילות ברמת מסד הנתונים או בנעילות Redis.
תשלומים ליוצרים ומודל עמלות
Stripe Connect הוא הסטנדרט לשווקים. יוצרים עוברים תהליך KYC, ולאחר מכן הפלטפורמה יכולה להעביר כספים ישירות לחשבונם. בעוד Stripe Connect הוא סטנדרט, נתמכים גם חלופות כמו YooKassa ו-PayPal Payouts, עם אינטגרציה של 2–4 שבועות.
def create_connect_account(creator):
account = stripe.Account.create(
type='express',
country='RU',
email=creator.email,
capabilities={
'card_payments': {'requested': True},
'transfers': {'requested': True},
},
)
creator.stripe_account_id = account.id
creator.save()
link = stripe.AccountLink.create(
account=account.id,
refresh_url='https://site.com/dashboard/connect/refresh',
return_url='https://site.com/dashboard/connect/complete',
type='account_onboarding',
)
return link.url
def transfer_to_creator(campaign, net_amount):
transfer = stripe.Transfer.create(
amount=int(net_amount * 100),
currency=campaign.currency.lower(),
destination=campaign.creator.stripe_account_id,
metadata={'campaign_id': str(campaign.id)},
)
return transferעמלת הפלטפורמה הסטנדרטית היא 5–8% מהסכום שגויס בתוספת עמלות עסקה. Stripe מאפשר ניכוי עמלות אוטומטי דרך SUM. אנו עוזרים ללקוחות לייעל עמלות: עבור פרויקטים עם מחזור מעל 5 מיליון רובל, אנו מנהלים משא ומתן על תעריפים נמוכים יותר, וחוסכים עד $10,000 בשנה לפלטפורמות עם נפח גבוה.
מה כלול במסירת הפרויקט
מה כלול:
- תיעוד: מפרט טכני, תיאור API, מדריך ניהול.
- קוד מקור: קוד פלטפורמה מלא עם הערות, צינורות CI/CD.
- גישה: הרשאות מנהל, גישה לאחסון, לוח בקרה של Stripe, מאגר קוד.
- הדרכה: שני מפגשים לצוות על ניהול ויצירת קמפיינים.
- תמיכה: חודש אחד של תמיכת אחריות לאחר הפריסה, תיקוני באגים.
תהליך העבודה:
- ניתוח: לימוד מודל עסקי, קהל יעד, אילוצים משפטיים.
- עיצוב: סכימה, בחירת מחסנית טכנולוגית (React/Next.js, Laravel, PostgreSQL, Redis).
- פיתוח: יישום MVP (אימות, קמפיינים, תשלומים).
- בדיקות: בדיקות עומס, מקרי קצה (AON, תנאי מרוץ).
- פריסה: הגדרת CI/CD, ניטור, הדרכת צוות.
לוח זמנים לפיתוח:
| שלב | לוח זמנים |
|---|---|
| MVP (KIA, קמפיינים בסיסיים, תגמולים, Stripe) | 6–8 שבועות |
| פלטפורמת AON מלאה + Connect + יעדי מתיחה + הודעות דוא"ל + אנליטיקה | 3–4 חודשים |
| אינטגרציה של שערי תשלום נוספים (YooKassa, PayPal) | +2–4 שבועות |
לוחות הזמנים משתנים בהתאם למורכבות הדרישות וצרכי ההתאמה האישית. העלות מחושבת באופן פרטני לאחר הערכת היקף העבודה.
צרו קשר להערכת פרויקט — נכין הצעה מסחרית מפורטת תוך 2 ימי עסקים.







