# How to Implement Credit-Based Generation with Super Admin Forced Charging in a GPT Image Application

> Implement credit-based generation with Super Admin forced charging in your GPT image app. Discover how this system ensures credit consumption on every request for robust control.

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

---

**The website enforces credit-based image generation through a multi-layered architecture combining API-level conditional routing, Supabase RPC calls, and SQL stored procedures that deduct credits atomically, with Super-Admins bypassing free-generation quotas via a `p_force_credit` flag that guarantees credit consumption on every request.**

The `freestylefly/awesome-gpt-image-2` repository demonstrates a production-ready billing system where image generation is gated by credit validation. This guide breaks down how the codebase coordinates authentication, reservation logic, and SQL-level business rules to ensure accurate credit accounting—with special handling for Super-Admin users who must always pay despite any free-tier entitlements.

## Authentication and Profile Loading for Credit Checks

Every generation request begins with identity verification and profile hydration. In [`api/_lib/supabase.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/supabase.js), the `getAuthContext` function validates the bearer token and constructs a profile containing three critical billing fields:

- `creditBalance` — current purchasable credits
- `freeGenerationsUsed` — consumption of promotional quota
- `isSuperAdmin` — boolean derived from `row.role === 'super_admin'`

```javascript
// api/_lib/supabase.js lines 49-55
const profile = {
  email: row.email,
  isSuperAdmin: row.role === 'super_admin',
  creditBalance: row.credit_balance,
  freeGenerationsUsed: row.free_generations_used,
  // ...
};

```

This profile drives all downstream charging decisions. The `isSuperAdmin` flag is not merely presentational—it directly controls which reservation path the API invokes.

## API-Level Reservation Routing

The core enforcement logic resides in [`api/generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js). The handler inspects `auth.profile.isSuperAdmin` to select between two reservation functions:

```javascript
// api/generate-image.js
reservation = auth.profile.isSuperAdmin
  ? await reserveCreditGeneration(auth.client, auth.user.id, caseId, prompt)
  : await reserveGeneration(auth.client, auth.user.id, caseId, prompt);

```

**Regular users** trigger `reserveGeneration`, which calls the Supabase RPC `reserve_generation_usage` with `p_force_credit: false` (line 60). **Super-Admins** invoke `reserveCreditGeneration` with `p_force_credit: true` (line 84). This single boolean parameter cascades into dramatically different SQL behavior.

### SQL Reservation Logic with Force-Credit Override

The stored procedure `reserve_generation_usage` lives in [`supabase/migrations/20260509090000_membership_billing.sql`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/supabase/migrations/20260509090000_membership_billing.sql). Its execution flow branches on both user status and the `p_force_credit` parameter:

| Condition | Action | Credit Consumed |
|-----------|--------|---------------|
| `free_generations_used < 1` AND `p_force_credit = false` | Create free reservation, increment `free_generations_used` | 0 |
| `credit_balance >= 1` | Atomic credit deduction, create reservation, log transaction | 1 |
| Neither condition met | Raise `CREDITS_REQUIRED` exception | N/A |

The **Super-Admin forced charging mechanism** works through `p_force_credit = true`. When this flag is set, the procedure **skips the free-generation branch entirely** and proceeds directly to credit deduction. Even if a Super-Admin has unused free generations, they are charged one credit per image.

Lines 70-73 handle the failure case for insufficient credits:

```sql
-- supabase/migrations/20260509090000_membership_billing.sql
RAISE EXCEPTION 'CREDITS_REQUIRED' USING errcode = 'P0001';

```

This exception propagates to the API layer, which translates it into an HTTP 402 `CREDITS_REQUIRED` response (lines 10-12 of [`generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/generate-image.js)). Other RPC errors become generic `GENERATION_FAILED` messages after logging (lines 14-17).

## Post-Generation Credit Accounting

After successful image generation, the system finalizes the reservation:

```javascript
// From the analysis: completeReservation marks reservation completed (lines 5-9)
await completeReservation(reservation.id, generationData);

```

The updated profile—including the decremented `creditBalance`—returns to the client via `profileFromReservation` (lines 44-55), ensuring UI synchronization without requiring a separate profile fetch.

## Frontend Integration and Error Handling

The React frontend in [`src/main.jsx`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/src/main.jsx) (lines 822-894) interprets these responses to control the generation interface:

- Displays credit balance and free-generation status
- Disables generate button on `CREDITS_REQUIRED` (402)
- Shows Super-Admin indicators when `profile.isSuperAdmin` is true

This creates a cohesive user experience where billing constraints are enforced server-side but communicated clearly client-side.

## Complete Implementation Examples

### Regular User: Free-Then-Paid Flow

```javascript
// api/generate-image.js
reservation = await reserveGeneration(client, userId, caseId, prompt);
// Internally invokes:
client.rpc('reserve_generation_usage', {
  p_user_id: userId,
  p_case_id: caseId,
  p_prompt: prompt,
  p_force_credit: false  // allows free generation if available
});

```

### Super-Admin: Forced Credit Deduction

```javascript
// api/generate-image.js
reservation = await reserveCreditGeneration(client, userId, caseId, prompt);
// Internally invokes:
client.rpc('reserve_generation_usage', {
  p_user_id: userId,
  p_case_id: caseId,
  p_prompt: prompt,
  p_force_credit: true   // skips free tier, always charges
});

```

### SQL Credit Deduction and Transaction Logging

```sql
-- Simplified from reserve_generation_usage
IF v_profile.credit_balance >= 1 THEN
  UPDATE public.profiles
     SET credit_balance = credit_balance - 1
   WHERE id = p_user_id
   RETURNING * INTO v_profile;

  INSERT INTO public.generation_reservations 
    (user_id, case_id, prompt, force_credit, credit_amount, usage_source)
  VALUES 
    (p_user_id, p_case_id, p_prompt, p_force_credit, 1, 'credit');

  INSERT INTO public.credit_transactions 
    (user_id, amount, type, source, reference_id, metadata)
  VALUES 
    (p_user_id, -1, 'generation', 'case_generation_test', v_reservation_id,
     jsonb_build_object('caseId', p_case_id, 'usageSource', 'credit'));
END IF;

```

## Architecture Summary

| Layer | Component | Billing Responsibility |
|-------|-----------|------------------------|
| **Frontend** | [`src/main.jsx`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/src/main.jsx) | Display credit state, handle 402 errors |
| **API Gateway** | [`api/generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js) | Route to `reserveGeneration` or `reserveCreditGeneration` based on `isSuperAdmin` |
| **Auth Utility** | [`api/_lib/supabase.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/_lib/supabase.js) | Hydrate profile with `creditBalance`, `freeGenerationsUsed`, `isSuperAdmin` |
| **Database RPC** | `reserve_generation_usage` | Atomic credit check, deduction, reservation creation, transaction logging |
| **Migration** | [`20260509090000_membership_billing.sql`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/20260509090000_membership_billing.sql) | Schema and procedure definitions, `CREDITS_REQUIRED` exception |

## Summary

- **Authentication layer** embeds billing state (`creditBalance`, `isSuperAdmin`) into every request context via `getAuthContext`
- **API handler** branches on `profile.isSuperAdmin` to set `p_force_credit` true or false
- **SQL procedure** atomically implements: free-tier check, credit deduction, transaction logging, and insufficient-credit errors
- **Super-Admin forced charging** bypasses free-generation logic entirely through the `p_force_credit` parameter
- **Error propagation** converts SQL `CREDITS_REQUIRED` into HTTP 402 for client handling
- **Profile synchronization** returns updated balances post-generation without extra round-trips

## Frequently Asked Questions

### What happens when a user has both free generations and credits available?

The SQL procedure checks `p_force_credit` first. For regular users (`p_force_credit: false`), it consumes the free generation before touching paid credits. For Super-Admins (`p_force_credit: true`), the free-generation branch is skipped and one credit is deducted immediately, regardless of free-generation status.

### How does the system prevent race conditions in credit deduction?

The `reserve_generation_usage` stored procedure executes within a single database transaction. The `UPDATE ... WHERE credit_balance >= 1` pattern ensures atomic deduction—concurrent requests for the last credit will serialize, with all but one failing the balance check and raising `CREDITS_REQUIRED`.

### Can the forced-credit mechanism be extended to other user roles?

Yes. The `p_force_credit` parameter is role-agnostic. To force charging for additional roles, modify the conditional in [`api/generate-image.js`](https://github.com/freestylefly/awesome-gpt-image-2/blob/main/api/generate-image.js) to check `profile.role` against an array (e.g., `['super_admin', 'premium_tier']`) rather than only `isSuperAdmin`. No SQL changes are required.

### Why return HTTP 402 instead of 403 or 400 for insufficient credits?

HTTP 402 Payment Required is the semantically correct status for credit exhaustion. It distinguishes billing failures from authentication (401/403) or validation errors (400), allowing clients to trigger upsell flows precisely when users need to purchase credits.