Skip to main content
Credits are the platform currency used to generate AI photos and unlock premium style collections.

Credit System

Credit Costs

ai_generation
number
default:"100"
Cost per AI photo transformation
premium_pack_unlock
number
default:"3000"
One-time cost to unlock premium style packs

Purchasing Credits

Available Credit Packs

Current credit packages for B2C users (prices in ARS):

Starter

500 credits $4,000 ARS 5 AI generations

Standard

1,100 credits $8,000 ARS 11 AI generations (+10% bonus)

Business

1,500 credits $10,000 ARS 15 AI generations (+20% bonus)

Payment Integration

MercadoPago integration for Latin America:
const handlePayment = async (pack) => {
  const { data } = await supabase.functions.invoke('mercadopago-payment', {
    body: {
      user_id: session.user.id,
      credits: pack.credits,
      price: pack.price,
      pack_name: pack.name,
      redirect_url: window.location.origin
    }
  });
  
  // Redirect to MercadoPago checkout
  window.location.href = data.init_point;
};

Payment Flow

1

Select Pack

Choose credit amount from pricing page
2

MercadoPago Redirect

Secure checkout on MercadoPago platform
3

Payment Processing

Credit card, debit, or cash payment options
4

Webhook Confirmation

MercadoPago notifies platform of successful payment
5

Credits Added

Balance updated automatically in user profile

Premium Style Packs

Available Packs

Premium style collections for 3,000 credits each:
8 Styles - 3,000 credits
  • Baba Yaga
  • The Continental
  • High Table Legacy
  • Excommunicado
  • Desert Assassin
  • Ballerina Soul
  • Osaka Night
  • Last Stand

Unlocking Packs

const handleUnlockPack = async (subCategory) => {
  // Verify balance
  if (profile.credits < PREMIUM_PACK_PRICE) {
    setShowPricing(true);
    return;
  }
  
  // Update profile
  const newUnlockedPacks = [...(profile.unlocked_packs || []), subCategory];
  const newCredits = profile.credits - PREMIUM_PACK_PRICE;
  
  const { error } = await supabase
    .from('profiles')
    .update({
      credits: newCredits,
      unlocked_packs: newUnlockedPacks
    })
    .eq('id', session.user.id);
  
  // Confetti celebration!
  confetti({
    particleCount: 100,
    spread: 70,
    origin: { y: 0.6 }
  });
};
Premium pack unlocks are permanent and non-refundable. Make sure you want the pack before confirming.

Credit Balance Display

Always-visible credit counter in navigation:
<div className="flex items-center gap-2 px-4 py-2 bg-black/40 rounded-full">
  <Zap className="w-4 h-4 text-yellow-400" />
  <span className="font-bold">{profile.credits.toLocaleString()}</span>
  <span className="text-xs text-white/40">créditos</span>
</div>

Low Balance Warning

Alert when credits drop below generation cost:
if (profile.credits < 100) {
  return (
    <div className="bg-red-500/20 border border-red-500/40 p-4 rounded-xl">
      <AlertTriangle className="w-5 h-5" />
      <p>Saldo bajo. Recarga créditos para seguir generando.</p>
      <button onClick={() => setShowPricing(true)}>Comprar Créditos</button>
    </div>
  );
}

Partner & Event Credits

Partner Credit Allocation

Partners purchase credits in bulk:
  • Minimum: 1,000 credits
  • No maximum limit
  • Wholesale pricing (contact admin)
  • No expiration date

Event Credit Assignment

Partners allocate credits to events:
const handleCreateEvent = async (eventData) => {
  // Deduct from partner balance
  const newPartnerBalance = partner.credits - eventData.credits_allocated;
  
  // Create event with allocation
  await supabase.from('events').insert({
    ...eventData,
    credits_allocated: eventData.credits_allocated,
    credits_used: 0,
    partner_id: partner.id
  });
  
  // Update partner balance
  await supabase
    .from('partners')
    .update({ credits: newPartnerBalance })
    .eq('id', partner.id);
};

Credit Top-Up

Add credits to running events:
1

Open Event

Navigate to event in partner dashboard
2

Click Top-Up

Credit card icon in event header
3

Enter Amount

Specify additional credits (1-1000)
4

Confirm Transfer

Credits moved from partner to event atomically

Credit Consumption

Per Generation

Each AI photo costs 100 credits:
// User generation
await supabase
  .from('profiles')
  .update({ credits: profile.credits - 100 })
  .eq('id', userId);

// Event generation (atomic)
await supabase.rpc('event_generation', {
  event_id: eventId,
  user_id: userId
});

Refund Policy

Credits refunded if generation fails:
catch (error) {
  // Refund on failure
  await supabase
    .from('profiles')
    .update({ credits: profile.credits + 100 })
    .eq('id', userId);
  
  showToast('Error en generación. Créditos devueltos.', 'error');
}
Connection timeouts do NOT refund credits - the generation continues in the cloud.

Transaction History

All credit movements logged:
interface Transaction {
  id: string;
  user_id: string;
  amount: number;           // Positive = credit, negative = debit
  type: 'purchase' | 'generation' | 'pack_unlock' | 'refund' | 'transfer';
  details: object;
  created_at: timestamp;
}
View in profile history:
const { data: transactions } = await supabase
  .from('transactions')
  .select('*')
  .eq('user_id', userId)
  .order('created_at', { ascending: false });

Daily Limits

Free users limited to 2 generations per day:
const today = new Date();
today.setHours(0, 0, 0, 0);

const { count } = await supabase
  .from('generations')
  .select('*', { count: 'exact', head: true })
  .eq('user_id', userId)
  .gte('created_at', today.toISOString());

if (count >= 2) {
  showToast('Máximo de impresiones del día alcanzado.', 'error');
  return;
}
Daily limits reset at midnight (00:00 server time). Premium users and events have no daily limits.

Build docs developers (and LLMs) love