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!
Don't stop learning!
There is so much to discover about C#. That's why I am making my favorite tips and tricks available for free. Enter your email address below to become a better .NET developer.
Did you know?
Our beautiful, multi-column C# reference guides contain more than 150 tips and examples to make it even easier to write better code.
Get your cheat sheets