Initial and Delayed Charges Using Stripe in C#



Last updated: January 28th, 2024

Background

I recently built a site that has an intiial (trial) charge of $9.95 and then, if the product is not returned 30 days after shipment, a final $59.95 charge. This is an article about how I set up the initial and delayed charges in C# using Stripe, a payment service provider.

Getting Setup with Stripe

There are many companies out there that can help you process credit cards. The reason I decided to use Stripe was the simplicity of a single account that had an SDK in C# (although not officially supported). You could use a gateway processor and merchant service provider with a merchant account, it could save you money at the cost of time and complexity. Stripe charges 30¢ per successful charge and then 2.9% on top of that, see Stripe's pricing for more details.
To get started with Stripe, you first make an account; fortunately, you can use your account and their test API right away without business details. They have many test card numbers to emulate responses. To go live, you submit business information such as the bank account that funds will be transferred to (they can transfer funds every 2 days), the statement descriptor (what the buyer sees on their credit card statement), an address for the business and enable fraud filters (CVC and zip code verification aren't required but are a best practice). From there you can use a live mode API key to start accepting payment. They have an excellent dashboard to display account activity.

Payment Workflow

Stripe has an easy client side option, Stripe Checkout, which pops a credit card form (good for designers with simple requirements), server side SDKs (for more complicated scenarios) as well as other options for both deeper client and server side integration. Stripe's API does have the concept of subscriptions which make rebilling easy but in the case of a small initial and larger delayed charge, it doesn't make a lot of sense to use that. Although there are some companies that specialize in more complicated workflows using Stripe (like BillForward), I decided to do it myself to save money.
The basic workflow I implemented is this:
  1. Authorize the initial + delayed amount of $69.90
  2. If success, add their card details to Stripe as a customer and store the customer ID, setting the order to "not shipped" and "authorized" in my system
  3. Every day, run a batch process which gets all orders that are "shipped" in "authorized" status in my system or have been "shipped" with a "trial" status that has expired (was shipped over 30 days ago)
    • For "shipped," "authorized" orders: check to see that there is no record of the $9.95 captured from the $69.90 auth in Stripe and then capture it, if needed, from the authorization charge ID (note: there are only 7 days to capture all or part of an auth), then mark the order as in "trial" status
    • For "shipped," expired "trial" orders (not "returned"): check that the $59.95 amount has not been charged in Stripe, if it hasn't been charged, charge the remaining $59.95 due and set the order status as "payment complete"
I decided to use the auth, capture model simply because I wanted to know that the customer could at one time pay the full amount. Although it's still possible the 2nd charge could fail, the likelihood is lower. The orders are processed daily using a cron job, in this case Quartz.NET.

Every day, the Quartz job looks for shipped orders that were authorized (capturing the $9.95) and orders in trial status that were shipped over 30 days ago (charging $59.95). In Stripe, once you store a customer's payment details, you can charge the customer by using the customer's ID. This enables workflows with multiple, potentially smaller, payments. Again, you cannot know if any payment after the authorization expires will succeed.
The problem with this workflow is that some customers will look at their statement and see their bank listing the authorization of $69.90 as a charge. In this case they may contact customer service and complain. It's important to assure the buyer that they must wait 7 days until the remaining auth is reversed, as Stripe says regarding auth/ capture.

Some Code Examples

I wrote the application in C# using ASP.NET for the front end and an Azure Worker Role for the batch tasks; as such, I used the Stripe.net NuGet package, the source code for which is on GitHub. Below are some snippets of code I used to accomplish the workflow.
Initializing Stripe.net in the application:

Authorizing full amount:

Creating a Stripe customer (a Stripe customer ID is used to charge them later):

Check that the amount (trial or delayed) is due:

Try to capture part of the authorized amount:

Charge the final amount to the happy customer:

Summary

This article showed the problem and a solution for an initial and delayed amount for a single product processed with Stripe in C# using the Stripe.net NuGet package. Regarding the product, there have been very few returns. In the case of a return, any remaining authorized amount expires. I also added the ability to do refunds on captured charges in the backend portal.
The working implementation of this code was done for SleepTight Mouthpiece, a snoring mouthpiece which uses a trial amount for 30-days and then captures the remaining amount if the product is not returned.

Comments

No Comments

Post Comment

Prove you are human 9 + 12 =



Join my email list!



ryan
About Me

With 15 years in tech, I've excelled as a senior software engineer, specializing in ASP.NET, C#, SQL, Azure, and front-end technologies. I've led diverse projects across various sectors, from startups to global corporations, particularly during my decade in the San Francisco Bay Area.


Sign Up With SoftSys Hosting! (My host)