Testing subscriptions with Stripe Test Clocks and Workbench

/Article

Subscriptions involve recurring payments that can have billing cycles that span weeks or months. When integrating a subscription feature, you need to be able to trigger scenarios on demand and observe any changes that happen in your system to validate the expected behavior. This becomes challenging for any use case that occurs over large periods of time. Stripe’s test clocks along with Workbench can reduce the time and effort required for verifying your system’s behavior for situations that are dependent on time .

Both test clocks and Workbench are features available in your account at no additional cost. With test clocks, you simulate the passage of time while your account is in test mode, so you don’t have to wait hours or even days to see how your system behaves. You can observe any state changes and react to any events that get triggered. With Workbench, you have a browser based tool to manage and observe the activities that occur in your Stripe integration.

This article shows how to use these tools together to ensure your payment integration is ready for production.

Creating the tiered offerings

Before setting up a test clock simulation, you should have the subscription offerings provisioned in your product catalog. The screenshot below shows three tiers for customers to choose from. These tiers are modeled as products that each include a unique name, tax category, and a recurring price with a monthly billing period.

Product catalog

Setting up the simulation

A test clock is attached to one or more customers and advanced to a target date. This causes any time dependent objects in the Stripe account to update as well, such as subscriptions and webhook events.

To create a test clock, you have the option of going the no-code route via the Stripe Dashboard, programmatically using the REST API, or using one of the supported language SDKs. The code samples here are written in C# and make use of the Stripe .NET SDK, but the steps are similar for other languages.

First, you need to use the TestClockService class from the SDK to create a new test clock instance. At creation time, a test clock must be given a name and have its FrozenTime property set. This property represents the starting point for the respective clock and is specified as a Unix Epoch timestamp. You can set it to time in the future or in the past, but remember the test clock can only move forward in time after it is created.

// Retrieve API key from appsettings.json var apiKey = _configuration.GetSection("Stripe")["SecretKey"]; var requestOptions = new RequestOptions{ ApiKey = apiKey }; // Create test clock var currentTime = DateTimeOffset.UtcNow.DateTime; var tcCreateOptions = new TestClockCreateOptions { Name = $"Subscription Clock", FrozenTime = currentTime }; var testClockService = new TestClockService(); var newTestClock = await testClockService.CreateAsync(tcCreateOptions, requestOptions);

Next, create the customer and subscription objects for the scenario you want to test. Existing customers cannot be used for test clock simulations, but you are able to add up to three new ones. Using the CustomerService class, create a new customer with the Name, Email and PaymentMethod properties assigned.

The following example uses pm_card_visa, which is one of the available test cards that always results in a successful test payment. Also, the TestClock property of the new customer must be set at creation time to the ID of the previously created clock.

// Create a new customer and attach the test clock var ccOptions = new CustomerCreateOptions { Name = "Fake Customer", Email = "customer@fake.com", Description = "Faker User Account", PaymentMethod = "pm_card_visa", TestClock = newTestClock.Id, InvoiceSettings = new() { DefaultPaymentMethod = "pm_card_visa"} }; var customerService = new CustomerService(); var newCustomer = await customerService.CreateAsync(ccOptions, requestOptions); var originalPMID = newCustomer.InvoiceSettings.DefaultPaymentMethodId;

The final object to create is the subscription for a test customer. Supply the SubscriptionService class with the price ID from one of the subscription tiers in the product catalog along with the customer ID.

// Create a new subscription var priceId = "<subscription-tier-price-id>"; var options = new SubscriptionCreateOptions { Customer = newCustomer.Id, Items = new List<SubscriptionItemOptions> { new SubscriptionItemOptions {Price = priceId} } }; var subscriptionService = new SubscriptionService(); var newSubscription = await subscriptionService.CreateAsync(options, requestOptions);

After running the code, you will see that the customer, subscription, and test clock objects all have the option of advancing time for the simulation.

Advance time highlight

You can use Workbench to inspect the activity within a Stripe account. It is built into the Stripe Dashboard, so there isn’t anything that needs to be installed. Once it is enabled, you can access the various logs, errors, and events occurring in an account.

Inside the Inspector tab of Stripe Workbench, you can view details about what has happened with the subscription so far. Looking at the Logs and Events tab for the subscription reveals a collection of activity, triggered as a result of the code that was executed. These events signal that an initial invoice was paid and the subscription was successfully started. You can even dive deeper into the events and inspect the properties for each of them.

Workbench Inspector

Running the simulation

To move the test clock simulation forward in time, make a call to the Advance method from the TestClockService and provide it with the future time you want it to progress to. To simulate a billing cycle for a monthly subscription, you must add one month to the initial frozen time the test clock was set to.

var tcAdvanceOptions = new TestClockAdvanceOptions{ FrozenTime = currentTime.AddMonths(1) }; await testClockService.AdvanceAsync(newTestClock.Id, tcAdvanceOptions, requestOptions);

Back in the subscriptions details page in the Stripe Dashboard, the invoice for the subscription is successfully paid and the account is in a new billing period. Opening up the Events tab in Stripe Workbench, you can see events for the account like invoice.created and customer.subscription.updated, and also events for the test clock simulation like test_helpers.test_clock.ready and test_helpers.test_clock.advancing.

Workbench Events

The following example runs a simulation for a failed subscription payment and shows which activities get generated via Workbench.

The current default payment method for the customer is set to a test card that only returns successful payments. An additional card must be attached to the customer and set as the new default. The pm_card_chargeCustomerFail test card is a good option for this.

First, use the Attach method from the PaymentMethodService class to add it as an additional payment method for the customer. Next, update the customer by setting this new payment method as the default.

var pmAttachOptions = new PaymentMethodAttachOptions { Customer = newCustomer.Id }; var pmService = new PaymentMethodService(); var newPaymentMethod = await pmService.AttachAsync("pm_card_chargeCustomerFail", pmAttachOptions, requestOptions); var cuOptions = new CustomerUpdateOptions { InvoiceSettings = new() { DefaultPaymentMethod = newPaymentMethod.Id } }; await customerService.UpdateAsync(newCustomer.Id, cuOptions, requestOptions);

Advance the test clock simulation to the next billing period.

var tcAdvanceOptions = new TestClockAdvanceOptions{ FrozenTime = currentTime.AddMonths(2) }; await testClockService.AdvanceAsync(newTestClock.Id, tcAdvanceOptions, requestOptions);

In the subscriptions details page, the fake customer’s subscription shows canceled and the last invoice is failed. Looking at the triggered events through Workbench displays a number of failed events. Specifically, inspecting the cancellation_details property of the customer.subscription.deleted event reveals the subscription was canceled because Stripe was unable to collect payment from the customer.

Using the information collected from these two simulation runs equips you with the insights that help you know what events and properties are important for your application. That also means you know what events you should test for and where to look whenever issues arise in your subscriptions.

Conclusion

Testing time-sensitive scenarios can be challenging, especially when they extend over long periods. This post shows that tools like Stripe Workbench and test clocks can help validate your development cycle when validating the behavior of your subscription integration.

To learn more about developing applications with Stripe, visit our YouTube Channel and take a look at the additional resources linked below.

/About the author

Cecil Phillip

Cecil Phillip is a part of the Developer Relations team at Stripe. He is originally from St. John’s, Antigua and has over a decade of experience in building software for various industries. He’s a huge fan of .NET and loves learning about patterns for building distributed systems. Feel free to reach out to him on social media, and check out some of his videos on the Stripe Developer channel.

/Related Articles
[ Fig. 1 ]
10x
Doing more with less: Reducing requests to the Stripe API
The performance of an application can directly affect the customer experience. Using the expand feature along with caching techniques, developers...
Workbench
[ Fig. 2 ]
10x
Observing immediate versus delayed payments with Stripe Workbench
Immediate and delayed payments need to be handled differently within an integration. It’s important to understand what events to listen for and...
Workbench
Payment Methods