Accepting payments with Stripe and ASP.NET Core
Stripe is a service for doing online payments. It's easy to get started with it because they take care of both the frontend and the backend. The frontend scripting is available for many different frameworks like React, Android, IOS, and of course JavaScript and with different levels of customization. The backend is also available in almost any backend framework one could wish for, like .NET. We will discuss the different options for a JavaScript frontend, the .NET package for Stripe as the title suggests, and lastly, the use of Webhooks.
Choosing a frontend option
The frontend uses tokens to represent the card’s information. It does so by preventing the default submission of the form and creating a token of the card’s information by contacting the Stripe servers. This ensures you never need to handle personal card information on your server. It is recommended to use an SSL certificate on the payment site to ensure no one can intercept the information that the client script sends.
Stripe.js is the most basic and lightweight option and the one most customizable. It supplies a clean multipart input where the user can fill in their card nr., expiration date, and CVC nr. The input instantly detects the card type and validates the card nr. and expiration date. You can read more about the integration of Stripe.js in Stripe's guide.
Checkout gets you all you need to make an easy payment form. It is shown as an overlaying form on your website, which is triggered by a button press. The form includes an email field, an option to remember the card information and a multipart input equivalent to the one described for Stripe.js. It is possible to customize the form somewhat and even make custom handlers for interacting with the form. The newest version of Checkout also makes it possible to use 3D Secure. You can read more about the different options for integration of Checkout in Stripe's guide.
Secure payments
From the 4th of September 2019, new security standards will apply for online payments in the EU. So, if you are targeting the European or global customer segment, you should have this in mind. The EU council PSD2 continuously works on making online payment safer for the users. This requires payments to be done using an extra step of security. You have probably already seen such methods and the one most popularly used by different banks is 3D Secure. This is already an integrated part of the Checkout solution but is not a supported feature in Stripe.js without performing some extra steps. To enable this for Stripe.js you will need to use a PaymentIntent
.
Registering at Stripe.com
Before getting started with coding you need an account at Stripe. You get this by going to their register page and filling out the form. After you are done with this you can access the Stripe dashboard. A nice feature that we will need in this experimental test phase is the View test data, which you can enable/disable directly in the menu. This option switches between real and test payments, API keys, and so on. To see and create API keys go to Developers>API keys. You have both publishable and secret keys, but you should never use the secret key on the frontend. These keys differ depending on if you are in testing mode or not, so make sure to switch to live keys when you publish your site.
Creating a payment
We chose to use MVC in our example and have made a Controller for payments and associated views. We chose to use the Checkout for our frontend, because it has the simplest setup.
@{
ViewData["Title"] = "Payment";
}
<div>
<form action="/Payment/Processing" method="POST">
<script src="https://checkout.stripe.com/checkout.js" class="stripe-button"
data-key="pk_test_YourPublishAbleKey"
data-amount="@ViewBag.PaymentAmount"
data-name="[My Company Name]"
data-description="10 rubberducks for 1$ each"
data-image="https://stripe.com/img/documentation/checkout/marketplace.png"
data-locale="en"
data-zip-code="true"
data-label="Pay 10$">
</script>
</form>
</div>
The elements in the form are made by the referenced JavaScript, so this is all you need for the frontend for now. You can change the form to your liking by using the data attributes. An example could be to fill out the amount dynamically using a ViewModel or ViewBag as shown. Other options such as specifying the currency (US Dollars is the default) can be added as well and you can read more about this in Stripe's guide for Checkout. On submit, the form posts the inputs to the action specified, which include the strings stripeToken
and stripeEmail
by default. Stripe.js also creates the input named stripeToken
on submit, so the frontend can easily be switched to that implementation.
To get started on the backend we first need to add the package NuGet Stripe.net to our project. We then need to define the API key that we will use and a good convention is to set this in the Configure
-method in Startup.cs
:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
//Your other configuration
StripeConfiguration.SetApiKey(Configuration["Stripe:TestSecretKey"]);
}
We reference our API key using User Secrets, but you could use App Settings or an inline string instead if that fits your style better. You can read more about User Secrets in our post ASP.NET Core (not that secret) User Secrets Explained.
We can now make the controller for our Payment Views and the post:
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using Stripe;
namespace StripeTest.Controllers
{
public class PaymentController : Controller
{
private int amount = 100;
public IActionResult Index()
{
ViewBag.PaymentAmount = amount;
return View();
}
[HttpPost]
public IActionResult Processing(string stripeToken, string stripeEmail)
{
Dictionary<string, string> Metadata = new Dictionary<string, string>();
Metadata.Add("Product", "RubberDuck");
Metadata.Add("Quantity", "10");
var options = new ChargeCreateOptions
{
Amount = amount,
Currency = "USD",
Description = "Buying 10 rubber ducks",
SourceId = stripeToken,
ReceiptEmail = stripeEmail,
Metadata = Metadata
};
var service = new ChargeService();
Charge charge = service.Create(options);
return View();
}
}
}
We take the two parameters stripeToken
and stripeEmail
in our action, but could add more, like information about the billing address, if you enable that in the frontend (if you have more inputs than this, a better approach would be to use a model for the form, but for now this should be fine).
We would like to add some metadata to the charge which we will use later. This is done using a Dictionary.
We then create a ChargeCreateOption
object which contains the information about the Charge
, where we pass in stripeToken
and the Metadata
Dictionary. We then create a ChargeService
and use the ChargeCreateOption
to make the charge. The Status
of the Charge
is at first pending
and will change to either succeeded
or failed
when the charge has been processed by Stripe.
Respond to the payment
But what now? How do we know the Status
of the charge? There are two different ways of getting the Status
of the charge.
We can Retrieve the charge. This is done using a ChargeService
again and using the action .Get(id)
which takes the id of the Charge
as a parameter, which we would have to save when we create it. The status could still be pending
when we make the request, so we would have to run the request multiple times in intervals to check.
We can use Webhooks. In the Dashboard, we can specify an endpoint that will be called when a Charge
changes status
and use this to take actions on our server when we need to and not make unnecessary requests.
We decide to use Webhooks. We first need to make the Webhook on our page. We simply add another action to our Controller:
private readonly string WebhookSecret = "whsec_OurSigningSecret";
//Previous actions
[HttpPost]
public IActionResult ChargeChange()
{
var json = new StreamReader(HttpContext.Request.Body).ReadToEnd();
try
{
var stripeEvent = EventUtility.ConstructEvent(json,
Request.Headers["Stripe-Signature"], WebhookSecret, throwOnApiVersionMismatch: true);
Charge charge = (Charge)stripeEvent.Data.Object;
switch (charge.Status)
{
case "succeeded":
//This is an example of what to do after a charge is successful
charge.Metadata.TryGetValue("Product", out string Product);
charge.Metadata.TryGetValue("Quantity", out string Quantity);
Database.ReduceStock(Product, Quantity);
break;
case "failed":
//Code to execute on a failed charge
break;
}
}
catch (Exception e)
{
e.Ship(HttpContext);
return BadRequest();
}
return Ok();
}
We have made the Action ChargeChange
which starts by reading the body that was passed with the post. We then try to construct the event and cast it to the Charge
type. Depending on the Status
we can act differently, like updating our stock status for our imaginary webshop.
We also use elmah.io to ship the possible exception so we have an overview of the errors that happen in this critical part of a site. You can read more about how to do this in our documentation Logging to elmah.io from ASP.NET Core. An error that one could experience in the start could be an API Version Mismatch since we have chosen to throw an error for these mismatches in the construction of the event.
Now we just need to have Stripe call our Webhook, which is done in the dashboard in the menu Developers>Webhooks and selecting Add endpoint. We then enter the path of our webhook, which would be something like:
https://mysite.com/Payment/ChargeChange/
We can also choose to have it only access our webhook for certain types of changes, like only when it changes to charge.succeeded
or charge.failed
. After this, you can go into the Endpoint in the list and see our Webhook Secret.
This is a basic start for how to work with Stripe. They have many more features we didn't get to cover, including the new PaymentIntent
and the setup of Subscriptions
. It's easy to imagine many more technologies that Stripe could be used together with, like SignalR for real-time response on a status update of a Charge
.
elmah.io: Error logging and Uptime Monitoring for your web apps
This blog post is brought to you by elmah.io. elmah.io is error logging, uptime monitoring, deployment tracking, and service heartbeats for your .NET and JavaScript applications. Stop relying on your users to notify you when something is wrong or dig through hundreds of megabytes of log files spread across servers. With elmah.io, we store all of your log messages, notify you through popular channels like email, Slack, and Microsoft Teams, and help you fix errors fast.
See how we can help you monitor your website for crashes Monitor your website