PaperTiger - a stateful mock Stripe server for Elixir testing

We just published PaperTiger, a mock Stripe server we built to solve real pain points in our test suite.

The Problem

Testing Stripe integrations in Elixir (and in any language, really!) has some rough edges:

  • Stripe’s official stripe-mock is stateless - you can’t create a customer and then fetch it, which makes testing realistic flows impossible

  • Stripe’s webhook sandbox is limited - 5 endpoints max, dashboard-only management, no programmatic control

  • Shared test mode causes collisions - CI runs interfere with manual testing, leading to flaky tests

  • No time control - testing subscription renewals means waiting or hacking around Stripe’s billing cycle

We needed something that actually behaves like Stripe - stateful resources, proper webhook chains, and time control for billing cycles.

What PaperTiger Does

  • Stateful API - Create a customer, attach a payment method, subscribe them, and it all persists (in ETS)

  • Automatic webhook delivery - Resource changes emit properly-signed webhooks with retry logic

  • Time control - Accelerate time for subscription testing, or use manual mode for precise control

  • BillingEngine - Simulates subscription lifecycle: period rollovers, invoice creation, payment processing

  • Chaos mode - Inject payment failures with configurable decline codes for testing error paths

  • Contract testing - Run the same tests against PaperTiger and real Stripe to verify behavior matches

Quick Example

setup do
  {:ok, config: PaperTiger.stripity_stripe_config()}
end

test "subscription billing cycle", %{config: config} do
  {:ok, customer} = Stripe.Customer.create(%{email: "test@example.com"}, config)
  {:ok, sub} = Stripe.Subscription.create(%{customer: customer.id, ...}, config)
  
  assert sub.status == "active"
  
  # Advance time past the billing period
  PaperTiger.Clock.advance_days(32)
  
  # Subscription renewed, invoice created and paid
  {:ok, invoices} = Stripe.Invoice.list(%{customer: customer.id}, config)
  assert length(invoices.data) == 2
end

Links

We’d welcome feedback, issues, or contributions. If you’ve dealt with similar Stripe testing headaches, hopefully this helps.

16 Likes

PaperTiger is now v1.0.0 and is much more usable than the early versions. For us internally, this has been a major win: we replaced a large amount of custom Stripe mocking code with PaperTiger-based tests, and it’s also been very useful in development mode. If you used it early on and had any difficulties you may want to try again as it should be significantly improved. Details in the changelog.

3 Likes