PinStripe - A Stripe "Small Development Kit" for Elixir

PinStripe is a “small development kit” for Elixir and Stripe based off of @wojtekmach’s article SDKs with Req: Stripe on the Dashbit blog.

That blog post is great for more senior programmers and people who have time to roll their own integration. But what about those who just want to say “boom, Stripe”?

It would be great if Stripe provided an official Elixir SDK, but they don’t. The community project Stripity Stripe attempts to fill that gap. It has worked great for many people for a long time, but since the Stripe API is so sprawling it’s been cumbersome for maintainers and new code hasn’t been merged in over a year. This can give newcomers pause.

PinStripe aspires to find a middle ground by providing a maintainable, minimalistic interface with a few conveniences that should cover 95% of use cases without needing to meticulously cover the entire Stripe SDK.

Features include:

  • Simple API Client built on Req with automatic ID prefix recognition
  • Webhook Handler DSL using Spark for clean, declarative webhook handling
  • Automatic Signature Verification for webhook security
  • Code Generators powered by Igniter for zero-config setup
  • Sync with Stripe to keep your local handlers in sync with your Stripe dashboard

Please note this library is still experimental! Tests are passing, igniter installation and mix tasks all seem to work! But community feedback and contributions are very welcome.

Kudos to @lawik for help with the name!

32 Likes

ThinStripe, PinStripe, RacingStripe, TigerCantChangeIts. So many naming options :stuck_out_tongue:

2 Likes

EarnedMyStripes

2 Likes

Oh I really love PinStripe!

Gonna wait to see if more suggestions roll in before I pull the trigger on that but that’s great. Fun, fashionable, rolls of the tongue, and yet also suggests professionalism.

2 Likes

And also organized crime :smiley:

2 Likes

It is pretty gangster :smiling_face_with_sunglasses:

1 Like

TinyElixirStripe has officially been renamed to PinStripe!

Kudos to @lawik for the Don Draper energy!

@AstonJ - is it possible to update the title of this post?

6 Likes

Done :wink:

2 Likes

Thank you! Is it also possible to edit the tags? I’d like a pin-stripe tag I can subscribe to :folded_hands:

1 Like

PinStripe v0.2.2: Testing Utilities

PinStripe now includes handy testing utilities. This release adds two new modules.

PinStripe.Test.Mock provides high-level mocking helpers that wrap Req.Test:

test "reads a customer using stub_read" do
  Mock.stub_read("cus_123", %{"id" => "cus_123", "email" => "test@example.com"})

  {:ok, response} = Client.read("cus_123")
  assert response.body["email"] == "test@example.com"
end

PinStripe.Test.Fixtures leans on Stripe CLI to provide fixture generation:

test "creates a customer" do
  # Load/generate a customer fixture (use atoms for API resources)
  customer = PinStripe.Test.Fixtures.load(:customer)

  # Use with Req.Test
  Req.Test.stub(PinStripe, fn conn ->
    Req.Test.json(conn, customer)
  end)

  {:ok, response} = Client.create(:customers, %{email: "test@example.com"})
  assert response.body["object"] == "customer"
end

Fixture generation requires a Stripe test API Key (live keys are not allowed). Each unique generated fixture will create a record in your Stripe dev dashboard and a cached version in test/fixtures/stripe.

If/when you change your Stripe API version, you can run mix pin_stripe.sync_api_version to fetch the current version and clear all locally cached fixtures. Fixtures will then be regenerated the next time you run your tests.

1 Like

I don’t use Stripe, but it looks like your library’s approach to webhook handling could be extracted as, or inspire, a more general solution.

Curious how Spark is involved. I’ll read through the webhook handling code in the Dashbit blog and then see how you evolved it here.

Spark is what generates this very simple DSL for the WebhookHandler module:

defmodule MyApp.StripeWebhookHandlers do
  use PinStripe.WebhookHandler

  handle "customer.created", MyApp.StripeWebhookHandlers.CustomerCreated

  handle "customer.updated", fn data -> 
    // do stuff
  end 
end

That part is actually not in the Dashbit blog. They just do:

defmodule TeamsWeb.StripeWebhookController do
  use TeamsWeb, :controller
  plug :verify_signature

  def create(conn, %{"type" => type}) do
    # ...
  end

  defp verify_signature(conn, []) do
    # ...
  end
end

… the idea being that a dev reading it knows “ahh, I put some handling code in the create function.”

Whereas PinStripe’s WebHook controller is literally just this:

defmodule MyAppWeb.StripeWebhookController do
  use PinStripe.WebhookController,
    handler: MyApp.StripeWebhookHandlers
end

That injects all the sig verification code from the Dashbit controller and forwards webhook events to the WebhookHandler module.

So PinStripe is more opinionated, but it saves some fussy setup.

It’s actually possible though that the handle "stripe.event" ... DSL could just live right there in the controller though :thinking:

Then it would just be:

defmodule MyAppWeb.StripeWebhookController do
  use PinStripe.WebhookController

  handle "customer.created", fn(data) -> ... end
  handle "customer.updated", MyApp.StripeWebhookHandlers.CustomerUpdated
end

Might do that! Not sure there’s a need to have the handler declarations live in a separate module. It would save a bit of indirection.

2 Likes

[0.3.0] - 2025-12-19

Changed

Breaking Change: Simplified webhook handler architecture

Webhook event handlers are now defined directly in your WebhookController instead of a separate WebhookHandler module. This reduces indirection and simplifies the overall architecture.

Before (0.2.x):

defmodule MyApp.StripeWebhookHandlers do
  use PinStripe.WebhookHandler
  
  handle "customer.created", fn event -> ... end
end

defmodule MyAppWeb.StripeWebhookController do
  use PinStripe.WebhookController,
    handler: MyApp.StripeWebhookHandlers
end

After (0.3.0):

defmodule MyAppWeb.StripeWebhookController do
  use PinStripe.WebhookController
  
  handle "customer.created", fn event -> ... end
end

Migration
Move your handle declarations from your WebhookHandler module into your WebhookController. Handler modules (for complex handlers) should remain in lib/my_app/stripe_webhook_handlers/ but are now referenced directly from the controller.

… but let’s be honest, no one is using this yet :joy:

2 Likes

Probably 6-7 years back I worked on a client project that had stripity stripe and they were rawdogging everything and had very little in terms of tests. I needed to modify billing so I wanted tests. And just being able to test behavior manually against Stripe.

I ended up writing some test helpers for the Stripe CLI to receive webhooks ( Use the Stripe CLI | Stripe Documentation ). Was a pretty neat thing.

Might fit as an optional config option for test and dev?

3 Likes

Oh yeah! So actually the Dashbit article has an example of that too where it automatically starts the Stripe listener in dev mode and points it at the webhook endpoint. It’s on my short list to add that to the installer.

Also fwiw PinStripe does currently generate fixtures for webhhooks. Fixture gen uses the Stripe CLI, which means fixtures create a record in the Stripe dev dashboard when they’re first generated. After that though they are cached locally or, should I say, fixed.

3 Likes

Thanks, it’s great to have a new library for Stripe - we’re currently using StripityStripe, but it hasn’t had a release in 2 years or so…

1 Like

I’d love it if people would test it out! I haven’t used it in a real project yet so I still consider it experimental.

1 Like

very cool, love the name and will test it out on a project I am launching soon

1 Like

That would be great! I also haven’t tried it in a real project yet, but will be doing so soon.

Also welcome to the forum! It says this is the first time you posted :raising_hands:

I am a fan of the name too, to be clear that was @lawik :joy:

Since I am implementing Stripe checkout in a Phoenix LiveView app, I’m curious how PinStripe’s approach differs from an approach like this - Setup Stripe with Phoenix LiveView · FullstackPhoenix. I’m planning on using Stripe Elements for the checkout UI and am also using Ash Framework.

1 Like