Skip to content

Conversation

@icecrasher321
Copy link
Collaborator

Summary

Can prepurchase credits with payment method on file.

Type of Change

  • New feature

Testing

Tested manually

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

@vercel
Copy link

vercel bot commented Dec 4, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
Project Deployment Preview Comments Updated (UTC)
docs Skipped Skipped Dec 7, 2025 3:05am

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Dec 4, 2025

Greptile Overview

Greptile Summary

Implements prepurchased credit system for Pro and Team plans. Users can purchase $10-$1000 in credits using their payment method on file, which are then applied to reduce overage charges before billing.

Key Changes:

  • Added creditBalance decimal fields to userStats and organization tables
  • Credit purchase flow: creates Stripe invoice, charges payment method, adds credits synchronously
  • Credits automatically applied during threshold billing and end-of-cycle overage billing
  • Usage limits adjusted to planBase + creditBalance (but only increases, never decreases)
  • Email notifications sent via webhook after successful payment
  • Added billingBlockedReason enum to distinguish payment failures from disputes

Issues Found:

  • Credits added before payment confirmation (line 221 in purchase.ts) - if payment fails asynchronously after stripe.invoices.pay() initiates but before confirmation, credits are already granted
  • Usage limit only increases when credits purchased, never decreases when credits spent, which could allow usage beyond intended limit if user previously set custom high limit

Confidence Score: 3/5

  • This PR has moderate risk due to payment timing issue that could grant credits before payment confirmation
  • Score of 3 reflects solid implementation with proper atomic operations for credit deduction and good idempotency handling, but critical payment timing issue where credits are added before payment is confirmed could allow users to receive credits even if payment fails asynchronously. The usage limit logic also has a subtle bug where limits only increase but never decrease when credits are spent.
  • Pay close attention to apps/sim/lib/billing/credits/purchase.ts - the payment confirmation timing issue on lines 215-221 needs to be addressed before merging to production

Important Files Changed

File Analysis

Filename Score Overview
apps/sim/lib/billing/credits/purchase.ts 3/5 Implements credit purchase flow using Stripe invoices; charges payment method on file and adds credits immediately after payment; synchronous operation with proper validation and metadata tracking
apps/sim/lib/billing/credits/balance.ts 4/5 Core credit balance operations with atomic deduction using CTE-based SQL; handles both user and organization credit balances with proper transaction safety
apps/sim/app/api/billing/credits/route.ts 5/5 API endpoint for credit operations; validates UUID requestId and amount constraints; properly authenticated with session checks
apps/sim/lib/billing/threshold-billing.ts 4/5 Applies credits to reduce threshold overage charges before creating invoices; uses transaction locks to read credit balance atomically and deduct within same transaction
apps/sim/lib/billing/webhooks/invoices.ts 4/5 Handles credit purchase webhooks for email notifications; applies credits at cycle end to reduce remaining overage before invoicing
packages/db/schema.ts 5/5 Adds creditBalance decimal fields to userStats and organization tables; adds billingBlockedReason enum for tracking payment failure vs dispute blocks

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as Credit Balance UI
    participant API as /api/billing/credits
    participant Purchase as purchaseCredits()
    participant Stripe
    participant DB as Database
    participant Webhook as Invoice Webhook

    User->>UI: Click "Add Credits"
    UI->>UI: Generate requestId (UUID)
    User->>UI: Enter amount ($10-$1000)
    UI->>API: POST with amount and requestId
    
    API->>API: Validate session and amount
    API->>Purchase: purchaseCredits(userId, amount, requestId)
    
    Purchase->>DB: canPurchaseCredits(userId)
    DB-->>Purchase: Pro/Team only = true
    
    Purchase->>DB: getHighestPrioritySubscription(userId)
    DB-->>Purchase: subscription data
    
    Purchase->>DB: isOrgAdmin(userId, orgId)
    DB-->>Purchase: admin check result
    
    Purchase->>Stripe: subscriptions.retrieve(subscriptionId)
    Stripe-->>Purchase: subscription with customer and payment method
    
    Purchase->>Stripe: invoices.create(metadata contains credit purchase type)
    Note over Purchase,Stripe: Uses idempotency key with requestId
    Stripe-->>Purchase: invoice created
    
    Purchase->>Stripe: invoiceItems.create(amount)
    Stripe-->>Purchase: line item added
    
    Purchase->>Stripe: invoices.finalizeInvoice(invoiceId)
    Stripe-->>Purchase: invoice finalized
    
    Purchase->>Stripe: invoices.pay(invoiceId)
    Note over Purchase,Stripe: Initiates payment (async)
    Stripe-->>Purchase: payment initiated
    
    Purchase->>DB: addCredits(entityType, entityId, amount)
    Note over Purchase,DB: ISSUE: Credits added before payment confirmed
    DB-->>Purchase: credits added
    
    Purchase->>DB: getCreditBalance(entityId)
    DB-->>Purchase: new balance
    
    Purchase->>DB: setUsageLimitForCredits()
    Note over Purchase,DB: Only updates if newLimit > currentLimit
    DB-->>Purchase: limit updated
    
    Purchase-->>API: success response
    API-->>UI: 200 OK
    UI-->>User: Credits added successfully message
    
    Note over Stripe,Webhook: Later (async)
    Stripe->>Webhook: invoice.payment_succeeded
    Webhook->>DB: getCreditBalance(entityId)
    DB-->>Webhook: current balance
    Webhook->>Webhook: Send confirmation emails
    Webhook-->>Stripe: webhook processed
Loading

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

27 files reviewed, 5 comments

Edit Code Review Agent Settings | Greptile

@waleedlatif1
Copy link
Collaborator

@greptile

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

28 files reviewed, 2 comments

Edit Code Review Agent Settings | Greptile

@icecrasher321 icecrasher321 merged commit 9f884c1 into staging Dec 7, 2025
9 checks passed
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

Successfully merging this pull request may close these issues.

4 participants