Part of my bailiwick within our codebase has become our companies use of Stripe’s and Xero’s respective API’s.

The Big Setup

Unfortunately, none of this code had any testing. The business logic held within the functionality is complex, making it hard for newcomers (me!) or those who haven’t looked at the code in a while (also me!) to debug and follow what’s going on.

For example, the web hook we receive from Stripe for invoice generation runs a gauntlet of business logic and at first was one long function in a Symfony actions file. This quickly became untenable as I started adding even more business logic to handle some “optional add-ons” users can pick on signup for our product. In one project I refactored the long function into different methods and in another I wrote a fairly lengthy and somewhat complicated (in itself! 😬) set of tests.

Some pain points I was running into:

  • Whenever we added new types of optional add-ons or main plan types, I’d have to retest all of our different contexts that get run.
  • There was and still is some refactoring that could be done, and it’s not something to start in on offhandedly, especially without tests.
  • We keep adding new business logic into how specific plans can be handled and testing needs to be done after all those code changes.
  • I am the only one working on and responsible for this, and my sanity is at stake!

That last one is a little tongue in check, but there is no motivator like misery.

deleteStripeSubscriptions method

Stripe to EasyRx to Xero Testing

To alleviate my misery, I wrote a set of tests to ensure our monthly and annual web hooks that start with Stripe and end at Xero are running as expected.

With these web hooks we:

  • Calculate overage charges for different features in our app while respecting trial periods and different levels of plan base counts.
  • Create new contacts in Xero.
  • Create new invoices and payments in Xero.

Testing this process is reliant on having values we store in our database and in Stripe’s and Xero’s respective testing servers.
The tests are based on a set of configurations that lay out:

  • The type of invoice web hook we are receiving from Stripe. These could be main plan types or optional add on types.
  • Whether the EasyRx user is a practice or a laboratory and what their ID is in our database.
  • The type of main plan and optional add-ons the user has.
  • Custom overage cost options that can be set per account.
  • The final message that should be received for the test to be successful.
  • The invoice type needed when calling back to Stripe at a certain point in our process.
  • The Stripe customer ID and the Xero customer ID.

Testing Setup

With that information the tests:

  • Add subscriptions and plans to Stripe.
  • Add plans and relevant pieces of data within our app for that plan.
  • Add rows to the EasyRx database for that laboratory or practice to test overage for different features that are used and charged for per use in our app.
  • Adds Xero Product and Services codes/names and Accounts that match our production Xero values to the Xero Demo server. This is done so we can avoid Xero errors. Xero resets the demo server every few weeks, which is a fun little hoop to jump through.

Testing Run

Each test will take a main plan, some variation of an optional add-on, a possible custom overage price, and run an invoice through our API endpoint that Stripe sends to.

The invoice may be a main plan or an optional add-on. Either can be annual or monthly, but we only want to run our calculations on a monthly one.
To further complicate things, if we have an annual optional add-on, we would like to run that calculation on a monthly main plan. But if that main plan is also not monthly, then we need to calculate it on a plan we add to Stripe called ‘Annual Overage’. Annual Overage is a plan run monthly.
The changes to database and Stripe reverse after the tests run.
There are some more nuances to it, but that is a good example of what is tested.

It is a slightly cumbersome test. I just added a new context to it today (05-12-2022) but at the same time I made a change to our codebase and was able to easily ensure it works with what we already have in place.
Before, I would have been testing manually for hours and still feel slightly unsure (depending on the relative complexity of change, of course). Now I can run a set of tests in 4 minutes with some nominal upkeep and be at ease.