Blog

Building a Subscription eCommerce from Scratch

snapshot of Boneappetitedk

The Brief

I had a chance to work with a Copenhagen-based company that sells customized dog food on Boneappetitedk.com. The core feature is a calorie calculator that recomends meal plans based on dog's breed, weight, body condition and activity level. Customers then subscribe to receive that plan on a recurring or one-time basis.

I had also written more descriptive information of the project Boneappetitedk

Why Platforms Failed Us

My first instinct was Shopify. I had used it before, it handles payments well, and the ecosystem is enormous. But the subscription model required plugins, and the plugins that came close to what we needed were either too expensive, too generic, or both. Customising the calorie calculation logic on top of a plugin layer was very difficult to implement.

I looked at MedusaJS as a self-hosted alternative. At the time the available version was 1.x, and the documentation for subscriptions was sparse. The project timeline did not allow for that much exploration.

In the end, the cleanest path was a custom build but i knew that i have to do all the fullstack work and deployment by myself. Also i knew that it is hard to get right on the security part becasue the system that involves payment processing need to have hight security standards.

The Stack Decision

I chose what I knew well:

  • .NET Core for the backend and API : I had years of production experience with it and trusted it for the codebase i produce.
  • Vue.js for the frontend app : Client required a customizable app with their own design tweaks so i choose Vue.js for its simplicity.
  • PostgreSQL for the database : open source, reliable, excellent JSON support for the dynamic meal plan data
  • Stripe for payments : I had integrated it before and its subscription primitives are genuinely good

Stripe Subscriptions Are Surprisingly Deep

I expected Stripe to be the easy part. It mostly was, but handling subscription was a bit tricky compared to one-time payments.

A few things that took longer than expected:

Proration : When a customer changes their meal plan mid-cycle, Stripe can automatically calculate what they owe or are owed for the remainder of the billing period. It required careful orchestration between the app and Stripe's API to show the charge clearly.

Webhooks are the source of truth : Your payment intent might succeed on the frontend, but the subscription is not active until Stripe fires customer.subscription.created. I learned early to never update subscription state based on the API response alone but to consider the webhook as the source of truth.

C#
[HttpPost("webhook")]
public async Task<IActionResult> HandleStripeWebhook()
{
    var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
    var stripeEvent = EventUtility.ConstructEvent(
        json,
        Request.Headers["Stripe-Signature"],
        _webhookSecret
    );

    switch (stripeEvent.Type)
    {
        case Events.CustomerSubscriptionCreated:
            var subscription = (Subscription)stripeEvent.Data.Object;
            await _subscriptionService.ActivateAsync(subscription.Id);
            break;

        case Events.InvoicePaymentFailed:
            var invoice = (Invoice)stripeEvent.Data.Object;
            await _notificationService.SendPaymentFailedEmail(invoice.CustomerEmail);
            break;
    }

    return Ok();
}

Failed payments need a recovery flow : Stripe retries failed payments automatically, but you need to handle the invoice.payment_failed event and communicate that to the customer gracefully.

The Part I Enjoyed Most

Working on the calorie calculation logic itself was most rewarding where i closely worked with the vet experts and nutritionists on their nutrition formula. I then mapped the formula on the app to calculate daily calorie needs to their custom made dog food. This project expands my knowledge on custom ecommerce development and experience working with an international team.

Wrapping Up

Building from scratch is not always the right answer. Platforms exist for good reasons and most eCommerce projects are better served by them. But when the core feature is genuinely bespoke and the platform fight would cost more than a custom build then rolling your own is a legitimate choice - provided you have the discipline to do it properly.