# How Stripe Is Integrated for Payments in awesome-gpt-image-2: Complete Implementation Guide

> Learn how Stripe integrates for payments in awesome-gpt-image-2. Explore the three-layer architecture, server-side billing library, and dedicated endpoints for subscriptions and credit packs.

- Repository: [苍何/awesome-gpt-image-2](https://github.com/freestylefly/awesome-gpt-image-2)
- Tags: how-to-guide
- Published: 2026-09-10

---

**The awesome-gpt-image-2 repository implements Stripe payments through a three-layer architecture using environment-based configuration, a server-side billing library in [`api/_lib/billing.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/billing.js), and dedicated checkout/webhook endpoints that handle both subscription memberships and one-off credit packs.**

This open-source image generation platform uses **Stripe** as its primary payment provider, backed by **Supabase** for order persistence and user management. The integration supports recurring billing for membership plans and immediate payments for credit packs, all synchronized through Stripe Checkout Sessions and webhook events. Understanding this Stripe integration for payments reveals a production-ready pattern for handling dual-mode billing in modern web applications.

## Architecture Overview

The payment system splits responsibilities across three logical layers:

1. **Configuration Layer** – Environment variables store API keys and webhook secrets
2. **Billing Library** – [`api/_lib/billing.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/billing.js) encapsulates Stripe SDK initialization, customer management, and Supabase order operations
3. **HTTP Endpoints** – [`api/billing/checkout.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/billing/checkout.js) creates sessions while [`api/billing/webhook.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/billing/webhook.js) processes asynchronous events

This separation ensures that payment logic remains testable and that raw webhook payloads can be verified before touching database records.

## Configuration and Environment Setup

Stripe credentials are never hardcoded. The repository expects two critical environment variables defined in `.env.example`:

- `STRIPE_SECRET_KEY` – Server-side API key for creating checkout sessions
- `STRIPE_WEBHOOK_SECRET` – Endpoint secret for verifying webhook signatures

The `isStripeConfigured()` function in [`api/_lib/billing.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/billing.js) validates that `STRIPE_SECRET_KEY` exists before any payment operations proceed, preventing runtime errors in misconfigured deployments.

## Server-Side Billing Library

The core abstraction lives in [`api/_lib/billing.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/billing.js), which wraps the Stripe SDK and Supabase client.

### Stripe Client Initialization

The `getStripeClient()` function lazily instantiates the Stripe SDK with a pinned API version:

```javascript
// api/_lib/billing.js
import Stripe from 'stripe';

export function getStripeClient() {
  if (!stripeClient) {
    stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY, {
      apiVersion: '2026-02-25.clover', // Pinned version for stability
    });
  }
  return stripeClient;
}

```

Pinning the API version prevents unexpected breaking changes when Stripe updates its SDK.

### Customer Management

The `getOrCreateStripeCustomer()` helper ensures every user has a corresponding Stripe customer object. It checks the `profiles` table for an existing `stripe_customer_id`; if absent, it creates a new customer via `stripe.customers.create()` and persists the ID back to Supabase. This links all future transactions to a consistent customer record.

### Order Persistence

Before generating a checkout URL, the system creates a record in the `payment_orders` table using `createPaymentOrder()`. This record tracks the `user_id`, product details, amount, currency, and initial status. After Stripe confirms the session creation, `markOrderCheckoutCreated()` updates the order with the Stripe session ID, creating an audit trail that withstands webhook delays or failures.

## Checkout Flow Implementation

The [`api/billing/checkout.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/billing/checkout.js) endpoint orchestrates the purchase initiation.

### Session Creation Process

When a POST request arrives with `{ productType, productId }`, the handler:

1. Validates Stripe configuration via `isStripeConfigured()`
2. Retrieves product details (membership plan or credit pack) from `getBillingProduct()`
3. Ensures a Stripe customer exists via `getOrCreateStripeCustomer()`
4. Creates a pending order in Supabase
5. Builds a dynamic checkout payload using `checkoutLineItem()` for price formatting

The session `mode` switches based on product type: `subscription` for memberships or `payment` for one-off credit packs.

### Metadata Linking Strategy

Critical metadata attaches to every Stripe object:

```javascript
// api/billing/checkout.js
const metadata = {
  orderId: order.id,
  userId: auth.user.id,
  productType,
  productId
};

if (productType === 'membership') {
  sessionPayload.subscription_data = { metadata };
} else {
  sessionPayload.payment_intent_data = { metadata };
}

```

This metadata enables the webhook handler to reconcile Stripe events with Supabase records without relying on fragile string parsing.

## Webhook Handling for Payment Confirmation

The [`api/billing/webhook.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/billing/webhook.js) endpoint processes Stripe’s asynchronous events. It configures `bodyParser: false` to preserve the raw request body required for signature verification.

### Signature Verification

```javascript
// api/billing/webhook.js
const rawBody = await readRawBody(req);
const signature = req.headers['stripe-signature'];

const event = stripe.webhooks.constructEvent(
  rawBody,
  signature,
  process.env.STRIPE_WEBHOOK_SECRET
);

```

Without this verification, attackers could forge payment success notifications.

### Event Processing Logic

The handler dispatches three event types:

- **`checkout.session.completed`** – Finalizes credit-pack orders via `completeCreditPackOrder()` or activates memberships through `upsertMembershipFromSubscription()` and `grantMembershipCredits()`
- **`invoice.payment_succeeded`** – Grants recurring monthly credits when subscription invoices pay
- **`customer.subscription.updated` / `deleted`** – Syncs membership status changes back to Supabase

Each handler uses the metadata fields embedded during checkout to locate the original `payment_orders` record and update the user's credit balance or membership tier accordingly.

## Complete Payment Flow Example

A typical purchase follows this sequence:

```javascript
// Front-end initiates purchase
const { data } = await axios.post('/api/billing/checkout', {
  productType: 'credit_pack',
  productId: 'pack_100_credits'
});

// Redirect to Stripe hosted checkout
window.location.href = data.url;

```

Once the user completes payment, Stripe POSTs to `/api/billing/webhook`:

```javascript
// Server processes webhook
case 'checkout.session.completed': {
  const session = event.data.object;
  // Retrieve metadata to find order
  const { orderId, userId, productType } = session.metadata;
  
  if (productType === 'credit_pack') {
    await completeCreditPackOrder(supabase, orderId);
  } else {
    const subscription = await stripe.subscriptions.retrieve(
      session.subscription
    );
    await upsertMembershipFromSubscription(supabase, userId, subscription);
    await grantMembershipCredits(supabase, userId);
  }
  break;
}

```

The user's account reflects the purchase immediately after webhook processing, with the `payment_orders` table marking the transaction as completed.

## Summary

- **Stripe integration for payments** in awesome-gpt-image-2 relies on environment variables (`STRIPE_SECRET_KEY`, `STRIPE_WEBHOOK_SECRET`) configured in `.env.example`
- The [`api/_lib/billing.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/billing.js) library centralizes SDK initialization, customer management, and Supabase order operations
- Checkout sessions are created in [`api/billing/checkout.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/billing/checkout.js) with embedded metadata linking Stripe objects to Supabase records
- Webhook verification in [`api/billing/webhook.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/billing/webhook.js) uses raw body parsing and signature validation to securely process `checkout.session.completed` and subscription events
- The architecture distinguishes between `subscription` mode for memberships and `payment` mode for credit packs, handling both recurring and one-time billing patterns

## Frequently Asked Questions

### How does awesome-gpt-image-2 handle Stripe customer creation?

The system uses `getOrCreateStripeCustomer()` in [`api/_lib/billing.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/billing.js) to check if the user's profile record contains a `stripe_customer_id`. If missing, it calls `stripe.customers.create()` with the user's email and UUID, then stores the returned customer ID in the Supabase `profiles` table, ensuring all subsequent charges associate with the correct Stripe entity.

### What webhook events does the integration process?

The endpoint in [`api/billing/webhook.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/billing/webhook.js) explicitly handles three event types: `checkout.session.completed` for initial purchase confirmation, `invoice.payment_succeeded` for recurring subscription payments, and `customer.subscription.updated` or `deleted` for membership status changes. Each event updates the Supabase database to reflect the current billing state.

### How are one-off credit packs differentiated from subscriptions in the code?

The differentiation occurs in [`api/billing/checkout.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/billing/checkout.js) where the `mode` parameter is set dynamically: `mode: productType === 'membership' ? 'subscription' : 'payment'`. Memberships use Stripe's subscription engine with recurring billing, while credit packs use one-time payment intents. The webhook handlers then branch based on this `productType` metadata to either grant one-time credits or set up recurring membership benefits.

### Where are Stripe API credentials configured?

Credentials are configured via environment variables listed in `.env.example`: `STRIPE_SECRET_KEY` for server-side API calls and `STRIPE_WEBHOOK_SECRET` for verifying incoming webhooks. The `isStripeConfigured()` function validates their presence at runtime, and `getStripeClient()` initializes the SDK with a pinned API version (`2026-02-25.clover`) to ensure consistent behavior across deployments.