Разработка краудфандинговой платформы

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.
Разработка и обслуживание любых видов сайтов:
Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Разработка краудфандинговой платформы
Сложная
от 2 недель до 3 месяцев
Часто задаваемые вопросы
Наши компетенции:
Этапы разработки
Последние работы
  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Разработка краудфандинговой платформы

Краудфандинговая платформа — это не просто «сайт со страницей оплаты». Это система управления кампаниями, сбором средств, выплатами создателям, коммуникацией с бэкерами и соблюдением требований платёжного законодательства. Разница между работающим продуктом и списком фич — в деталях реализации каждой из этих частей.

Модели финансирования

Прежде чем проектировать схему данных, нужно определиться с моделью:

All-or-Nothing (AON) — средства списываются только при достижении цели. Kickstarter-модель. Технически сложнее: нужна pre-authorization (холдирование) или отложенный capture.

Keep-it-All (KIA) — средства списываются сразу, создатель получает всё независимо от цели. Indiegogo-модель. Проще реализовать, но юридически требует чёткого описания условий возврата.

Гибридная — цель фиксированная, но при превышении открываются stretch goals. Самый сложный вариант с точки зрения логики состояний кампании.

Схема данных

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 'RUB',
    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),   -- Stripe PaymentIntent или ЮKassa payment_id
    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,            -- NULL = без ограничений
    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');

Платёжный флоу для AON-модели

AON требует двухэтапной оплаты: сначала авторизация (холдирование), потом capture при успехе кампании.

Stripe:

# Создание PaymentIntent с manual capture
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:
            # PaymentIntent истёк или уже отменён
            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 держит authorization максимум 7 дней для карт (до 30 дней для некоторых типов). Для кампаний длиннее 7 дней нужна другая стратегия — например, сохранение платёжного метода и capture в последний день.

Сохранение карты для длинных кампаний

# Сохраняем карту через SetupIntent, charge в конце кампании
def save_payment_method(user, card_token):
    # Создаём или получаем Stripe Customer
    if not user.stripe_customer_id:
        customer = stripe.Customer.create(
            email=user.email,
            metadata={'user_id': str(user.id)}
        )
        user.stripe_customer_id = customer.id
        user.save()

    setup_intent = stripe.SetupIntent.create(
        customer=user.stripe_customer_id,
        payment_method=card_token,
        confirm=True,
        usage='off_session',
    )
    return setup_intent.payment_method


def charge_saved_card(pledge):
    intent = stripe.PaymentIntent.create(
        amount=int(pledge.amount * 100),
        currency='rub',
        customer=pledge.backer.stripe_customer_id,
        payment_method=pledge.payment_method_id,
        confirm=True,
        off_session=True,
        metadata={'pledge_id': str(pledge.id)},
    )
    return intent

Celery-задачи для завершения кампаний

# tasks.py
from celery import shared_task
from django.utils import timezone


@shared_task
def check_campaign_deadline(campaign_id):
    campaign = Campaign.objects.get(id=campaign_id)

    if campaign.ends_at > timezone.now():
        return  # Ещё не завершилась

    total = Pledge.objects.filter(
        campaign=campaign,
        status__in=['authorized', 'captured']
    ).aggregate(total=Sum('amount'))['total'] or 0

    if campaign.model == 'aon' and total < campaign.goal_amount:
        campaign.status = 'failed'
        campaign.save()
        cancel_pledges_for_campaign.delay(campaign_id)
    else:
        campaign.status = 'funded'
        campaign.save()
        capture_pledges_for_campaign.delay(campaign_id)
        notify_creator_campaign_success.delay(campaign_id)


# Celery beat расписание
CELERY_BEAT_SCHEDULE = {
    'check-campaign-deadlines': {
        'task': 'campaigns.tasks.check_all_deadlines',
        'schedule': crontab(minute='*/15'),
    },
}

Выплаты создателям

Stripe Connect — стандарт для маркетплейсов. Создатели подключают свои банковские счета через onboarding:

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()

    # Ссылка на onboarding
    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):
    """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

Фронтенд: прогресс-бар и real-time обновления

// Прогресс кампании через Server-Sent Events
// GET /api/campaigns/:id/progress

export async function* campaignProgressStream(campaignId: string) {
  while (true) {
    const stats = await getCampaignStats(campaignId)
    yield `data: ${JSON.stringify(stats)}\n\n`
    await sleep(10_000) // обновляем каждые 10 секунд
  }
}

// React hook
function useCampaignProgress(campaignId: string) {
  const [progress, setProgress] = useState<CampaignStats | null>(null)

  useEffect(() => {
    const source = new EventSource(`/api/campaigns/${campaignId}/progress`)
    source.onmessage = (e) => setProgress(JSON.parse(e.data))
    return () => source.close()
  }, [campaignId])

  return progress
}

Комиссионная модель

Стандарт: платформа берёт 5–8% от собранной суммы плюс транзакционная комиссия Stripe (1.4% + 25₽ для европейских карт, 2.9% + 30¢ для остальных). Логика списания платформенной комиссии через application_fee_amount при создании PaymentIntent:

intent = stripe.PaymentIntent.create(
    amount=10_000,  # 100 рублей
    currency='rub',
    application_fee_amount=600,  # 6% платформенная комиссия
    transfer_data={'destination': creator.stripe_account_id},
)

Сроки

MVP краудфандинговой платформы (KIA-модель, Stripe, кампании, награды, базовый личный кабинет): 6–8 недель. Полная AON-платформа с Connect, stretch goals, email-уведомлениями и аналитикой: 3–4 месяца.