DOCS LLMs

Usage credits

RailsFast supports a fully working usage credits system as an opt-in feature, powered by our usage_credits gem: keep track of how many credits each of your users has, and let them spend credits on your app.

NOTE

As of RailsFast v1.0.0, usage_credits is not enabled by default — most products (a typical SaaS with plans and limits) never need credits, and pricing_plans already covers plan limits. Credits shine when you sell consumable usage: AI generations, API calls, per-minute processing.

Turning it on

  1. Uncomment gem "usage_credits" in the Gemfile (it's under PROJECT-BASED GEMS) and run bundle install.

  2. Run the gem's install generator and migrate:

    rails generate usage_credits:install
    rails db:migrate
  3. Add the model hook to app/models/user.rb:

    has_credits
  4. Configure your credit packs, subscription refills, and operations in config/initializers/usage_credits.rb (created by the generator — the examples below go there).

  5. If you sell subscription refills, uncomment the refill_credits entries in config/recurring.yml so UsageCredits::FulfillmentJob runs on schedule.

For the full API (packs, expiring credits, rollover, refunds, low-balance alerts), the usage_credits README is the source of truth.

TIP

If what you actually need is money-like balances (deposits, withdrawals, transfers between users) rather than consumable credits, use the wallets gem instead — see Beyond credits: wallets.

The 30-second version

# Users get credits from subscriptions
subscription_plan :pro do
  stripe_price "price_xyz"
  gives 10_000.credits.every(:month)
end

# Operations cost credits
operation :generate_image do
  costs 50.credits
end

# Spend credits safely
@user.spend_credits_on(:generate_image) do
  ImageGenerator.call(params)  # Credits only deducted if this succeeds
end

That's it. Stripe handles billing, RailsFast handles fulfillment, you handle your product.

What you can build

Product Type Credits Model Example
AI/ML API Per-request or per-token "Generate image costs 50 credits"
SaaS with limits Monthly allocation "Pro plan: 10,000 API calls/month"
Metered billing Pay-as-you-go "1 credit per MB processed"
Freemium Trial + paid tiers "Free: 100 credits, Pro: unlimited"

Real examples

AI Image Generator (like Midjourney)

# config/initializers/usage_credits.rb

subscription_plan :creator do
  stripe_price month: "price_creator_monthly", year: "price_creator_yearly"
  gives 1_000.credits.every(:month)
  unused_credits :rollover  # Unused credits carry over
end

operation :generate_image do
  costs 10.credits
end

operation :upscale_image do
  costs 25.credits
end

operation :generate_video do
  costs 100.credits + 10.credits_per(:second)
end
# In your controller
def create
  @user.spend_credits_on(:generate_image) do
    @image = ImageAI.generate(params[:prompt])
  end
rescue UsageCredits::InsufficientCredits
  redirect_to pricing_path, alert: "You need more credits!"
end

Document Processing API (like DocuSign API)

operation :process_document do
  costs 5.credits + 1.credit_per(:page)
end

# Usage
pages = PDF.page_count(uploaded_file)
cost = @user.estimate_credits_to(:process_document, pages: pages)

if @user.has_enough_credits_to?(:process_document, pages: pages)
  @user.spend_credits_on(:process_document, pages: pages) do
    DocumentProcessor.process(uploaded_file)
  end
end

Transcription Service (like Whisper API)

operation :transcribe_audio do
  costs 1.credit_per(:minute)  # 1 credit per minute of audio
end

# Show cost before processing
duration_minutes = AudioFile.duration(file) / 60.0
puts "This will cost #{@user.estimate_credits_to(:transcribe_audio, minutes: duration_minutes)} credits"

Credit lifecycle

┌─────────────────────────────────────────────────────────────┐
│                    How users get credits                    │
├─────────────────────────────────────────────────────────────┤
│  Subscription    │  "Pro plan gives 10,000/month"           │
│  Credit pack     │  "Buy 5,000 credits for $49"             │
│  Bonus           │  "Referral bonus: 500 credits"           │
│  Trial           │  "Free trial: 100 credits"               │
└─────────────────────────────────────────────────────────────┘
                              │
                              ▼
┌─────────────────────────────────────────────────────────────┐
│                    What happens to credits                  │
├─────────────────────────────────────────────────────────────┤
│  Spent           │  spend_credits_on(:operation)            │
│  Expired         │  expires_at: 30.days.from_now            │
│  Rolled over     │  unused_credits :rollover                │
│  Refunded        │  Automatic on Stripe refund              │
└─────────────────────────────────────────────────────────────┘

Subscription + credit packs together

Users can have a subscription AND buy extra credit packs:

# Monthly subscription
subscription_plan :pro do
  stripe_price "price_pro_monthly"
  gives 5_000.credits.every(:month)
end

# One-time booster packs
credit_pack :small_boost do
  gives 1_000.credits
  costs 9.dollars
end

credit_pack :large_boost do
  gives 10_000.credits
  costs 49.dollars
end
# In your controller - let users buy extra credits anytime
def purchase_credits
  pack = UsageCredits.find_credit_pack(params[:pack])
  session = pack.create_checkout_session(current_user)
  redirect_to session.url
end

Handling edge cases

Low balance alerts

UsageCredits.configure do |config|
  config.low_balance_threshold = 100

  config.on_low_balance_reached do |ctx|
    UserMailer.low_credits(ctx.owner).deliver_later
  end
end

Show remaining credits in UI

<!-- In your navbar or dashboard -->
<span class="credits-badge">
  <%= current_user.credits %> credits remaining
</span>

<!-- With formatted display -->
<span><%= current_user.credit_wallet.formatted_balance %></span>

Transaction history for billing

# Show users their credit usage
@transactions = current_user.credit_history.recent.limit(50)
<% @transactions.each do |tx| %>
  <tr>
    <td><%= tx.created_at.strftime("%b %d") %></td>
    <td><%= tx.category.humanize %></td>
    <td class="<%= tx.amount.positive? ? 'text-green' : 'text-red' %>">
      <%= tx.formatted_amount %>
    </td>
    <td><%= tx.formatted_balance_after %></td>
  </tr>
<% end %>

Quick setup checklist

  1. Define your plans in config/initializers/usage_credits.rb
  2. Define your operations with their costs
  3. Uncomment the fulfillment job in config/recurring.yml:
    refill_credits:
      class: UsageCredits::FulfillmentJob
      queue: default
      schedule: every 5 minutes
  4. Wire up Stripe prices to your plans
  5. Add spend_credits_on calls where you want to charge credits

Wallet-level operations

usage_credits is built on the wallets gem, which provides the underlying ledger. This means wallet-level operations like transfers are available:

# Transfer credits between users (refunds, gifting, etc.)
seller.credit_wallet.transfer_to(
  buyer.credit_wallet,
  500,
  category: :refund,
  metadata: { order_id: 42 }
)

Transfers preserve expiration buckets by default. For cash-like behavior (evergreen on receive):

seller.credit_wallet.transfer_to(
  buyer.credit_wallet,
  500,
  expiration_policy: :none
)

Going deeper

The usage_credits gem documentation covers:

  • Dynamic pricing (costs 10.credits + 1.credit_per(:mb))
  • Credit expiration and rollover policies
  • Upgrade/downgrade handling
  • Refund behavior
  • Full callback system for analytics
  • Transaction audit trails
  • Wallet-level transfers and expiration policies

For multi-currency wallets, game resources, or marketplace balances, see Beyond credits: wallets for games, telecom, and marketplaces.