ASP.NET Core  

Building SaaS Platforms with Subscription Management using Stripe and ASP.NET Core

Introduction

The Software as a Service (SaaS) model has revolutionized how software is delivered and monetized. Instead of one-time purchases, users subscribe to a service on a recurring basis — monthly, quarterly, or annually.

To manage these subscriptions securely and efficiently, developers often integrate trusted payment gateways like Stripe . This article explores how to build a SaaS platform with subscription management using Stripe and ASP.NET Core , covering key architectural concepts, implementation steps, webhook handling, and real-world best practices.

Understanding SaaS Subscription Architecture

In a subscription-based platform, you need to manage:

  • User accounts (signup, authentication, roles)

  • Subscription plans (free, standard, premium tiers)

  • Recurring billing (via Stripe)

  • Webhooks for payment events (e.g., renewal, cancellation)

  • Access control (features based on plan type)

High-Level Workflow

  
    [User Registers] 
      ↓
[Selects Subscription Plan]
      ↓
[Redirected to Stripe Checkout]
      ↓
[Payment Confirmation via Stripe Webhook]
      ↓
[Subscription Activated in ASP.NET Core DB]
      ↓
[Access Granted Based on Plan Level]
  

Tech Stack Overview

LayerTechnologyPurpose
FrontendAngular / ReactSubscription UI & Checkout
BackendASP.NET Core 9API endpoints, logic, webhook handling
DatabaseSQL ServerStore users, subscriptions, invoices
Payment GatewayStripeBilling, subscription, and invoicing
AuthenticationASP.NET Identity + JWTSecure user sessions

Setting up Stripe

Step 1: Create a Stripe Account

Go to https://dashboard.stripe.com → Create a test account.

Step 2: Obtain API Keys

Navigate to Developers → API Keys

  • Publishable Key → for frontend

  • Secret Key → for backend

Step 3: Create Subscription Plans

In the Stripe Dashboard:

  • Products → Add Product → “Pro Plan”, “Enterprise Plan”

  • Add Pricing → Monthly / Yearly
    Each plan will have a Price ID (e.g., price_12345 ).

Implementing Stripe in ASP.NET Core

Step 1: Install Stripe NuGet Package

  
    dotnet add package Stripe.net
  

Step 2: Configure Stripe Settings

In appsettings.json :

  
    {"Stripe": {
    "PublishableKey": "pk_test_abc123",
    "SecretKey": "sk_test_xyz789",
    "WebhookSecret": "whsec_XXXX"}}
  

In Program.cs :

  
    builder.Services.Configure<StripeSettings>(builder.Configuration.GetSection("Stripe"));
StripeConfiguration.ApiKey = builder.Configuration["Stripe:SecretKey"];
  

Step 3: Create the Checkout Session

In SubscriptionController.cs :

  
    using Microsoft.AspNetCore.Mvc;
using Stripe.Checkout;

[ApiController]
[Route("api/[controller]")]
public class SubscriptionController : ControllerBase
{
    [HttpPost("create-checkout-session")]
    public ActionResult CreateCheckoutSession([FromBody] string priceId)
    {
        var domain = "https://localhost:4200";
        var options = new SessionCreateOptions
        {
            SuccessUrl = $"{domain}/success?session_id={{CHECKOUT_SESSION_ID}}",
            CancelUrl = $"{domain}/cancel",
            PaymentMethodTypes = new List<string> { "card" },
            Mode = "subscription",
            LineItems = new List<SessionLineItemOptions>
            {
                new SessionLineItemOptions
                {
                    Price = priceId,
                    Quantity = 1
                }
            }
        };

        var service = new SessionService();
        var session = service.Create(options);
        return Ok(new { url = session.Url });
    }
}
  

Step 4: Angular Frontend Integration

Angular Service Example:

  
    createCheckout(priceId: string) {
  return this.http.post<any>('https://localhost:5001/api/subscription/create-checkout-session', { priceId });
}
  

Component Example:

  
    this.subscriptionService.createCheckout('price_12345').subscribe((res) => {
  window.location.href = res.url;
});
  

This redirects the user to Stripe’s hosted checkout page securely.

Handling Stripe Webhooks

Webhooks are essential for automatically updating your system when events occur — such as successful payments, cancellations, or subscription renewals.

Step 1: Create Webhook Endpoint

  
    [HttpPost("webhook")]
public async Task<IActionResult> StripeWebhook()
{
    var json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync();
    try
    {
        var stripeEvent = EventUtility.ConstructEvent(
            json,
            Request.Headers["Stripe-Signature"],
            _config["Stripe:WebhookSecret"]
        );

        if (stripeEvent.Type == Events.InvoicePaymentSucceeded)
        {
            var invoice = stripeEvent.Data.Object as Stripe.Invoice;
            // Update subscription status in DB
        }
        else if (stripeEvent.Type == Events.CustomerSubscriptionDeleted)
        {
            var subscription = stripeEvent.Data.Object as Stripe.Subscription;
            // Mark subscription as canceled
        }

        return Ok();
    }
    catch (StripeException e)
    {
        return BadRequest(e.Message);
    }
}
  

Step 2: Register Webhook in Stripe Dashboard

Go to:
Developers → Webhooks → Add Endpoint
Set URL to your deployed API:
https://myapi.com/api/subscription/webhook

Managing Subscriptions in Database

You’ll need database tables like:

  
    CREATE TABLE Users (
    UserId INT PRIMARY KEY IDENTITY,
    Email NVARCHAR(100),
    StripeCustomerId NVARCHAR(50)
);

CREATE TABLE Subscriptions (
    SubscriptionId INT PRIMARY KEY IDENTITY,
    UserId INT FOREIGN KEY REFERENCES Users(UserId),
    StripeSubscriptionId NVARCHAR(50),
    PlanName NVARCHAR(50),
    Status NVARCHAR(20),
    StartDate DATETIME,
    EndDate DATETIME
);
  

Every time a webhook event is received, update these tables accordingly.

Role-Based Access Control by Subscription Tier

In your ASP.NET Core middleware , you can conditionally grant access:

  
    [Authorize]
[HttpGet("pro-feature")]
public IActionResult GetProFeature()
{
    var plan = GetUserPlan(User.Identity.Name);
    if (plan != "Pro")
        return Forbid();

    return Ok("Welcome to Pro Features!");
}
  

This ensures only paid users can access premium APIs or UI components.

CI/CD Integration and Environment Configuration

Never store your Stripe keys directly in source code.
Use Azure Key Vault , AWS Secrets Manager , or CI/CD environment variables:

  
    dotnet user-secrets set "Stripe:SecretKey" "sk_live_xxx"
  

In GitHub Actions / Jenkins , inject secrets securely for staging and production environments.

Handling Cancellations and Renewals

Use Stripe’s Billing Portal for allowing users to manage subscriptions:

  
    [HttpGet("billing-portal")]
public IActionResult CreateBillingPortal(string customerId)
{
    var options = new Stripe.BillingPortal.SessionCreateOptions
    {
        Customer = customerId,
        ReturnUrl = "https://localhost:4200/dashboard"
    };

    var service = new Stripe.BillingPortal.SessionService();
    var session = service.Create(options);
    return Ok(new { url = session.Url });
}
  

Performance and Scalability Considerations

  1. Use async operations for API calls and webhook handling.

  2. Implement retry logic for webhook events.

  3. Cache plan metadata (using MemoryCache or Redis ) to reduce Stripe API calls.

  4. Use background jobs (Hangfire, Azure Functions) for invoice or renewal sync.

  5. Secure all API routes with HTTPS and token-based authentication.

Example Technical Workflow

  
    [User Signup] 
    ↓
[Chooses Plan] 
    ↓
[Stripe Checkout Session Created via ASP.NET Core API]
    ↓
[Payment Processed by Stripe]
    ↓
[Webhook Receives Confirmation]
    ↓
[Database Updates Subscription Status]
    ↓
[Angular Frontend Reflects Active Plan]
  

Best Practices

  • Test using Stripe Test Mode before going live.

  • Validate all incoming webhook events.

  • Log all subscription changes for auditing.

  • Use idempotent webhook handling to prevent duplicate DB updates.

  • Offer grace periods for expired subscriptions.

  • Keep your pricing configuration in Stripe, not hardcoded.

Conclusion

Building a SaaS platform with Stripe and ASP.NET Core provides a robust, secure, and scalable foundation for recurring billing and user subscription management.

By combining ASP.NET Core’s powerful backend capabilities with Stripe’s reliable billing APIs, developers can create end-to-end subscription systems — from checkout to renewal, cancellation, and access control — with minimal manual effort.

With thoughtful architecture, proper webhook handling, and secure storage of sensitive data, your SaaS platform will be ready to scale confidently for enterprise users.