Thursday, October 25, 2018

Stripe Programming - The Complexity Factor

For anyone who is contemplating building their own E-Commerce solution, particularly those of you who are doing RPG related projects with any sort of Subscription and/or Platform (where your users can sell their things through your site) model ... this is for you. I'm going to go over the maddening realities of Stripe programming.

First, let's be clear. Stripe is probably (almost definitely) the best, simplest, and most coherent of all the options out there currently. It is the easiest one to program and work with that I've found so far. Paypal? Fuggetaboutit. First Data? OMG, no please no. In fact, I did an exhaustive analysis of the features of all of them and Stripe turns up as the best of the lot. Perhaps most principally in that Stripe along with all the other important e-commerce features required allows you to plug in Avalara which is a Tax Calculation and Processing system so you can easily handle all of the State sales taxes for your product and/or service.

So, that said, Stripe sucks. It's enormously complicated stuff. Not so much in how to code it, which depending on your language of choice (it supports many languages such as
Ruby, Python, PHP, Java, Node, Go, and (to a lesser degree than the others for some reason) .Net)) is more or less straight forward. In the case of .Net, my language of choice, we get code like this:

Dim CustomerService = New StripeCustomerService()
  Dim Customer As New StripeCustomer
  Dim CustomerID As String = ""
  Dim StripeRequestOptions As New StripeRequestOptions
  Dim CustomerOptions As New StripeCustomerCreateOptions()


    CustomerOptions.Description = "Customer for " & StripeCustomerEmail
    CustomerOptions.Email = StripeCustomerEmail
    CustomerOptions.Metadata = New Dictionary(Of String, String)() From 
       {"UserID", UserID}
    StripeRequestOptions.IdempotencyKey = IdempotencyKey.ToString


    Customer = CustomerService.[Create](CustomerOptions, StripeRequestOptions)
    StripeCustomerKey = Customer.Id

    Catch ex As StripeException
    End Try

So, not too horrible. Of course, in the case of .Net your first task, which isn't as easy as you'd like, is to find coherent complete code examples. The Stripe website support system offers the barest minimal examples for you, and often they are inadquate for anything more complicated than the equivalent of Dim strSayHi as String = "Hello World".

Ok, so where does it get complicated to the point that you're sitting there at 2:30am pulling the last of your hairs out of your head? Ahhhhh... ok. Let's take a look.

You're a good developer. You set up a good solid development environment. A local machine on which to do your initial development, a test machine (or site) on which to post your code and test it out, and have your friends and testers try to find bugs in it, and then the Production (aka Live) server on which you are hosting your Real website. Fine. That's normal.

Now lets look at Stripe. They too have a Production and Test system. So you can create test accounts and they will show up in the test system, and not pollute your production system with test junk. Fair enough. But they don't have anything that is equivalent to your Local environment. So ... when you create new accounts while you're doing your initial Stripe programming, what you want to do is send Stripe a message that says "Hi, please create a new Stripe Customer in your database for me, and then send me the Stripe Customer Key back." Then you can keep a record of that key in your own database so that when you do transactions with your customer, like allowing them to upgrade from one Subscription Tier to the next, you have the customer key so you can ensure the correct customer gets their account updated. Makes sense so far, right?

But what happens when you create a customer on your local machine, as you are naturally bound to do while you're doing your initial development, or else how can you program the thing? So you create a new Stripe Customer. Presumably in their test system. But you record it in your local system database. Hold that thought.

So everything works and you deploy to test. Goodie goodie. You have your friends test and create a customer. This new customer also goes into the Stripe Test database, but on your side it doesn't go into your local database (of course) but goes into your Test system database instead. Did you consider what will happen when you go to look for these two customers from either your local or your test application. Each one will only find one of those customers. Both of them are in the Stripe Test Customer database, though.

What does this mean? Well, it means that your code has to account for whether or not your code is being run in Local or Test mode (or Production for that matter). That turns out to be a whole bunch of code, by the way. Let's say that you didn't realize this, and there's nothing on the Stripe support site to tell you that you should be thinking about this BEFORE you get started coding, until you've gotten midway in your programming effort. You now realize that something weird is happening when you look for accounts on your local system, and they don't exist. Making matters worse is if you happen to back up and restore your production database back to local to debug what you think is going on. Now you have only your production stripe accounts locally, and they do not match your stripe test accounts at all. Wow. What a mistake! You can, of course, roll back to your local database provided you thought of this in advance and thought to back it up before overwriting it with the production version. But why would you think of that? Well, experience with Stripe will teach you to make those extra moves, believe me.

Ok, so at some point you decide, it's probably a good idea to check the returned customer key to ensure that it is a valid key. Right? That's normal. So you do some checks. Does the key start with "cus_"? Is it 18 characters long? Ok, good. It's valid so you continue with your code. Except wait. Sometimes it is not 18 characters. So you figure it must be an invalid key. One day of debugging later, you come to realize that valid Stripe customer keys can be any length! Wow!! Who the hell does that?? Stripe, of course. So you go to the IRC channel and ask the engineers who very politely explain, yes, we can change the key length at any time without notice. Ohhh... wowowowow. Ok. Well then I can check the "cus_" at least. Nope. That too, they calmly explain, can also be changed at any time without notice. Sooooo... wait, you say, ... exactly how am I supposed to validate that I got back a valid stripe customer key? "If you are a good programmer you will figure that out. We can't help you with that kind of thing - that's on your end." they calmly tell you. In other words - you can not validate that you have a valid stripe key because they do not ensure consistency of format. Fascinating. So you rip out your validation code because it not only doesn't work, but produces phantom errors. Fine. Whatever. You keep going.

The more you build the more complicated the issues get... mostly because Stripe very nicely offloads all of the validation work to you. You wind up having to build a great number of additional structures in your system to compensate for Stripe not doing so on their side. Here's another example.

As you are tooling along minding your own business building your site and having fun learning all about Stripe's idiosyncrasies, you discover another fascinating fact. Stripe does nothing at all to prevent the creation of duplicate keys with the same email address for your application in their system. Zip. So as you are building and testing your login and Stripe account creation system you later find that you have dozens of accounts with different Stripe Customer Keys and the same email address. Ask yourself - how will you know on your side which customer id is the one that conducted a given transaction if you have a dozen stripe customer keys for the same user? Hello?

The answer to this is, according to the calm and soothing Stripe engineers, is to build your system "the right way, not the wrong way". Ohhhh... duh. Of course! But what does this mean? It means you need to account for possible duplicate Stripe account creations on your side. They do nothing to either ignore a new Stripe request from your application if there is already an existing account and send the original Stripe Customer Key back to you (which is what they should do), or even alert you that such a key already exists (a lesser solution they could also easily implement that still leaves you somewhat in the lurch on your side). Instead they just blithely create yet another Customer Key and send that to you. Yay. That is so wrong.

So anyway, now you have worked it out. You realize these things and you build a bunch of additional structures necessary to track what system on Stripe you are addressing ("Production", "Test", or "Local"). You have in your web.config file keys for both systems and a flag saying which keys to use depending on which system. You have in your database REF_Stripe_Customer table a "System" field that says which system the account is for, and you check that (because for a while there you were pulling out clumps of hair trying to figure out why your customers seem to be valid on one system but flagging errors in the others). And you have at least a dozen extra functions to manage dealing with these, among other, Stripe related conundrums you've slammed face first into along the way. Here's a little diagram to suggest what you're dealing with.

So no, I do not have a complete solution to offer you at this point, I'm sorry. I feel like I'm at the tail end of the issues, but I'm not 100% sure. At any point new gotcha's can surface. I will say, however, that this should at least get you over the hump in terms of the kinds of things you need to think about when doing Stripe programming.

Stripe programming is not hard ... once you know what to do. In fact, once you get it, it actually is pretty easy. But watch that first step! It's a doozy! And be ready to do lots of Stripe Account cleanup coding along the way. You will likely need it!

No comments: