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.
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
-
Uncomment
gem "usage_credits"in theGemfile(it's under PROJECT-BASED GEMS) and runbundle install. -
Run the gem's install generator and migrate:
rails generate usage_credits:install rails db:migrate -
Add the model hook to
app/models/user.rb:has_credits -
Configure your credit packs, subscription refills, and operations in
config/initializers/usage_credits.rb(created by the generator — the examples below go there). -
If you sell subscription refills, uncomment the
refill_creditsentries inconfig/recurring.ymlsoUsageCredits::FulfillmentJobruns on schedule.
For the full API (packs, expiring credits, rollover, refunds, low-balance alerts), the usage_credits README is the source of truth.
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
- Define your plans in
config/initializers/usage_credits.rb - Define your operations with their costs
- Uncomment the fulfillment job in
config/recurring.yml:refill_credits: class: UsageCredits::FulfillmentJob queue: default schedule: every 5 minutes - Wire up Stripe prices to your plans
- Add
spend_credits_oncalls 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.