Stripe has become the default payment platform for modern SaaS products, marketplaces, and subscription-based applications. Its API is powerful, well-documented, and flexible enough to handle everything from one-off payments to complex billing models.

But when you’re working in ASP.NET Core, especially on production systems, the question isn’t “How do I charge a card?” — it’s:

How do I correctly integrate Stripe into my backend and manage customers in a clean, scalable way?

In this post, we’ll walk through how to integrate the Stripe API with ASP.NET Core and focus specifically on creating Stripe customers, which is the foundation for subscriptions, saved payment methods, invoices, and recurring billing.

This guide assumes:


Why Stripe Customers Matter

Before writing code, it’s important to understand why Stripe customers exist.

A Stripe Customer represents a real user in your system and allows you to:

In most real-world systems:

This separation gives you flexibility and keeps billing concerns isolated from core user logic.


Step 1: Create a Stripe Account and Get API Keys

If you haven’t already:

  1. Create an account at Stripe
  2. Switch to Test Mode
  3. Copy your Secret Key

You’ll use this key server-side only. Never expose it to the frontend.


Step 2: Install Stripe.NET in ASP.NET Core

Stripe provides an official .NET SDK.

Install it via NuGet:

dotnet add package Stripe.net

This package wraps Stripe’s REST API and provides strongly-typed models for customers, payments, subscriptions, and more.


Step 3: Configure Stripe in ASP.NET Core

Add Stripe settings to appsettings.json

{
  "Stripe": {
    "SecretKey": "sk_test_XXXXXXXXXXXXXXXXXXXXXXXX"
  }
}

Create a configuration model

public class StripeSettings
{
    public string SecretKey { get; set; }
}

Register Stripe in Program.cs

builder.Services.Configure<StripeSettings>(
    builder.Configuration.GetSection("Stripe"));

Stripe.StripeConfiguration.ApiKey =
    builder.Configuration["Stripe:SecretKey"];

This ensures Stripe is configured once at application startup.


Step 4: Decide When to Create a Stripe Customer

There are two common approaches:

Option 1: Create a Stripe customer at user registration

✔ Simple
❌ Creates unused customers

Option 2: Create a Stripe customer only when billing starts

✔ Cleaner
✔ More efficient
✔ Preferred for SaaS apps

We’ll follow Option 2, which means creating a Stripe customer on demand.


Step 5: Design a Customer Creation Endpoint

Your backend should own Stripe interactions. The frontend never talks to Stripe directly when creating customers.

Example API endpoint

POST /api/billing/customers

This endpoint:


Step 6: Create a Stripe Customer Service

Encapsulating Stripe logic in a service keeps your controllers clean and testable.

Create IStripeCustomerService

public interface IStripeCustomerService
{
    Task<string> CreateCustomerAsync(
        string email,
        string fullName,
        Guid userId);
}

Implement the service

using Stripe;

public class StripeCustomerService : IStripeCustomerService
{
    private readonly CustomerService _customerService;

    public StripeCustomerService()
    {
        _customerService = new CustomerService();
    }

    public async Task<string> CreateCustomerAsync(
        string email,
        string fullName,
        Guid userId)
    {
        var options = new CustomerCreateOptions
        {
            Email = email,
            Name = fullName,
            Metadata = new Dictionary<string, string>
            {
                { "UserId", userId.ToString() }
            }
        };

        var customer = await _customerService.CreateAsync(options);

        return customer.Id;
    }
}

Why metadata matters

Stripe metadata allows you to:

Always store your internal user ID in Stripe metadata.


Step 7: Register the Service

In Program.cs:

builder.Services.AddScoped<IStripeCustomerService, StripeCustomerService>();

Step 8: Create the API Controller

Request model

public class CreateStripeCustomerRequest
{
    public string Email { get; set; }
    public string FullName { get; set; }
}

Controller implementation

[ApiController]
[Route("api/billing/customers")]
public class BillingController : ControllerBase
{
    private readonly IStripeCustomerService _stripeCustomerService;

    public BillingController(IStripeCustomerService stripeCustomerService)
    {
        _stripeCustomerService = stripeCustomerService;
    }

    [HttpPost]
    public async Task<IActionResult> CreateCustomer(
        CreateStripeCustomerRequest request)
    {
        // Example: retrieve userId from auth context
        var userId = Guid.Parse(User.FindFirst("sub").Value);

        var stripeCustomerId =
            await _stripeCustomerService.CreateCustomerAsync(
                request.Email,
                request.FullName,
                userId);

        // Save stripeCustomerId to your database here

        return Ok(new
        {
            StripeCustomerId = stripeCustomerId
        });
    }
}

Step 9: Persist the Stripe Customer ID

In your database, your user table should include:

StripeCustomerId (nullable)

Once created, you never need to create another Stripe customer for that user.

Every future billing action uses this ID:


Step 10: Common Mistakes to Avoid

❌ Creating customers multiple times

Always check if StripeCustomerId already exists before creating a new one.


❌ Exposing Stripe secret keys

Stripe secret keys must never leave the backend.


❌ Mixing Stripe logic into controllers

Stripe logic belongs in services, not controllers.


❌ Skipping metadata

You’ll regret this when debugging production issues.


Handling Errors Gracefully

Stripe throws rich exceptions. Catch them explicitly:

try
{
    // Stripe call
}
catch (StripeException ex)
{
    // Log ex.StripeError.Message
    throw;
}

Always log Stripe errors with:


Testing in Stripe Test Mode

Stripe provides test data:

Test mode behaves almost identically to live mode — use it extensively.


When to Create Customers vs Payment Intents

FeatureCustomerPayment Intent
User identity
Saved cards
Subscriptions
One-off paymentsOptional

Most SaaS apps use both:


Final Thoughts

Integrating Stripe with ASP.NET Core isn’t hard — but doing it correctly makes a massive difference as your application grows.

By:

You end up with a billing system that is:

Creating Stripe customers is the foundation for everything else Stripe offers. Get this part right, and subscriptions, invoices, and payments become straightforward instead of painful.

Leave a Reply

Your email address will not be published. Required fields are marked *