Subscription Regular Delivery System for E-Commerce

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.

Showing 1 of 1 servicesAll 2065 services
Subscription Regular Delivery System for E-Commerce
Complex
~1-2 weeks
FAQ
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

Development of Subscription for Regular Deliveries in E-Commerce

Subscription changes store economics: customer no longer decides to buy each time — they decided once. For store this is predictable cash flow and lower repeat sales cost. Technically one of most complex e-commerce tasks: recurring payments, schedule management, payment failure handling, pause and cancellation management.

System Architecture

Subscription system consists of three independent layers:

1. Subscription core — plans, subscriptions, periods storage 2. Billing engine — recurring payments, retry logic, dunning 3. Fulfillment — order generation and shipment management

Separation is critical: billing failure shouldn't block already-paid subscriptions, plan change shouldn't break current billing cycles.

Data Schema

CREATE TABLE subscription_plans (
    id              BIGSERIAL PRIMARY KEY,
    name            VARCHAR(255) NOT NULL,
    slug            VARCHAR(100) NOT NULL UNIQUE,
    interval_type   VARCHAR(20) NOT NULL, -- day | week | month | year
    interval_count  INTEGER NOT NULL DEFAULT 1,
    trial_days      INTEGER NOT NULL DEFAULT 0,
    is_active       BOOLEAN NOT NULL DEFAULT true
);

CREATE TABLE subscription_plan_items (
    id          BIGSERIAL PRIMARY KEY,
    plan_id     BIGINT NOT NULL REFERENCES subscription_plans(id),
    variant_id  BIGINT NOT NULL REFERENCES product_variants(id),
    qty         INTEGER NOT NULL DEFAULT 1,
    discount    NUMERIC(5,2) NOT NULL DEFAULT 0 -- subscriber discount %
);

CREATE TABLE subscriptions (
    id                  BIGSERIAL PRIMARY KEY,
    user_id             BIGINT NOT NULL REFERENCES users(id),
    plan_id             BIGINT NOT NULL REFERENCES subscription_plans(id),
    status              VARCHAR(50) NOT NULL DEFAULT 'active',
                        -- trial | active | paused | past_due | cancelled | expired
    payment_method_id   VARCHAR(255) NOT NULL, -- token from payment system
    current_period_start TIMESTAMP NOT NULL,
    current_period_end   TIMESTAMP NOT NULL,
    trial_ends_at        TIMESTAMP,
    paused_at            TIMESTAMP,
    resumes_at           TIMESTAMP,
    cancelled_at         TIMESTAMP,
    cancel_reason        TEXT,
    created_at           TIMESTAMP NOT NULL DEFAULT NOW()
);

CREATE TABLE subscription_billing_attempts (
    id              BIGSERIAL PRIMARY KEY,
    subscription_id BIGINT NOT NULL REFERENCES subscriptions(id),
    amount          NUMERIC(12,2) NOT NULL,
    status          VARCHAR(50) NOT NULL, -- success | failed | pending
    payment_id      VARCHAR(255),
    failure_code    VARCHAR(100),
    failure_message TEXT,
    attempted_at    TIMESTAMP NOT NULL DEFAULT NOW(),
    next_retry_at   TIMESTAMP
);

Recurring Payments

Key question — which payment gateway supports card tokenization and server charges without user involvement:

Gateway Recurring Features
Stripe PaymentIntents + SetupIntents Best API, SCA-ready
YuKassa recurring_object Relevant for RF
CloudPayments token-payments Suits CIS
Robokassa Recurring via Recurring Basic

Example with CloudPayments — typical for Belarus/Russia:

class RecurringBillingService
{
    public function chargeSubscription(Subscription $subscription): BillingAttempt
    {
        $amount = $this->calculateAmount($subscription);

        $attempt = BillingAttempt::create([
            'subscription_id' => $subscription->id,
            'amount'          => $amount,
            'status'          => 'pending',
        ]);

        try {
            $result = $this->cloudpayments->chargeToken([
                'Token'       => $subscription->payment_method_id,
                'Amount'      => $amount,
                'Currency'    => 'RUB',
                'AccountId'   => $subscription->user_id,
                'Description' => "Subscription #{$subscription->id}",
            ]);

            $attempt->update(['status' => 'success', 'payment_id' => $result->TransactionId]);
            $this->advancePeriod($subscription);

        } catch (PaymentDeclinedException $e) {
            $attempt->update([
                'status'          => 'failed',
                'failure_code'    => $e->getCode(),
                'failure_message' => $e->getMessage(),
                'next_retry_at'   => $this->calculateRetryTime($attempt),
            ]);
            $this->handleFailedPayment($subscription, $attempt);
        }

        return $attempt;
    }
}

Dunning — Failed Payment Handling

Dunning is retry process on charge failure. Standard scheme:

Day 0: Charge failed → status past_due, attempt 1
Day 3: Retry attempt 2 + "Payment problem" email
Day 7: Retry attempt 3 + email with update card button
Day 14: Retry attempt 4 + SMS
Day 21: Subscription automatically cancelled

Each step — job in queue:

// Scheduled on failed charge
RetrySubscriptionPayment::dispatch($subscription)
    ->delay(now()->addDays(3));

// Scheduled on second failure
SendDunningEmail::dispatch($subscription, 'update_card')
    ->delay(now()->addDays(7));

Important: on failure service still ships current period — subscriber shouldn't suffer for technical issue. Subscription moves to past_due but order created.

Pause Management

Pause — competitive advantage over full cancellation. Subscriber specifies resume date:

class SubscriptionManager
{
    public function pause(Subscription $subscription, Carbon $resumeAt): void
    {
        if ($resumeAt->lte(now())) {
            throw new InvalidResumeDateException();
        }

        $subscription->update([
            'status'      => 'paused',
            'paused_at'   => now(),
            'resumes_at'  => $resumeAt,
        ]);

        // Cancel next charge
        $subscription->pendingBillingJobs()->delete();

        // Schedule resume
        ResumeSubscription::dispatch($subscription)->delay($resumeAt);
    }
}

Max pause duration — configurable (usually 3 months). Pauses mid-period: unused time doesn't expire — next period starts from resumes_at.

Subscription Customization

Subscriber must self-manage:

  • Change composition (add/remove items)
  • Change volume (×1, ×2, etc.)
  • Change interval (bi-weekly → monthly)
  • Skip one period
  • Change delivery address
  • Update card

Mid-cycle plan change requires proration and possible credit or charge:

// Upgrade mid-cycle: charge for difference
$unusedDays = now()->diffInDays($subscription->current_period_end);
$totalDays  = $subscription->current_period_start->diffInDays($subscription->current_period_end);
$prorationFactor = $unusedDays / $totalDays;
$chargeNow = ($newPlan->price - $currentPlan->price) * $prorationFactor;

Order Generation

On shipment day, real order created:

class SubscriptionOrderFactory
{
    public function createFromSubscription(Subscription $subscription): Order
    {
        return DB::transaction(function () use ($subscription) {
            $order = Order::create([
                'user_id'         => $subscription->user_id,
                'source'          => 'subscription',
                'subscription_id' => $subscription->id,
                'delivery_address_id' => $subscription->delivery_address_id,
            ]);

            foreach ($subscription->plan->items as $planItem) {
                $price = $planItem->variant->price
                    * (1 - $planItem->discount / 100);

                $order->items()->create([
                    'variant_id' => $planItem->variant_id,
                    'qty'        => $planItem->qty,
                    'unit_price' => $price,
                ]);

                $this->stockService->reserve($planItem->variant_id, $planItem->qty);
            }

            return $order;
        });
    }
}

Subscriber Personal Cabinet

Minimal UI for subscription management:

  • Delivery history with order view
  • Next shipment date
  • Buttons: "Skip Next", "Pause", "Cancel"
  • Composition and volume management
  • Payment history with receipt download

Implementation Timeline

  • Basic subscriptions + recurring billing (one gateway): 7–10 days
  • Dunning + retry logic + notifications: 3–4 days
  • Pause management, skips, plan upgrade: 3–4 days
  • Subscriber personal cabinet: 3–5 days
  • Analytics (MRR, churn, LTV): +2–3 days

Full system: 3–5 weeks depending on payment gateway and pricing complexity.