Stripe / Payments Extension
This is brought over from Discord, where we briefly discussed a Stripe/payments extension that's already in the roadmap. Let's discuss this here briefly and we can then make a draft RFC when we have more clarity on the scope and approach.
Approaches
One approach is to not store Stripe data in the database but map queries to API calls. This is more inline with GraphQL approach.
- It avoids dealing with syncing etc.
- Limited in what's possible to do. e.g: analytical queries, aggregation ...
The other approach is to actually store Stripe data in the database. This is the approach I think makes sense for EdgeDB.
Creating the schema
Since the surface we’re talking about here is quite big, we could provide the most common types off the shelf and enable/teach users to create Stripe Object Types themselves. The API docs are the way to go to “manually introspect” the Stripe schema.
Handling Events
Now that we have a database schema in place. We need to choose which events to handle.
By default that will be all webhooks related to Stripe types the user chose when creating the schema. Our handling of events mainly creates data in the db. But sometimes, users want to handle some webhooks themselves (e.g: one-time payment checkout completed event). We should allow that as well.
A simple, yet powerful way to handle both scenarios would be to:
- Receive the webhook first in EdgeDB side
- Create database objects from that webhook automatically
- Send an http request (webhook) to the web server. It contains both the new data created/updated/deleted (when available) along with the original event.
Detailed docs for events can be found here
Calling Stripe API
With the HTTP extension, it’s possible to call Stripe API from the database. But why create a level of indirection when the user can call the Stripe API client directly? There is an official OpenAPI spec if that’s of interest.
The flow
Generated by Claude
sequenceDiagram
participant C as Client App
participant S as Server
participant E as EdgeDB
participant SA as Stripe API
participant SW as Stripe Webhooks
%% Initial Setup
Note over C,SW: Setup Phase
S->>E: Configure Stripe types to sync
S->>E: Create schema for selected types
S->>SW: Register webhook URL
%% Runtime Operations
Note over C,SW: Runtime Operations
%% Direct API Call Flow
C->>S: Request payment operation
S->>SA: Call Stripe API (create payment, etc)
SA-->>S: Return API response
S-->>C: Return response to client
%% Webhook Flow
Note over SA,E: Async webhook processing
SA->>SW: Generate event
SW->>E: Send webhook event
activate E
E->>E: Update database state
E->>S: Forward webhook + DB changes
deactivate E
S->>S: Custom webhook handling
%% Query Flow
C->>S: Query payment data
S->>E: EdgeQL query
E-->>S: Return local data
S-->>C: Return response
%% Error Handling
Note over C,SW: Error Scenarios
SA->>SW: Failed payment event
SW->>E: Send webhook event
E->>E: Update payment status
E->>S: Forward failure event
S->>C: Notify client
@haikyuu
I strongly agree with most of your assessment here: We should store and synchronize the data, and pass through webhooks via our webhooks mechanism.
There is also probably some minimal subset of the Stripe schema that is worth synchronizing, while keeping the rest of it in Stripe. In your own design, did you decide to model all of the Stripe Core+Payments domain? And furthermore, I wonder if it's worth considering a less vendor-specific design before committing to the full Stripe layout. There is probably a kernel of similarity between most popular SaaS-targeted payment processors like Lago, Gumroad, Chargebee, etc.
did you decide to model all of the Stripe Core+Payments domain?
No, here are the entities we model.
- StripeCustomer
- StripeSubscriptionPauseCollection
- StripeSubscriptionItem
- StripeSubscription
- StripeInvoiceLine
- StripeInvoice
- StripeCharge
- StripePrice
- StripeProduct
- StripePaymentIntent
- StripePromoCode
- StripeDiscount
- StripeCoupon
And furthermore, I wonder if it's worth considering a less vendor-specific design before committing to the full Stripe layout.
I understand the motivation behind this. But I think Stripe is the de-facto solution for most, and is a safe bet.
However, there is hyperswitch which is an open source open payment switch. It seems interesting but doesn't seem to model quite basic concepts like Coupons!
Also, even Stripe is a moving target. For example, in Subscriptions single discount was deprecated in favor of multiple discounts. This makes the task of having a less vendor-specific design (of the data model) quite hard.
Also, even Stripe is a moving target.
I think this is honestly my concern: if we try to model Stripe's schema 1-for-1, we'll end up having to have versioned schemas ourselves. If I squint, that feels almost like we should abstract around Stripe's schema design even for Stripe, so maybe it's worth putting some effort into designing a more minimal subset of the most valuable parts of payment (and subscription-related) processing, to limit our exposure to being so tightly coupled to external non-standardized services like this. Not saying it'll be trivial (or even possible!) but probably worth considering at this early stage. To be clear, I don't expect for application developers to jump between payment processors, but it's possible that different developers might chose different processors even if Stripe is by far the biggest competitor in this space.