Stripe Customer Credit Card Payment with Blazor

In the previous tutorial, you learned how to link a Stripe Connect account with a local user store in a Blazor application. If, instead, you need a way to collect payment information from your customers, this tutorial will help you understand the Stripe customer flow.

Source Code

Source code for this project, and for others in the Blazor and Stripe series, are available on Github.

Disclaimer

This tutorial does not use Stripe’s browser-side library, Stripe.js, to collect credit card information. As a result, credit card information will be passed through your server if you are working with a server-side Blazor project. This tutorial is intended for testing purposes only. A subsequent tutorial will demonstrate the preferred method where a token is generated client-side using Stripe.js and JSInterop.

What you will need

This guide is built around a Blazor application, but you can also use the principles in an MVC or Razor pages project. We will be using the Stripe.net Nuget package. Install it by issuing the following command in the Package Manager Console.

Install-Package Stripe.net

You can easily configure Stripe.net by setting some global variables in your project’s Startup.cs file.

StripeConfiguration.ApiKey = Configuration.GetSection("Stripe")["ApiKey"];

You can find your API key in your Stripe developer dashboard. Don’t forget to use the Test key if your application is not production-ready. The above code is using the Secret Manager to store sensitive data. Please see the Secret Manager tutorial if you have questions about keeping secrets out of your source code.

Create Data Model

In this tutorial, I will be collecting a customer’s credit card information. Depending on your application needs, you may need to collect ACH bank account information instead.

First, create a model that will represent the Card data required by Stripe.

public class CardModel
{
    [Required, Display(Name = "Card Holder Name")]
    public string CardHolderName { get; set; }
 
    [Required, CreditCard, Display(Name = "Card Number")]
    public string CardNumber{ get; set; }
 
    [Required, Display(Name = "Expiration Month"), Range(0, 12)]
    public long? CardExpiryMonth { get; set; }
 
    [Required, Display(Name = "Expiration Year"), Range(2020,2100)]
    public long? CardExpiryYear { get; set; }
 
    [Required, Display(Name = "CVC Security Code"),
        RegularExpression("^[0-9]*$", ErrorMessage = "CVC security code can only contain numbers")]
    public string CardCvc { get; set; }
}

I went ahead and added some data validation annotations to this model.

Design Customer Setup Page

Now, create a new page in your Blazor web app called CustomerSetup.razor with the following route and using directive.

@page "/Account/CardSetup"
@using Stripe

On this page, we will create a form that accepts a user’s payment information. Since we are interested in creating a Stripe Customer and linking it to an existing user in our database, let’s ensure a user is logged in in order to see the form. We can do this using the AuthorizeView component.

<AuthorizeView Context="authContext">
	<Authorized>

	</Authorized>
</AuthorizeView>

Your form may look like the following.

<EditForm OnValidSubmit="@FormSubmit" Model="@accountOptions">
    <DataAnnotationsValidator />
    <ValidationSummary />
    <h4>Add Credit Card</h4>
    <div class="form-group row">
        <label for="cardholderName" class="col-sm-6 col-form-label">Cardholder Name:</label>
        <input id="cardholderMame" class="form-control col-sm-6 input-lg" type="text"
               @bind="accountOptions.CardHolderName">
    </div>
    <div class="form-group row">
        <label for="cardNum" class="col-sm-6 col-form-label">Card Number:</label>
        <input id="cardNum" class="form-control col-sm-6 input-lg" type="number"
               @bind="accountOptions.CardNumber" />
    </div>
    <div class="form-group row">
        <label for="cardExpiryMo" class="col-sm-6 col-form-label">Expiration Month:</label>
        <input id="cardExpiryMo" class="form-control col-sm-6 input-lg" type="number"
               @bind="accountOptions.CardExpiryMonth" />
    </div>
    <div class="form-group row">
        <label for="cardExpiryYear" class="col-sm-6 col-form-label">Expiration Year:</label>
        <input id="cardExpiryYear" class="form-control col-sm-6 input-lg" type="number"
               @bind="accountOptions.CardExpiryYear" />
    </div>
    <div class="form-group row">
        <label for="cardCvc" class="col-sm-6 col-form-label">CVC Security Code:</label>
        <input id="cardCvc" class="form-control col-sm-6 input-lg" type="number"
               @bind="accountOptions.CardCvc" />
    </div>
    <div class="row">
        <div class="col-12 text-right">
            <button class="btn btn-primary" type="submit">Submit</button>
        </div>
    </div>
</EditForm>

Configure Customer Setup Logic

Next, you must configure your form’s binding and OnSubmit handler. Create an instance of the model you created previously to be consumed by the form, and create the FormSubmit method. The name of this method should correspond to the name you provided as the OnValidSubmit callback of the EditForm component.

@code {
    private Card card;
    private CardModel accountOptions = new CardModel();

    private async Task FormSubmit()
    {
	
    }
}

The FormSubmit() method should generate a Stripe token based on the credit card information. Then it should create a customer and link the credit card information (by token) to that customer. In a real application, you would only save the newly created customer’s Stripe Customer ID to your local database.

private async Task FormSubmit()
{
    var cardOptions = new CreditCardOptions()
    {
        Number = accountOptions.CardNumber,
        ExpMonth = accountOptions.CardExpiryMonth,
        ExpYear = accountOptions.CardExpiryYear,
        Cvc = accountOptions.CardCvc
    };
 
    string cardToken = CreateCardToken(cardOptions);
 
    string customerId = CreateCustomer(accountOptions.CardHolderName);
    //this is where you would save customerId to your app's user table
 
    card = LinkCardToCustomer(customerId, cardToken);
}

Above is how that flow might come together. Next, we will write the CreateCardToken(), CreateCustomer(), and LinkCardToCustomer() methods.

private string CreateCardToken(CreditCardOptions cardOptions)
{
    var options = new TokenCreateOptions
    {
        Card = cardOptions
    };
 
    return new TokenService().Create(options).Id;
}
private string CreateCustomer(string description)
{
    var options = new CustomerCreateOptions
    {
        Description = description
    };
 
    return new CustomerService().Create(options).Id;
}
private Card LinkCardToCustomer(string customerId, string accountToken)
{
    var options = new CustomerUpdateOptions
    {
        Source = accountToken,
    };
 
    return new CustomerService().Update(customerId, options).Sources.FirstOrDefault() as Card;
}

A Note about Error Handling

When designing the page’s logic, you should include appropriate error handling. For example, when generating a token from account information, you should confirm that the token creation succeeded before proceeding. You can do this by catching StripeException errors and returning an empty string from the CreateCardToken() method if token creation failed.

private string CreateCardToken(CreditCardOptions cardOptions)
{
    var tokenOptions = new TokenCreateOptions
    {
        Card = cardOptions
    };
 
    try
    {
        return new TokenService().Create(tokenOptions).Id;
    }
    catch (StripeException e)
    {
        return "";
    }
}

Then, only proceed to customer creation if the token was successfully created.

string cardToken = CreateCardToken(cardOptions);
 
if (cardToken != null)
{
    ...
}

The Bottom Line

In this tutorial, you learned how to work with the Stripe.net library to integrate Stripe payment and customer information into your Blazor application. This is a critical step if you wish to monetize your application. Hopefully this tutorial has demonstrated that Blazor is, indeed, ready to be used in real-world applications!


Was this article helpful? Share it with your friends:

Share on email
Share on facebook
Share on twitter
Share on reddit

Leave a Comment

Looking for more?

You have visited our site before, and we appreciate you!

If you found the tutorials helpful, enter your email address for more free C# tips and tricks.

Can't get enough C#?

Enter your email address for more free C# tutorials and tips.