Crowdlending Platform Development

Our company is engaged in the development, support and maintenance of sites of any complexity. From simple one-page sites to large-scale cluster systems built on micro services. Experience of developers is confirmed by certificates from vendors.
Development and maintenance of all types of websites:
Informational websites or web applications
Business card websites, landing pages, corporate websites, online catalogs, quizzes, promo websites, blogs, news resources, informational portals, forums, aggregators
E-commerce websites or web applications
Online stores, B2B portals, marketplaces, online exchanges, cashback websites, exchanges, dropshipping platforms, product parsers
Business process management web applications
CRM systems, ERP systems, corporate portals, production management systems, information parsers
Electronic service websites or web applications
Classified ads platforms, online schools, online cinemas, website builders, portals for electronic services, video hosting platforms, thematic portals

These are just some of the technical types of websites we work with, and each of them can have its own specific features and functionality, as well as be customized to meet the specific needs and goals of the client.

Our competencies:
Development stages
Latest works
  • image_web-applications_feedme_466_0.webp
    Development of a web application for FEEDME
    1161
  • image_ecommerce_furnoro_435_0.webp
    Development of an online store for the company FURNORO
    1041
  • image_crm_enviok_479_0.webp
    Development of a web application for Enviok
    822
  • image_crm_chasseurs_493_0.webp
    CRM development for Chasseurs
    847
  • image_website-sbh_0.png
    Website development for SBH Partners
    999
  • image_website-_0.png
    Website development for Red Pear
    451

Crowdlending Platform Development

Crowdlending is P2P lending: borrowers get money from investor pool, platform organizes process, scoring, payment management and reserve fund. Unlike crowdfunding, money returned with interest — creates fundamentally different financial mechanics and regulatory requirements.

Key Differences from Crowdfunding

Crowdlending works with financial instruments. In Russia, crowdlending platform activity regulated by Federal Law No. 259-FZ "On Attraction of Investments Using Investment Platforms". Platform must be registered in Bank of Russia registry. Affects architecture: all transactions via nominal accounts, data stored per regulator requirements.

Data Schema

CREATE TABLE loan_requests (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    borrower_id     UUID NOT NULL REFERENCES users(id),
    amount          NUMERIC(15,2) NOT NULL,
    currency        CHAR(3) NOT NULL DEFAULT 'RUB',
    term_months     INTEGER NOT NULL,
    rate_annual     NUMERIC(5,2) NOT NULL,
    purpose         TEXT NOT NULL,
    status          VARCHAR(30) NOT NULL DEFAULT 'pending'
                    CHECK (status IN (
                        'pending','scoring','approved','funding',
                        'funded','active','repaid','defaulted','rejected'
                    )),
    funded_amount   NUMERIC(15,2) NOT NULL DEFAULT 0,
    risk_grade      CHAR(1),
    scoring_score   INTEGER,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE investments (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    investor_id     UUID NOT NULL REFERENCES users(id),
    loan_id         UUID NOT NULL REFERENCES loan_requests(id),
    amount          NUMERIC(15,2) NOT NULL,
    status          VARCHAR(20) NOT NULL DEFAULT 'pending'
                    CHECK (status IN ('pending','active','repaid','defaulted')),
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE TABLE repayment_schedule (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    loan_id         UUID NOT NULL REFERENCES loan_requests(id),
    payment_num     INTEGER NOT NULL,
    due_date        DATE NOT NULL,
    principal       NUMERIC(15,2) NOT NULL,
    interest        NUMERIC(15,2) NOT NULL,
    status          VARCHAR(20) NOT NULL DEFAULT 'pending'
                    CHECK (status IN ('pending','paid','overdue','written_off')),
    paid_at         TIMESTAMPTZ,
    UNIQUE (loan_id, payment_num)
);

CREATE TABLE wallets (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id         UUID NOT NULL REFERENCES users(id),
    type            VARCHAR(20) NOT NULL CHECK (type IN ('investor','borrower')),
    balance         NUMERIC(15,2) NOT NULL DEFAULT 0,
    reserved        NUMERIC(15,2) NOT NULL DEFAULT 0,
    UNIQUE (user_id, type)
);

CREATE TABLE wallet_transactions (
    id              UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    wallet_id       UUID NOT NULL REFERENCES wallets(id),
    type            VARCHAR(30) NOT NULL,
    amount          NUMERIC(15,2) NOT NULL,
    balance_after   NUMERIC(15,2) NOT NULL,
    reference_id    UUID,
    description     TEXT,
    created_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

Annuity Repayment Schedule Calculation

from decimal import Decimal, ROUND_HALF_UP
from datetime import date
from dateutil.relativedelta import relativedelta


def calculate_annuity_schedule(
    loan_amount: Decimal,
    annual_rate: Decimal,
    term_months: int,
    start_date: date
) -> list[dict]:
    """Annuity repayment schedule"""
    monthly_rate = annual_rate / 100 / 12

    # Annuity coefficient
    k = monthly_rate * (1 + monthly_rate) ** term_months / \
        ((1 + monthly_rate) ** term_months - 1)

    monthly_payment = (loan_amount * k).quantize(Decimal('0.01'), ROUND_HALF_UP)

    schedule = []
    balance = loan_amount
    payment_date = start_date

    for num in range(1, term_months + 1):
        payment_date = payment_date + relativedelta(months=1)
        interest = (balance * monthly_rate).quantize(Decimal('0.01'), ROUND_HALF_UP)

        if num < term_months:
            principal = monthly_payment - interest
        else:
            principal = balance

        balance -= principal

        schedule.append({
            'payment_num': num,
            'due_date': payment_date,
            'principal': principal,
            'interest': interest,
            'total': principal + interest,
            'balance_after': max(balance, Decimal('0')),
        })

    return schedule

Auto-Investment

Key function for investor retention — automatic fund distribution per configured criteria:

class AutoInvestRule(models.Model):
    investor = models.OneToOneField(User, on_delete=models.CASCADE)
    is_active = models.BooleanField(default=True)
    max_amount_per_loan = models.DecimalField(max_digits=15, decimal_places=2)
    min_loan_amount = models.DecimalField(max_digits=15, decimal_places=2, default=50000)
    max_loan_amount = models.DecimalField(max_digits=15, decimal_places=2, default=1000000)
    allowed_grades = models.JSONField(default=list)
    min_rate = models.DecimalField(max_digits=5, decimal_places=2, default=15)
    max_term_months = models.IntegerField(default=24)
    reinvest_returns = models.BooleanField(default=True)


@shared_task
def run_auto_invest():
    """Run every 15 minutes"""
    new_loans = LoanRequest.objects.filter(
        status='funding',
        funded_amount__lt=models.F('amount')
    )

    for loan in new_loans:
        rules = AutoInvestRule.objects.filter(
            is_active=True,
            allowed_grades__contains=loan.risk_grade,
            min_rate__lte=loan.rate_annual,
            max_term_months__gte=loan.term_months,
        )

        for rule in rules:
            wallet = Wallet.objects.select_for_update().get(
                user=rule.investor, type='investor'
            )
            available = wallet.balance - wallet.reserved
            invest_amount = min(rule.max_amount_per_loan, available)

            if invest_amount >= Decimal('1000'):
                create_investment(rule.investor, loan, invest_amount, wallet)

Accrual and Payment Processing

@shared_task
def process_due_payments():
    """Run daily"""
    today = date.today()
    due_payments = RepaymentSchedule.objects.filter(
        due_date=today,
        status='pending',
        loan__status='active'
    ).select_related('loan__borrower__wallet')

    for payment in due_payments:
        borrower_wallet = payment.loan.borrower.wallet

        if borrower_wallet.balance >= payment.principal + payment.interest:
            process_payment(payment)
        else:
            payment.status = 'overdue'
            payment.save()
            send_overdue_notification.delay(payment.id)
            accrue_late_fee.delay(payment.id)


def process_payment(payment):
    total = payment.principal + payment.interest
    with transaction.atomic():
        debit_wallet(payment.loan.borrower, total, 'loan_payment', payment.loan_id)
        distribute_to_investors(payment)
        payment.status = 'paid'
        payment.paid_at = timezone.now()
        payment.save()
        check_loan_completion(payment.loan)

Reserve Fund

Investor protection on borrower default:

RESERVE_FUND_RATE = Decimal('0.02')  # 2% of each loan

def fund_reserve_on_disbursement(loan):
    reserve_amount = (loan.amount * RESERVE_FUND_RATE).quantize(Decimal('0.01'))
    ReserveFund.objects.create(
        loan=loan,
        amount=reserve_amount,
        status='active'
    )

def cover_default_from_reserve(loan):
    """On default — compensate investors from reserve"""
    outstanding = loan.investments.filter(
        status='active'
    ).aggregate(total=Sum('amount'))['total'] or 0

    reserve = ReserveFund.objects.filter(status='active').aggregate(
        total=Sum('amount')
    )['total'] or 0

    coverage = min(outstanding, reserve)
    distribute_reserve_coverage(loan, coverage)

Documentation and Agreements

Each investment accompanied by document. Generate via template:

from reportlab.pdfgen import canvas
from jinja2 import Environment, FileSystemLoader


def generate_loan_agreement(loan, investor, investment):
    env = Environment(loader=FileSystemLoader('templates/legal'))
    template = env.get_template('loan_agreement.html')

    html = template.render(
        loan=loan,
        investor=investor,
        investment=investment,
        schedule=loan.repayment_schedule.all(),
        generated_at=timezone.now(),
    )

    from weasyprint import HTML
    pdf = HTML(string=html).write_pdf()

    path = f'documents/agreements/{loan.id}/{investor.id}.pdf'
    default_storage.save(path, ContentFile(pdf))
    return path

Timeframes

MVP P2P platform with basic scoring, wallets, repayment schedule and dashboards: 4–5 months. Minimum for regulated environment — nominal accounts, audit logs of all transactions and basic documentation. Full platform with auto-invest, reserve fund and secondary market: 8–12 months.