How to Use Stripe for Paid Memberships in GPT-Image2: Complete Integration Guide

GPT-Image2 implements a three-layer Stripe architecture—client initialization, checkout session creation, and webhook event processing—to handle recurring membership payments and automatically grant monthly credits upon successful transactions.

The freestylefly/awesome-gpt-image-2 repository provides a production-ready integration for selling paid memberships through Stripe. The system handles subscription lifecycle management, from initial checkout to recurring invoice payments, while maintaining payment order state in Supabase and synchronizing user credit allowances.

Configuring the Stripe Client and Helpers

Before processing payments, the application initializes the Stripe SDK through utility functions in api/_lib/billing.js. The isStripeConfigured() function validates that STRIPE_SECRET_KEY exists in environment variables, while getStripeClient() creates a singleton Stripe instance pinned to a specific API version.

// api/_lib/billing.js
export function isStripeConfigured() {
  return Boolean(process.env.STRIPE_SECRET_KEY);
}

export function getStripeClient() {
  if (!isStripeConfigured()) return null;
  if (!stripeClient) {
    stripeClient = new Stripe(process.env.STRIPE_SECRET_KEY, {
      apiVersion: STRIPE_API_VERSION
    });
  }
  return stripeClient;
}

The checkoutLineItem() helper transforms internal product objects into Stripe-compatible line items. For membership products, it injects recurring interval metadata; for credit packs, it treats them as one-time payments.

// api/_lib/billing.js (lines 66-86)
export function checkoutLineItem(product) {
  const priceData = {
    currency: product.currency,
    unit_amount: product.amountCents,
    product_data: {
      name: product.name.en,
      description: product.description.en,
      metadata: { productType: product.type, productId: product.id }
    }
  };
  if (product.type === 'membership') {
    priceData.recurring = { interval: product.interval };
  }
  return { quantity: 1, price_data: priceData };
}

Creating Stripe Checkout Sessions for Memberships

The api/billing/checkout.js endpoint orchestrates the purchase flow. It validates the request method, resolves the selected product, creates or retrieves a Stripe Customer for the authenticated user, persists a preliminary payment order in Supabase, and constructs the checkout session payload.

Key implementation steps in the checkout handler:

  1. Validate prerequisites – Ensure POST method and that both Supabase and Stripe are configured
  2. Resolve product – Fetch membership or credit pack details via getBillingProduct()
  3. Customer management – Create or retrieve Stripe Customer ID using getOrCreateStripeCustomer()
  4. Order persistence – Store pending order in Supabase via createPaymentOrder()
  5. Session construction – Build payload with mode, line items, and return URLs
// api/billing/checkout.js (lines 60-104)
const sessionPayload = {
  mode: productType === 'membership' ? 'subscription' : 'payment',
  customer: customerId,
  client_reference_id: auth.user.id,
  line_items: [checkoutLineItem(product)],
  success_url: `${appUrl}/?billing=success&session_id={CHECKOUT_SESSION_ID}`,
  cancel_url: `${appUrl}/?billing=cancelled`,
  allow_promotion_codes: true,
  metadata
};

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

const session = await stripe.checkout.sessions.create(sessionPayload);

The endpoint returns session.url to redirect users to Stripe's hosted checkout page.

Processing Webhook Events for Membership Fulfillment

Stripe posts event notifications to api/billing/webhook.js, which verifies signatures using STRIPE_WEBHOOK_SECRET before routing events to specific handlers. The endpoint handles three critical event types for paid memberships:

  • checkout.session.completed – Initial subscription activation
  • invoice.payment_succeeded – Recurring credit grants
  • customer.subscription.updated/deleted – Status synchronization

Handling Initial Checkout Completion

When checkout.session.completed fires, the handleCheckoutCompleted function retrieves the subscription object from Stripe, upserts membership records into the user_memberships table, grants initial credits via grantMembershipCredits(), and marks the payment order as completed.

// api/billing/webhook.js (lines 21-73)
if (productType === 'membership') {
  const subscription = await stripe.subscriptions.retrieve(subscriptionId);
  await upsertMembershipFromSubscription(client, subscription, { /* user context */ });
  await grantMembershipCredits(client, { /* credit details */ });
  await client.from('payment_orders')
    .update({ status: 'completed', stripeSubscriptionId })
    .eq('id', order.id);
}

Processing Recurring Invoice Payments

For monthly credit allowances, the invoice.payment_succeeded event triggers handleInvoicePaid (lines 76-88), which calls grantMembershipCredits() with metadata linking the credit grant to the specific Stripe invoice and subscription.

Managing Customer Subscriptions and Billing

Beyond initial purchase, the integration supports ongoing subscription management through dedicated endpoints:

  • api/billing/portal.js – Creates Stripe Customer Portal sessions allowing users to update payment methods or cancel subscriptions
  • api/billing/plans.js – Exposes the active catalog of membership tiers and credit packs from Supabase

Both endpoints import the shared billing library (api/_lib/billing.js) to ensure consistent Stripe client configuration and error handling.

Summary

  • Initialization: The Stripe client initializes lazily in api/_lib/billing.js using environment variables STRIPE_SECRET_KEY and STRIPE_API_VERSION
  • Checkout Flow: api/billing/checkout.js creates subscription-mode sessions for memberships, storing pending orders in Supabase before redirecting to Stripe
  • Webhook Security: api/billing/webhook.js verifies Stripe signatures, then grants credits and updates membership status upon checkout.session.completed and invoice.payment_succeeded events
  • Recurring Credits: Memberships automatically grant monthly credits through the invoice.payment_succeeded handler, ensuring users receive allowances without manual intervention
  • Self-Service: The billing portal endpoint allows customers to manage subscriptions independently through Stripe's hosted interface

Frequently Asked Questions

How does GPT-Image2 handle Stripe webhook security?

The webhook handler in api/billing/webhook.js constructs the event using stripe.webhooks.constructEvent() with the STRIPE_WEBHOOK_SECRET environment variable. This cryptographically verifies that payloads originate from Stripe, rejecting unauthorized requests with a 400 status before any business logic executes.

What happens when a membership invoice payment succeeds?

The system listens for invoice.payment_succeeded events and triggers grantMembershipCredits() via the handler at lines 76-88 of api/billing/webhook.js. This credits the user's account with their monthly allowance and records the transaction source as stripe_membership_invoice for audit trails.

Can users manage their subscriptions after purchase?

Yes. The api/billing/portal.js endpoint creates Stripe Customer Portal sessions. Existing subscribers can update payment methods, view invoices, or cancel subscriptions through Stripe's hosted interface, with changes synchronized back to Supabase via subsequent webhook events.

What environment variables are required to enable Stripe payments?

The integration requires STRIPE_SECRET_KEY for API authentication and STRIPE_WEBHOOK_SECRET for webhook verification. Optionally, APP_URL configures the return URLs for checkout completion and cancellation redirects. These variables enable the isStripeConfigured() checks in api/_lib/billing.js.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →