usage_credits 0.1.1 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09e65a66af34a0f2bf35a72d59fdd2d9d9799aaeaa1ac8b7628f222f65e5beab'
4
- data.tar.gz: 2dc466a29bc36385aeee7c8bbb77833e521c2207e84ac6cf00543253d02af74e
3
+ metadata.gz: 42e2dcff2633bf14d2390d436ab14f724fb1142bc6de5adf2b410ab36e13b785
4
+ data.tar.gz: fef29f9815fa2cb241ed30c342083f98003a607b9b6c95dfd1d86ed1df7a5753
5
5
  SHA512:
6
- metadata.gz: c1a6c0fc9dab7e315d67b6071b38d5e00b386bc21830438636405296b32fbabb510f0f31a77d995ea8d03972b939ebcf25aba3baa7f03847a66d2ee55b4a6a62
7
- data.tar.gz: fd4c0225c47dfca3903249b9456d428c9012060acb4f8beceb4c577e5673a4f650cdfd0ba74035bae5b253284e8ccb73eeb87a11b815fed4b9626f00968bf84a
6
+ metadata.gz: 59be8e233c0a004cc6ab205a5f80aa2c17ce38d64df2b7bd99f2521ab9a185779c309ca376b5ceee684420c27d1fe3111907aa5c26c52f3c96c2dba50187895f
7
+ data.tar.gz: 22cdd9089c33e9d50ecf598442d6aff6acd4924662542951e81eebbc3657d6300b8c66a746290aae6339b922d2ef7ef83caa097113aa3876a19cb3265c4ee0c4
data/.simplecov ADDED
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ SimpleCov.start 'rails' do
4
+ # Coverage directory
5
+ coverage_dir 'coverage'
6
+
7
+ # Enable branch coverage (must be before minimum_coverage)
8
+ enable_coverage :branch
9
+
10
+ # Set minimum coverage threshold to prevent coverage regression
11
+ # Current coverage: Line 84.38%, Branch 71.9%
12
+ # Note: Some paths (PostgreSQL JSON operators, error fallbacks) may not be exercised in SQLite tests
13
+ minimum_coverage line: 75, branch: 60
14
+
15
+ # Add custom groups for better organization
16
+ add_group 'Models', 'lib/usage_credits/models'
17
+ add_group 'Services', 'lib/usage_credits/services'
18
+ add_group 'Helpers', 'lib/usage_credits/helpers'
19
+ add_group 'Jobs', 'lib/usage_credits/jobs'
20
+ add_group 'Concerns', 'lib/usage_credits/models/concerns'
21
+ add_group 'DSL', ['lib/usage_credits/operation.rb', 'lib/usage_credits/credit_pack.rb', 'lib/usage_credits/credit_subscription_plan.rb']
22
+
23
+ # Filter out files we don't want to track
24
+ add_filter '/test/'
25
+ add_filter '/spec/'
26
+ add_filter '/config/'
27
+ add_filter '/db/'
28
+ add_filter '/vendor/'
29
+ add_filter '/bin/'
30
+
31
+ # Track all Ruby files in lib
32
+ track_files 'lib/**/*.rb'
33
+
34
+ # Disambiguate parallel test runs
35
+ command_name "Job #{ENV['TEST_ENV_NUMBER']}" if ENV['TEST_ENV_NUMBER']
36
+
37
+ # Use different formatters for CI vs local
38
+ if ENV['CI']
39
+ # CI: Use simple formatter for console output
40
+ formatter SimpleCov::Formatter::SimpleFormatter
41
+ else
42
+ # Local: Use HTML formatter for detailed report
43
+ formatter SimpleCov::Formatter::HTMLFormatter
44
+ end
45
+
46
+ # Merge results from parallel runs
47
+ merge_timeout 3600
48
+ end
data/AGENTS.md ADDED
@@ -0,0 +1,5 @@
1
+ # AGENTS.md
2
+
3
+ This file provides guidance to AI Agents (like OpenAI's Codex, Claude Code, etc) when working with code in this repository.
4
+
5
+ Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
data/Appraisals ADDED
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Test minimum supported Rails version (with latest Pay)
4
+ appraise "rails-7.2" do
5
+ gem "rails", "~> 7.2.0"
6
+ gem "pay", "~> 11.0"
7
+ gem "stripe", "~> 18.0"
8
+ end
9
+
10
+ # Test latest Rails version (with latest Pay) - this is the default/main Gemfile anyway
11
+ appraise "rails-8.1" do
12
+ gem "rails", "~> 8.1.0"
13
+ gem "pay", "~> 11.0"
14
+ gem "stripe", "~> 18.0"
15
+ end
16
+
17
+ # Test minimum supported Pay version (with latest Rails)
18
+ appraise "pay-8.3" do
19
+ gem "pay", "~> 8.3.0"
20
+ gem "stripe", "~> 13.0"
21
+ gem "rails", "~> 8.1.0"
22
+ end
23
+
24
+ # Test Pay 9.0 (popular stable version with latest Rails)
25
+ appraise "pay-9.0" do
26
+ gem "pay", "~> 9.0.0"
27
+ gem "stripe", "~> 13.0"
28
+ gem "rails", "~> 8.1.0"
29
+ end
30
+
31
+ # Test Pay 10.0 (with latest Rails)
32
+ appraise "pay-10.0" do
33
+ gem "pay", "~> 10.0.0"
34
+ gem "stripe", "~> 15.0"
35
+ gem "rails", "~> 8.1.0"
36
+ end
37
+
38
+ # Test latest Pay version (with latest Rails)
39
+ appraise "pay-11.0" do
40
+ gem "pay", "~> 11.0"
41
+ gem "stripe", "~> 18.0"
42
+ gem "rails", "~> 8.1.0"
43
+ end
data/CHANGELOG.md CHANGED
@@ -1,3 +1,23 @@
1
+ ## [0.2.1] - 2026-01-15
2
+
3
+ - Add custom `create_checkout_session` options (like `success_url`) to credit pack purchases by @yshmarov in https://github.com/rameerez/usage_credits/pull/5
4
+
5
+ ## [0.2.0] - 2025-12-29
6
+
7
+ - Add Claude Code GitHub Workflow by @rameerez in https://github.com/rameerez/usage_credits/pull/14
8
+ - Add test suite by @rameerez in https://github.com/rameerez/usage_credits/pull/15
9
+ - Update Pay gem dependency to support versions 8.3 to 9.x by @rameerez in https://github.com/rameerez/usage_credits/pull/16
10
+ - Update Pay gem dependency to support version 8.3 to < 10.0 by @kaka-ruto in https://github.com/rameerez/usage_credits/pull/10
11
+ - Add Pay version matrix testing with Appraisal by @rameerez in https://github.com/rameerez/usage_credits/pull/17
12
+ - Upgrade Pay dependency to support version 10.x by @rameerez in https://github.com/rameerez/usage_credits/pull/18
13
+ - Upgrade Pay dependency to support version 11.x by @rameerez in https://github.com/rameerez/usage_credits/pull/19
14
+ - Remove payment intent metadata from Subscription checkout session by @cole-robertson in https://github.com/rameerez/usage_credits/pull/2
15
+ - Handle subscription plan changes (upgrades & downgrades) by @rameerez in https://github.com/rameerez/usage_credits/pull/20
16
+ - Add configurable minimum fulfillment period for dev/test flexibility by @rameerez in https://github.com/rameerez/usage_credits/pull/21
17
+ - Add multi-period Stripe price support for subscription plans by @rameerez in https://github.com/rameerez/usage_credits/pull/22
18
+ - Fix a bug where very fast fulfillment periods would cause credits not to expire fast enough by @rameerez in https://github.com/rameerez/usage_credits/pull/23
19
+ - Fix incomplete fulfillment update on subscription plan upgrade by @rameerez in https://github.com/rameerez/usage_credits/pull/24
20
+
1
21
  ## [0.1.1] - 2025-01-14
2
22
 
3
23
  - Rename `Wallet#subscriptions` to `Wallet.credit_subscriptions` so that it doesn’t override the Pay gem’s own subscriptions association on `User`
data/CLAUDE.md ADDED
@@ -0,0 +1,5 @@
1
+ # CLAUDE.md
2
+
3
+ This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
+
5
+ Please go ahead and read the full context for this project at `.cursor/rules/0-overview.mdc` and `.cursor/rules/1-quality.mdc` now. Also read the README for a good overview of the project.
data/README.md CHANGED
@@ -1,11 +1,16 @@
1
1
  # 💳✨ `usage_credits` - Add usage-based credits to your Rails app
2
2
 
3
- [![Gem Version](https://badge.fury.io/rb/usage_credits.svg)](https://badge.fury.io/rb/usage_credits)
3
+ [![Gem Version](https://badge.fury.io/rb/usage_credits.svg)](https://badge.fury.io/rb/usage_credits) [![Build Status](https://github.com/rameerez/usage_credits/workflows/Tests/badge.svg)](https://github.com/rameerez/usage_credits/actions)
4
4
 
5
- Allow your users to have in-app credits / tokens they can use to perform operations.
5
+ > [!TIP]
6
+ > **🚀 Ship your next Rails app 10x faster!** I've built **[RailsFast](https://railsfast.com)**, a production-ready Rails boilerplate template that comes with everything you need to launch a software business in days, not weeks.
7
+
8
+ `usage_credits` allows your users to have in-app credits / tokens they can use to perform operations.
6
9
 
7
10
  ✨ Perfect for SaaS, AI apps, games, and API products that want to implement usage-based pricing.
8
11
 
12
+ [ 🟢 [Live interactive demo website](https://usagecredits.com/) ] [ 🎥 [Quick video overview](https://x.com/rameerez/status/1890419563189195260) ]
13
+
9
14
  Refill user credits with Stripe subscriptions, allow your users to top up by purchasing booster credit packs at any time, rollover unused credits to the next billing period, expire credits, implement PAYG (pay-as-you-go) billing, award free credits as bonuses (for referrals, giving feedback, etc.), get a detailed history and audit trail of every transaction for billing / reporting, and more!
10
15
 
11
16
  All with a simple DSL that reads just like English.
@@ -383,6 +388,31 @@ end
383
388
 
384
389
  For now, only Stripe subscriptions are supported (contribute to the codebase to help us add more payment processors!)
385
390
 
391
+ #### Subscriptions with a monthly + yearly price
392
+
393
+ For plans that offer multiple billing periods (e.g., monthly and yearly pricing), you can specify multiple Stripe price IDs using a hash:
394
+
395
+ ```ruby
396
+ subscription_plan :pro do
397
+ # Multi-period pricing: same plan, different billing frequencies
398
+ stripe_price month: "price_pro_monthly", year: "price_pro_yearly"
399
+
400
+ gives 10_000.credits.every(:month)
401
+ # ...
402
+ end
403
+ ```
404
+
405
+ The gem automatically handles plan matching for webhook events - when Stripe sends subscription events, the plan will be correctly identified regardless of which billing period was selected.
406
+
407
+ **Note:** Single-price plans (the traditional format) continue to work as before for backward compatibility:
408
+
409
+ ```ruby
410
+ subscription_plan :basic do
411
+ stripe_price "price_basic_monthly" # Single price ID (still works)
412
+ gives 1_000.credits.every(:month)
413
+ end
414
+ ```
415
+
386
416
  ### Specify a fulfillment period
387
417
 
388
418
  Next, specify how many credits a user subscribed to this plan gets, and when they get them.
@@ -413,6 +443,49 @@ subscription_plan :pro do
413
443
  end
414
444
  ```
415
445
 
446
+ ### Grace period for credit expiration
447
+
448
+ When credits expire (i.e., `unused_credits :expire`), there's a configurable **grace period** to ensure smooth transitions between fulfillment cycles. This prevents users from seeing zero credits between the moment old credits expire and new credits are fulfilled.
449
+
450
+ ```ruby
451
+ UsageCredits.configure do |config|
452
+ # Grace period for credit expiration (default: 5 minutes)
453
+ # Should match the frequency of your UsageCredits::FulfillmentJob runs
454
+ config.fulfillment_grace_period = 5.minutes
455
+ end
456
+ ```
457
+
458
+ > [!NOTE]
459
+ > **Automatic grace period capping:** If your fulfillment period is shorter than the grace period (e.g., credits every 15 seconds with a 5-minute grace period), the grace period is **automatically capped** to the fulfillment period. This prevents balance accumulation where credits pile up faster than they expire.
460
+ >
461
+ > For example, with `gives 100.credits.every(15.seconds)` and a 5-minute grace period, the effective grace period will be 15 seconds, not 5 minutes. A warning will be logged during initialization to alert you of this behavior.
462
+ >
463
+ > This is typically only relevant for development/testing with very short fulfillment periods. In production with monthly or daily fulfillment cycles, the default 5-minute grace period works perfectly.
464
+
465
+ ### Upgrades, downgrades, and plan changes
466
+
467
+ `usage_credits` reacts to plan changes (via the `pay` gem), and we handle automatically credit issuing for upgrades & downgrades:
468
+
469
+ - **Upgrades**: credits are granted immediately for the new plan. If the new plan expires credits, upgrade credits expire too.
470
+ - **Downgrades**: scheduled for the end of the current period; users keep current benefits until then.
471
+ - **Non-credit plan changes**: moving from credit → non-credit stops fulfillment at period end (no clawback).
472
+ - **Reactivation**: moving back to a credit plan reactivates fulfillment and grants credits (no signup bonus on reactivation).
473
+ - **Pending downgrades**: if a user returns to their current plan before the downgrade takes effect, we cancel the pending change and do **not** grant extra credits.
474
+ - **Credit gaming prevention**: we take measures to protect against user gaming the credit system by repeatedly upgrading/downgrading their subscription.
475
+
476
+ This happens automatically thanks to our Pay Subscription extension (changes to the `Subscription` model in the `pay` gem trigger `usage_credits` issuing the right credits based on the subscription change)
477
+
478
+ ### What we handle vs. what we don't (brief)
479
+
480
+ Handled:
481
+ - Subscription create, renew, cancel, upgrade, downgrade, non-credit transitions
482
+ - Pending downgrade application on renewal
483
+ - Credit expiration and rollover
484
+
485
+ Not handled (yet):
486
+ - Plan changes while **trialing** (we only handle `status == "active"`)
487
+ - Paused subscriptions (see TODO in code)
488
+
416
489
  ## Transaction history & audit trail
417
490
 
418
491
  Every transaction (whether adding or deducting credits) is logged in the ledger, and automatically tracked with metadata:
@@ -469,6 +542,10 @@ Which will get you:
469
542
 
470
543
  It's useful if you want to name your credits something else (tokens, virtual currency, tasks, in-app gems, whatever) and you want the name to be consistent.
471
544
 
545
+ ## Demo Rails app
546
+
547
+ There's a demo Rails app showcasing the features in the `usage_credits` gem under `test/dummy`. It's currently deployed to `usagecredits.com`. If you want to run it yourself locally, you can just clone this repo, `cd` into the `test/dummy` folder, and then `bundle` and `rails s` to launch it. You can examine the code of the demo app to better understand the gem.
548
+
472
549
  ## Technical notes on architecture and how this gem is built
473
550
 
474
551
  Building a usage credits system is deceptively complex.
@@ -558,9 +635,7 @@ Real billing systems usually find edge cases when handling things like:
558
635
  Please help us by contributing to add tests to cover all critical paths!
559
636
 
560
637
  ## TODO
561
-
562
- - [ ] Write a comprehensive `minitest` test suite that covers all critical paths (both happy paths and weird edge cases)
563
- - [ ] Handle subscription upgrades and downgrades (upgrade immediately; downgrade at end of billing period? Cover all scenarios allowed by the Stripe Customer Portal?)
638
+ No open TODOs here right now. If you find an edge case, please open an issue or PR.
564
639
 
565
640
  ## Testing
566
641
 
@@ -574,8 +649,7 @@ To install this gem onto your local machine, run `bundle exec rake install`.
574
649
 
575
650
  ## Contributing
576
651
 
577
- Bug reports and pull requests are welcome on GitHub at https://github.com/rameerez/usage_credits. Our code of conduct is: just be nice and make your mom proud of what
578
- you do and post online.
652
+ Bug reports and pull requests are welcome on GitHub at https://github.com/rameerez/usage_credits. Our code of conduct is: just be nice and make your mom proud of what you do and post online.
579
653
 
580
654
  ## License
581
655
 
@@ -0,0 +1,41 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "pay", "~> 10.0.0"
7
+ gem "stripe", "~> 15.0"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ group :development do
11
+ gem "appraisal"
12
+ gem "web-console"
13
+ gem "standard"
14
+ gem "rubocop", "~> 1.0"
15
+ gem "rubocop-minitest", "~> 0.35"
16
+ gem "rubocop-performance", "~> 1.0"
17
+ end
18
+
19
+ group :test do
20
+ gem "minitest", "~> 5.0"
21
+ gem "mocha"
22
+ gem "simplecov", require: false
23
+ gem "vcr"
24
+ gem "webmock"
25
+ gem "braintree", ">= 2.92.0"
26
+ gem "lemonsqueezy", "~> 1.0"
27
+ gem "paddle", "~> 2.6"
28
+ gem "prawn"
29
+ gem "receipts"
30
+ gem "sqlite3"
31
+ gem "pg"
32
+ gem "bootsnap", require: false
33
+ gem "puma"
34
+ gem "importmap-rails"
35
+ gem "sprockets-rails"
36
+ gem "stimulus-rails"
37
+ gem "turbo-rails"
38
+ gem "rdoc", ">= 7.0"
39
+ end
40
+
41
+ gemspec path: "../"
@@ -0,0 +1,41 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "pay", "~> 11.0"
7
+ gem "stripe", "~> 18.0"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ group :development do
11
+ gem "appraisal"
12
+ gem "web-console"
13
+ gem "standard"
14
+ gem "rubocop", "~> 1.0"
15
+ gem "rubocop-minitest", "~> 0.35"
16
+ gem "rubocop-performance", "~> 1.0"
17
+ end
18
+
19
+ group :test do
20
+ gem "minitest", "~> 5.0"
21
+ gem "mocha"
22
+ gem "simplecov", require: false
23
+ gem "vcr"
24
+ gem "webmock"
25
+ gem "braintree", ">= 2.92.0"
26
+ gem "lemonsqueezy", "~> 1.0"
27
+ gem "paddle", "~> 2.6"
28
+ gem "prawn"
29
+ gem "receipts"
30
+ gem "sqlite3"
31
+ gem "pg"
32
+ gem "bootsnap", require: false
33
+ gem "puma"
34
+ gem "importmap-rails"
35
+ gem "sprockets-rails"
36
+ gem "stimulus-rails"
37
+ gem "turbo-rails"
38
+ gem "rdoc", ">= 7.0"
39
+ end
40
+
41
+ gemspec path: "../"
@@ -0,0 +1,41 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "pay", "~> 8.3.0"
7
+ gem "stripe", "~> 13.0"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ group :development do
11
+ gem "appraisal"
12
+ gem "web-console"
13
+ gem "standard"
14
+ gem "rubocop", "~> 1.0"
15
+ gem "rubocop-minitest", "~> 0.35"
16
+ gem "rubocop-performance", "~> 1.0"
17
+ end
18
+
19
+ group :test do
20
+ gem "minitest", "~> 5.0"
21
+ gem "mocha"
22
+ gem "simplecov", require: false
23
+ gem "vcr"
24
+ gem "webmock"
25
+ gem "braintree", ">= 2.92.0"
26
+ gem "lemonsqueezy", "~> 1.0"
27
+ gem "paddle", "~> 2.6"
28
+ gem "prawn"
29
+ gem "receipts"
30
+ gem "sqlite3"
31
+ gem "pg"
32
+ gem "bootsnap", require: false
33
+ gem "puma"
34
+ gem "importmap-rails"
35
+ gem "sprockets-rails"
36
+ gem "stimulus-rails"
37
+ gem "turbo-rails"
38
+ gem "rdoc", ">= 7.0"
39
+ end
40
+
41
+ gemspec path: "../"
@@ -0,0 +1,41 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "pay", "~> 9.0.0"
7
+ gem "stripe", "~> 13.0"
8
+ gem "rails", "~> 8.1.0"
9
+
10
+ group :development do
11
+ gem "appraisal"
12
+ gem "web-console"
13
+ gem "standard"
14
+ gem "rubocop", "~> 1.0"
15
+ gem "rubocop-minitest", "~> 0.35"
16
+ gem "rubocop-performance", "~> 1.0"
17
+ end
18
+
19
+ group :test do
20
+ gem "minitest", "~> 5.0"
21
+ gem "mocha"
22
+ gem "simplecov", require: false
23
+ gem "vcr"
24
+ gem "webmock"
25
+ gem "braintree", ">= 2.92.0"
26
+ gem "lemonsqueezy", "~> 1.0"
27
+ gem "paddle", "~> 2.6"
28
+ gem "prawn"
29
+ gem "receipts"
30
+ gem "sqlite3"
31
+ gem "pg"
32
+ gem "bootsnap", require: false
33
+ gem "puma"
34
+ gem "importmap-rails"
35
+ gem "sprockets-rails"
36
+ gem "stimulus-rails"
37
+ gem "turbo-rails"
38
+ gem "rdoc", ">= 7.0"
39
+ end
40
+
41
+ gemspec path: "../"
@@ -0,0 +1,41 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "rails", "~> 7.2.0"
7
+ gem "pay", "~> 11.0"
8
+ gem "stripe", "~> 18.0"
9
+
10
+ group :development do
11
+ gem "appraisal"
12
+ gem "web-console"
13
+ gem "standard"
14
+ gem "rubocop", "~> 1.0"
15
+ gem "rubocop-minitest", "~> 0.35"
16
+ gem "rubocop-performance", "~> 1.0"
17
+ end
18
+
19
+ group :test do
20
+ gem "minitest", "~> 5.0"
21
+ gem "mocha"
22
+ gem "simplecov", require: false
23
+ gem "vcr"
24
+ gem "webmock"
25
+ gem "braintree", ">= 2.92.0"
26
+ gem "lemonsqueezy", "~> 1.0"
27
+ gem "paddle", "~> 2.6"
28
+ gem "prawn"
29
+ gem "receipts"
30
+ gem "sqlite3"
31
+ gem "pg"
32
+ gem "bootsnap", require: false
33
+ gem "puma"
34
+ gem "importmap-rails"
35
+ gem "sprockets-rails"
36
+ gem "stimulus-rails"
37
+ gem "turbo-rails"
38
+ gem "rdoc", ">= 7.0"
39
+ end
40
+
41
+ gemspec path: "../"
@@ -0,0 +1,41 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "rake", "~> 13.0"
6
+ gem "rails", "~> 8.1.0"
7
+ gem "pay", "~> 11.0"
8
+ gem "stripe", "~> 18.0"
9
+
10
+ group :development do
11
+ gem "appraisal"
12
+ gem "web-console"
13
+ gem "standard"
14
+ gem "rubocop", "~> 1.0"
15
+ gem "rubocop-minitest", "~> 0.35"
16
+ gem "rubocop-performance", "~> 1.0"
17
+ end
18
+
19
+ group :test do
20
+ gem "minitest", "~> 5.0"
21
+ gem "mocha"
22
+ gem "simplecov", require: false
23
+ gem "vcr"
24
+ gem "webmock"
25
+ gem "braintree", ">= 2.92.0"
26
+ gem "lemonsqueezy", "~> 1.0"
27
+ gem "paddle", "~> 2.6"
28
+ gem "prawn"
29
+ gem "receipts"
30
+ gem "sqlite3"
31
+ gem "pg"
32
+ gem "bootsnap", require: false
33
+ gem "puma"
34
+ gem "importmap-rails"
35
+ gem "sprockets-rails"
36
+ gem "stimulus-rails"
37
+ gem "turbo-rails"
38
+ gem "rdoc", ">= 7.0"
39
+ end
40
+
41
+ gemspec path: "../"
@@ -1,6 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  UsageCredits.configure do |config|
4
+ #
5
+ # Minimum fulfillment period for subscription plans (default: 1.day)
6
+ # In development/test, you can set this to a shorter period for faster testing:
7
+ #
8
+ # config.min_fulfillment_period = 2.seconds if Rails.env.development?
9
+ #
10
+ #
4
11
  #
5
12
  # Define your credit-consuming operations below
6
13
  #
@@ -55,6 +62,9 @@ UsageCredits.configure do |config|
55
62
  # gives 1000.credits.every(:month)
56
63
  # signup_bonus 100.credits
57
64
  # unused_credits :expire # Credits reset each month
65
+ #
66
+ # # Single price (backward compatible)
67
+ # stripe_price "price_basic_monthly"
58
68
  # end
59
69
  #
60
70
  # subscription_plan :pro do
@@ -62,6 +72,13 @@ UsageCredits.configure do |config|
62
72
  # signup_bonus 1_000.credits
63
73
  # trial_includes 500.credits
64
74
  # unused_credits :expire # Credits expire at the end of the fulfillment period (use :rollover to roll over to next period)
75
+ #
76
+ # # Multi-period pricing (monthly + yearly)
77
+ # stripe_price month: "price_pro_monthly", year: "price_pro_yearly"
78
+ #
79
+ # # When creating checkout sessions with multi-period plans, specify the period:
80
+ # # plan.create_checkout_session(user, success_url: "/success", cancel_url: "/cancel", period: :month)
81
+ # # plan.create_checkout_session(user, success_url: "/success", cancel_url: "/cancel", period: :year)
65
82
  # end
66
83
  #
67
84
  #
@@ -81,11 +98,22 @@ UsageCredits.configure do |config|
81
98
  #
82
99
  #
83
100
  #
84
- # For how long expiring credits from the previous fulfillment cycle will "overlap" the following fulfillment period.
101
+ # Grace period for credit expiration (default: 5.minutes)
102
+ #
103
+ # This is for how long expiring credits from the previous fulfillment cycle will "overlap" the following fulfillment period.
85
104
  # During this time, old credits from the previous period will erroneously count as available balance.
86
105
  # But if we set this to 0 or nil, user balance will show up as zero some time until the next fulfillment cycle hits.
87
106
  # A good default is to match the frequency of your UsageCredits::FulfillmentJob
88
- # fulfillment_grace_period = 5.minutes
107
+ #
108
+ # config.fulfillment_grace_period = 5.minutes
109
+ #
110
+ # NOTE: If your fulfillment period is shorter than the grace period (e.g., credits
111
+ # every 15 seconds with a 5-minute grace), the grace period is AUTOMATICALLY CAPPED
112
+ # to the fulfillment period. This prevents balance accumulation where credits pile
113
+ # up faster than they expire. A warning will be logged during initialization.
114
+ #
115
+ # This is typically only relevant for development/testing with very short periods.
116
+ # In production with monthly/daily fulfillment cycles, the default should work just fine.
89
117
  #
90
118
  #
91
119
  #