How Billing and Credit Management Work in GPT-Image2: A Technical Deep Dive
TLDR: GPT-Image2 uses a hybrid billing system that combines subscription-based memberships with pay-as-you-go credit packs, delegating all payment processing to Stripe and Alipay while storing credit balances in Supabase and updating them via atomic PostgreSQL stored procedures.
GPT-Image2 implements a robust billing engine to handle both recurring subscriptions and one-time credit purchases. The architecture, found in the freestylefly/awesome-gpt-image-2 repository, separates product catalog management from payment processing, using Stripe for subscription lifecycle management and Alipay for regional credit pack sales, with all credit ledger operations enforced at the database level.
Product Catalog and Pricing Structure
The system maintains a dynamic product catalog fetched directly from Supabase. In api/_lib/billing.js, the getBillingCatalog() function retrieves active products from two distinct tables: membership_plans for recurring subscriptions and credit_packs for one-time purchases.
This function formats each row with presentation fields such as priceLabel, currency, and credits, enabling the frontend to render pricing tiers without hardcoding values. For memberships, the catalog also exposes monthly_credits, which determines how many generations a subscriber receives per billing cycle.
Checkout Flow and Session Management
When a user initiates a purchase, the request hits api/billing/checkout.js. This handler validates the incoming payload and normalizes the productType to either membership or credit_pack.
The checkout flow executes several sequential operations:
- Product Validation – Calls
getBillingProduct()to verify the selected item exists and is active. - Customer Management – Invokes
getOrCreateStripeCustomer()to ensure a Stripe customer record exists for the user. - Order Creation – Uses
createPaymentOrder()to record the intended purchase (credits or plan type) in the database before payment occurs. - Session Generation – Builds a Stripe Checkout session via
checkoutLineItem()and returns the redirect URL to the client.
// Initiating a credit pack purchase
await fetch('/api/billing/checkout', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
productType: 'credit_pack',
productId: 'pack_001'
})
});
// Returns: { url: 'https://checkout.stripe.com/...', orderId, user }
Payment Provider Integration: Stripe and Alipay
GPT-Image2 supports multiple payment backends. Stripe serves as the default provider, configured via the STRIPE_SECRET_KEY environment variable and instantiated in api/_lib/billing.js.
For markets requiring Alipay, the system uses specialized stored procedures defined in supabase/migrations/20260721090000_alipay_webpay.sql. These PostgreSQL functions handle order completion, refunds, and balance updates for credit pack purchases, ensuring Alipay transactions follow the same atomic guarantees as Stripe webhooks.
Webhook Processing and Credit Fulfillment
Payment confirmation occurs asynchronously via webhooks. The endpoint in api/billing/webhook.js listens for Stripe events including checkout.session.completed and invoice.paid.
When a payment succeeds, the handler:
- Marks the corresponding payment order as paid in the database.
- For memberships: Grants the user a monthly allotment defined by the
monthly_creditsfield in their subscription plan. - For credit packs: Increments the user's
credit_balancefield by the purchased amount.
// Inside api/billing/webhook.js
if (event.type === 'checkout.session.completed') {
await handleCheckoutCompleted(client, stripe, event.data.object);
}
Credit Balance Storage and Consumption
All credit accounting resides in Supabase. The migration file supabase/migrations/20260509090000_membership_billing.sql defines the credit_balance integer field on the profiles table and implements PostgreSQL functions for atomic updates.
When the image generation pipeline processes a request (via api/generation/callback.js), it checks the caller's credit_balance. For paid generations, the system calls a stored procedure—typically via client.rpc('deduct_credit', { user_id: userId, amount: 1 })—which atomically decrements the balance (set credit_balance = p.credit_balance - 1) only if sufficient credits exist.
Free-tier users bypass the credit balance entirely; the system tracks their usage via the free_generations_used counter instead.
// Atomic credit deduction during image generation
await client.rpc('deduct_credit', {
user_id: userId,
amount: 1
});
Admin Monitoring and Metrics
Administrators can monitor the billing ecosystem through dedicated endpoints. api/admin/users.js exposes individual credit balances, while api/admin/metrics.js aggregates system-wide statistics including total credit balance, generated credits, purchased credits, and membership grants. These metrics derive from the same Supabase tables and aggregation queries defined in the admin metrics migrations (supabase/migrations/20260513095141_admin_metrics_charts.sql).
Summary
- Hybrid Model: GPT-Image2 supports both recurring memberships (
membership_plans) and one-time credit packs (credit_packs). - Payment Orchestration: Stripe handles subscriptions and general payments; Alipay is supported for credit packs via dedicated stored procedures.
- Atomic Ledger: All credit adjustments occur through PostgreSQL stored procedures in
supabase/migrations/20260509090000_membership_billing.sql, ensuring consistent balance updates. - Webhook Fulfillment: The
api/billing/webhook.jsendpoint converts Stripe payment events into credit grants or subscription activations. - Consumption Logic: Generation endpoints check
credit_balancebefore processing, deducting credits atomically for paid requests while trackingfree_generations_usedfor free tiers.
Frequently Asked Questions
How does GPT-Image2 handle different payment methods?
The system primarily uses Stripe for credit card and subscription billing, configured in api/_lib/billing.js using the STRIPE_SECRET_KEY environment variable. For Alipay support, the repository includes specific migrations in supabase/migrations/20260721090000_alipay_webpay.sql that implement server-side logic for order completion and refunds, allowing Asian markets to purchase credit packs through local payment rails.
What happens when a user's credit balance runs out?
When api/generation/callback.js processes a request, it verifies the user's credit_balance via the Supabase client. If the balance is insufficient for a paid generation, the stored procedure deduct_credit (or equivalent) will fail the atomic check, and the API returns an error indicating insufficient credits. The user must then purchase a credit pack or upgrade to a membership plan via api/billing/checkout.js to continue.
How are subscription renewals processed?
Stripe manages the subscription lifecycle through recurring billing events. When Stripe sends an invoice.paid webhook to api/billing/webhook.js, the handler identifies the active membership and applies the monthly_credits allotment defined in the membership_plans table to the user's profile. This occurs automatically each billing cycle without requiring manual intervention.
Where is the credit balance stored and updated?
The credit_balance field resides in the profiles table within Supabase, defined by supabase/migrations/20260509090000_membership_billing.sql. Updates happen exclusively through PostgreSQL stored procedures (such as those for Alipay fulfillment or credit deduction), ensuring atomic operations. The API layer reads this value via the Supabase client in api/_lib/supabase.js but never performs direct SQL updates to prevent race conditions.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →