ash icon indicating copy to clipboard operation
ash copied to clipboard

Extension Proposal: Ash Billing

Open chazwatkins opened this issue 9 months ago • 2 comments

What is the purpose of the extension?

High level purpose of the extension.

Provide an easy way for applications to use any billing provider.

Today, we have to leverage an existing SDK in the community, which may or may not be actively maintained to a billing provider's latest API version. Or there isn't one that exists yet and you must handle the integration yourself.

I think Elixir needs a plugin-style billing solution for similar to authentication solutions like AshAuthentication and Ueberauth. The Ash eco-system seems well poised to help provide such a solution.

Providing such a solution in the Ash ecosystem could be a positive drive for people to choose Ash as their application solution. However, the solution doesn't necessarily have to require an application to use Ash to leverage AshBilling.

Goals

  • Choose which billing provider to use

  • Choose which billing provider features to use. i.e. subscriptions, usage meters, etc

  • Option to choose different billing providers for specific billing features. i.e. Use Stripe for subscriptions, but use Org for usage billing. This is to keep compilation time down. Some providers have extensive APIs with endpoints the user may not care about. Stripe, for example, has a ton of functionality that most SaaS apps won't use. There's no need to incur extra compilation time for generated modules that will never be used.

  • Choose whether to store billing provider data in your data layer and sync with billing provider or do all CRUD via the billing provider's API. i.e. The application may want to handle cases where the billing provider is unavailable

  • Billing provider integrations are provided by introspecting the provider's Open API spec to ensure the latest provider's API features are always supported as resources. Like the inverse of ash_json_api for generating an Ash app's spec.

  • Initial Billing Providers

    • Stripe
    • Polar - Likely easiest for a POC since the API is relatively small and focused on SaaS

How would it extend the DSL?

Add an example of the DSL configuration this extension might add.

I don't know how this DSL would look at the moment. I can see this starting off as an extension to generate a DSL from open API specs that will be used to generate billing providers integrations for AshBilling. Then AshBilling provides the universal DSL to interact with any billing provider.

I'd like to see what others in the community think about the idea and what a unified billing DSL could look like.

chazwatkins avatar Apr 16 '25 14:04 chazwatkins

I found a project to generate an Elixir SDK from an Open API spec. https://github.com/aj-foster/open-api-generator

It currently uses its spec parsing, which could be updated to leverage OpenApiSpex. I tried it out on the Stripe Api spec yaml, and it generated all schema modules with typespecs and an operations module with functions for all endpoints.

Thought this might serve as inspiration for supporting any billing provider that has an Open API spec.

chazwatkins avatar Apr 16 '25 21:04 chazwatkins

Sorry I haven't been able to properly review this. I'm all for the concept 😄. This may need to start its life independently of me 😓

A few thoughts, though:

Provide a universal DSL for interacting with any billing provider like Ash Resources.

This is really tough. To the point that I think it may be a bad idea. I'm not sure 😄 Essentially the problem is that the internal design of any given provider affects how it is actually used and represented with the things that interact with it. Financial & billing providers often use fundamentally different mechanisms for things like idempotency. It is typically in their best interest to be offering products and services that can't just be retrofitted onto a competitor via some API changes. Not necessarily out of a desire to lock in, but to avoid commoditization of their service offerings. It's been a while since I worked payment providers for basic things like monthly subscriptions etc. but I'm leary of the idea that we can have one abstraction sit on top of multiple providers.

Perhaps we can for certain subsections of the domain. Like "monthly subscriptions" is probably modeled relatively similarly across providers. But for the entire domain of "billing", I'm not so sure.

If we were instead to approach this as building "your end" of any given service, i.e "AshStripe", we'd have a much less nebulous (although still moving) target. Still sounds wildly difficult to maintain, given how fast these APIs move. Maybe instead we can provide a basic domain model with "stubs"? Like "Here is where you charge them money" and "here is where you create a subscription for them"?


For compilation times, I think we can actually potentially leverage a "codegen" step, where they just generate what they need vendored into their app. Then there is no fancy compile time shenanigans, and if the API changes they just codegen again and look at the differences? We still need library level abstractions (probably), but at least for changing API touchpoints this would make the changing contracts clear and the repercussions of upgrading clear as well. Basically like Mishka Chelekom's component installation pattern but for API endpoint adapters.

zachdaniel avatar Apr 23 '25 20:04 zachdaniel