Stripe Subscriptions & Payment Authentication (3DSecure) using Stripe API, React and Asp.NET

Mahdi Karimipour
11 min readAug 3, 2021

--

How to process Stripe subscriptions with/without trial using React, Asp.NET and Stripe APIs. This also involves performing payment authentication with the customer’s bank, if required.

The Need

Out of the two common types of payments, one time payment and subscriptions, in this post I focus on subscriptions which are ideal for recurring and ongoing payments.

In the last post, I covered how to process one time payments with Stripe APIs, so if you haven’t read it, refer to One-time-payments with Stripe as a relevant topic to this post.

End Result

You can find the live demo of this guide right here at Pellerex. By following this post, at the end you will have the complete flow including the frontend React App and backend Asp.NET APIs to integrate and process payments with Stripe API safely.

By the way, this topic belongs to the series to set up Payments with React, Asp.NET and Stripe.

  1. Capturing Billing and Payment Details
  2. One Time Payment
  3. Subscriptions
  4. Cancel Subscription
  5. Switch Subscription
  6. Callbacks

Libraries

I am using Stripe backend library that you can fetch from here Stripe.net.

Also in your backend API, you would need to set up your Stripe Secret key like below:

As you see I am reading my secrets from the secret store, so if you like to read more on how I manage my API secrets, refer to Secret Management for Asp.NET APIs.

Also as a prerequisite to this post, to read more on how to set up your React app for Stripe to read card details securely, refer to Step by Step Guide to Using Stripe Elements with React.

Flow

This is important as it sets the scene for the rest of this post, and I will walk through every step.

  1. Frontend: Capture billing and payment details, already covered here
  2. Frontend: Dispatch the payment command using Redux
  3. Frontend: Create Stripe PaymentMethod using Redux Middleware
  4. Backend: Create customer on Stripe and our database
  5. Backend: Attach PaymentMethod using Stripe API
  6. Backend: Create Stripe Subscription using Stripe API
  7. Bank: Redirect to customer’s bank for payment authentication (optional)
  8. Frontend: Redirect to our web app for final confirmation, if the above payment authorisation happened
  9. Backend: Checking with Stripe on how the bank authorisation went

Test Cards

Before we begin, here is the link that you can find all the test cards to test different scenarios such as Success, Card Declines, or RequiresPaymentAuthorisation.

2. Dispatch Payment

We went through how to capture payment details securely before, so let’s start now by what happens when the user presses the submit button on our form. Below is the code.

To ream more on form handling, have a look at Form Handling and Input Validation, and if you might need a refresher on Redux, read my Redux in Plain English.

Here are the points to consider about this snippet:

  • Form Check: I start by checking all the validations are passing before I proceed.
  • Stripe is loaded: then I check if Stripe library has been fully loaded.
  • Billing Details: followed by constructing billing details in the format that Stripe wants it.
  • Access Card Details: Card details are passed to the payment function using an instance of Stripe object.
  • Tokens: As my API endpoints are protected using JWTs, I use withAuth as a higher order components to access users’ tokens. Obviously this means I am not allowing users with no profile to make any payments, a policy which you can change if you want to. To read on how I access user information with Higher Order Components, refer to Access User Details using HOCs.
  • Dispatch: Finally when I have all the information, I dispatch the call.

Note

Configuration, plumbing and troubleshooting your software foundation take a considerable amount of time in your product development. Consider using Pellerex which is a complete foundation for your enterprise software products, providing source-included Identity and Payment functions across UI (React), API (.NET), Pipeline (Azure DevOps) and Infrastructure (Kubernetes).

2.1 Reducers

Here is how I am creating and handling my actions and reducers:

I will come back and explain what these reducers do, but I will skip it for now, as I’d like to follow the flow of information, so everything makes sense.

To learn more the way I manage my API calls, and the apiCallBegan action, refer to API Call in React.

3. Creating PaymentMethod

Some context first. PaymentMethod is the terminology that just means how the payment is done, is it by card, money transfer, etc. Here I have simply used card.

Also as part of the one time payment operation, I have a series of tasks that need to be chained together, meaning I use the response from one call to prepare the request for another.

Don’t Change the State from within the Reducer

One way to achieve this is through initiating another call from within the reducer, however this pattern is discouraged, as each reducer needs to be a pure function, meaning using the same input and current state of store, the output (target state of the store) needs to be always the same. Introducing a third variable (i.e. another API call) won’t guarantee this notion, as the API might return a different value each time it is called. Hence I use a middleware called recurringPaymentOrchestrator, to orchestrate all these tasks.

And here is the code for the middleware:

And here is what’s happening here:

  • Execute the Right Middleware: First thing first, make sure this middleware is intended to be run for the right action.
  • OnStart: If there is anything that needs to be run before the actual operation, run it here.
  • createPaymentMethod: This is where the first task happens. I am sending the credit card details directly to Stripe as a PaymentMethod, and receive a PaymentMethod Id for it, which I will use later to process and confirm the payment in the call to my own backend API.
  • 3DSecure Authentication: Once the result comes back from my own API, I will know if there is a need to perform authorisation on the payment, by redirecting the user to their bank, where the bank sends a token to user’s mobile device, and they verify their identity. Once successful, they will be redirected back to my website.

If you would like to understand what’s behind getApi, and how I make a call to my backend APIs, refer to How to Call Backend APIs in React using Axios.

4. Process Subscription

Now that I have the PaymentMethod Id, as a reference to user’s credit card details on Stripe side, I can initiate processing and confirmation of the actual payment, by sending a request to my backend API. Below are the list of tasks which I will cover in detail.

4.1 Create customer on Stripe and our database

If you look at the list of items I am sending to my backend API, they are all straightforward, however let me explain the priceId.

As far as the Subscriptions are concerned, we need to define the products and prices on the Stripe dashboard before you process them. After defined, I simply send a priceId to Stripe, and ask Stripe to charge my customer for the amount of that price. Hence I have defined a set of tables to keep price information in my database which also contains Stripe priceIds for each product.

When I load prices from my database on the pages (like here), I let the user choose the subscription packages they want to buy, I record the priceId in Redux store, and pass it on to backend to create the subscription. That’s the purpose of priceId.

Now let’s have a look at how I create the customer, and I am basically doing a couple of tasks here:

  • Save/Retrieve Customer: If customer doesn’t exist in my DB, create it, otherwise fetch it
  • Create Stripe Customer: If customer didn’t exist, then create it on Stripe side as well. This is done through Stripe’s customer service APIs, which I have used from their Stripe.net package.

If you’d like to read more on how I manage my database through unit of work, refer to Unit of Work with EF, Code First, and Repository Patterns.

4.2 Attach PaymentMethod using Stripe API

Next we will need to attach the PaymentMethod Id we acquired from Step 3 (Frontend), and attach it to the Customer we created from Step 4.1.

4.3 Create Stripe Subscription

Here is where I create the subscription on the Stripe side.

Please note that I provide 20 days of trial here. Based on the number of trial days (0 or anything above 0), there are a few items which would be different, and I will explain them shortly.

4.3.1 Subscription With Trial

First let me walk through some terminology.

SetupIntent vs PaymentIntent

PaymentIntent as its name implies, means intention for payment. If confirmed, it transfers the money from customer’s account to your account.

SetupIntent on the other hand, means the intention to collect payment details (set it up), and collect the payment in a future date (such as monthly), in an ‘Off Session’ way. Off Session means without the customer being actively present on your website or app to do the the payment. On Session, means the opposite.

Now, back to the topic, Subscription with Trial means the customer will not be charged right away until the end of their trial. Considering this, here are the potential cases:

  • Payment Authentication is Needed: For any reason, be it required by the bank or rules defined by you, payment authentication might be needed. It means at some point, you will need to redirect your customer to their bank, to authenticate the payment by sending a code to their device. They will then need to be redirected back to your app by the bank, to complete the authentication process. If this was the case, the status of the subscription I just created will be Incomplete, meaning it needs authentication to be become complete. It also means, there will be a SetupIntent created for you, allowing you to capture the payment details without charging the customer immediately. Once the authentication is done, the subscription will go to Trialing state.
  • Payment Authentication is NOT Needed, and the status of the subscription becomes Trialing. This is when you can make your product or service available to customer until the end of their trial period at least. Again there will be a SetupIntent created for the customer.

When SubscriptionStatus is Active, there is no need to capture SetupIntent Id, you are done, and you can provision your offering to the customer.

However where SubscriptionStatus is Incomplete, meaning an action is required (i.e. Payment Authentication), we need to capture SetupIntentId/PaymentIntentId (depending on if the plan has trial or not) to confirm the status of authentication with Stripe, after customer performs the authentication.

Perform Authentication OnSession

I recommend to perform the authentication OnSession (i.e. when the customer is present on your website, or app). Otherwise if you leave the subscription in Incomplete state, you need to bring the customer back to your website (OnSession) at a later time using an email or notification and ask them to complete the authentication. In my view, if you Authenticate the customer at the first place OnSession, it is a superior experience comparing to taking them through the payment process again at a later date.

Here is the code for the above logic, and the notes to consider:

  • ReturnUrl: means the URL the bank will send the user to after Authentication is complete.
  • stripePaymentIntentService.Confirm: It is used to confirm the payment, and to understand if Payment Authentication (3DSecure) is needed.
  • PaymentIntentId: Whenever the Authentication is required, we will receive PaymentIntentId back to check the status of the authentication after it is done.
  • Authentication Url: The URL to the bank’s authentication page. It is used to send the user to that page.

https://gist.github.com/MahdiKarimipour/b16cd7f2c890411c8a767a17ddc04cfb

4.3.2 Subscription Without Trial

When there is no Trial, it means we immediately charge the client the subscription fee. Again there could be two scenarios:

  • Authentication is Needed: In which the subscription goes to Incomplete state, and we will repeat the above process. In this case we will receive a PaymentIntentId, as there is no trial and the payment happens immediately. As opposed to with trial, that we receive a SetupIntentId and we just set the card up without charging it.
  • Authentication is NOT Needed: In which the subscription goes to Active state immediately, and you can provision the offering to your client.

Note

Configuration, plumbing and troubleshooting your software foundation take a considerable amount of time in your product development. Consider using Pellerex which is a complete foundation for your enterprise software products, providing source-included Identity and Payment functions across UI (React), API (.NET), Pipeline (Azure DevOps) and Infrastructure (Kubernetes).

4.4 Redirect to customer’s bank for payment authorisation (optional)

Now back to Step 3, when I created the PaymentMethod, and called my backend API to do all these processing, I am now at the point that I receive the response from Step 4.4, and I can now continue. Here is the code again:

We are at line 59 now, and if the Payment Authorisation is required, I will redirect the user to their bank using my route manager, which is another middleware I use to manage redirects in my Redux chain of command.

4.5 Redirect to our web app for final confirmation (optional)

Stripe will provide you with a test page to test your payment authorisation and different flows. Once the user authorised/rejected the payment, they will be redirected to the ReturnUrl we specified above.

4.6 Checking with Stripe on how the bank authorisation went

And last but not least, I would recommend to check the final outcome of the payment when the user is back on your site, using the PaymentIntentId / SetupIntentId we received from our backend. Based on that response, you can navigate the user through your website. It is pretty straightforward, you can use Stripe’s SetupIntentService or PaymentIntentService for that purpose, by passing the PaymentIntentId, or SetupIntentId.

Pellerex: Payment Foundation for Your Next Enterprise Software

How are you building your current software today? Build everything from scratch or use a foundation to save on development time, budget and resources? For an enterprise software MVP, which might take 8–12 months with a small team, you might indeed spend 6 months on your foundation. Things like Identity, Payment, Infrastructure, DevOps, etc. they all take time, while contributing not much to your actual product. These features are needed, but they are not your differentiators.

Pellerex does just that. It provides a foundation that save you a lot development time and effort at a fraction of the cost. It gives you source-included Identity, Payment, Infrastructure, and DevOps to build Web, Api and Mobile apps all-integrated and ready-to-go on day 1.

Check out Pellerex and talk to our team today to start building your next enterprise software fast.

--

--

No responses yet