Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Stripe / Payments Extension #103

Open
haikyuu opened this issue Dec 17, 2024 · 3 comments
Open

Stripe / Payments Extension #103

haikyuu opened this issue Dec 17, 2024 · 3 comments

Comments

@haikyuu
Copy link

haikyuu commented Dec 17, 2024

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
Loading
@scotttrinh
Copy link
Contributor

@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.

@haikyuu
Copy link
Author

haikyuu commented Dec 18, 2024

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.

@scotttrinh
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants