Skip to content

Commit 2b2fc4e

Browse files
rameerezclaude
andcommitted
Add comprehensive tests for Stripe checkout sessions
This adds test coverage for both purchase modes to prevent regressions: ## Subscription checkout tests (4 tests) - Verifies subscription_data metadata is passed correctly - Verifies payment_intent_data is NOT passed (validates fix from #2) - Verifies checkout session delegation works - Verifies all plan configuration is included in metadata ## Credit pack checkout tests (5 tests) - Verifies payment mode is used for one-time purchases - Verifies payment_intent_data IS passed (correct for payment mode) - Verifies all pack configuration in metadata - Verifies product name and description - Verifies error handling These tests validate the fix in PR #2 by @cole-robertson and ensure both subscription and one-time purchase flows work correctly with Stripe's API requirements. According to Stripe API docs, payment_intent_data is only valid for mode: "payment" checkout sessions, not mode: "subscription". The gem correctly uses: - payment_intent_data for credit packs (payment mode) - subscription_data for subscriptions (subscription mode) Relates-to: #2 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 268336b commit 2b2fc4e

File tree

3 files changed

+292
-0
lines changed

3 files changed

+292
-0
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pay.md
2626
deploy.yml
2727

2828
test/dummy/log/*
29+
test/coverage/*
2930

3031
.cursor/
3132
.claude/

test/models/usage_credits/credit_pack_test.rb

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,4 +396,151 @@ class UsageCredits::CreditPackTest < ActiveSupport::TestCase
396396
assert_equal 0.99, pack.price
397397
assert pack.validate!
398398
end
399+
400+
# ========================================
401+
# STRIPE CHECKOUT SESSION CREATION
402+
# ========================================
403+
404+
test "create_checkout_session uses payment mode for one-time purchases" do
405+
pack = UsageCredits::CreditPack.new(:starter)
406+
pack.gives(1000)
407+
pack.costs(49.dollars)
408+
409+
user = users(:rich_user)
410+
411+
# Mock the payment processor to verify the arguments passed
412+
mock_payment_processor = Minitest::Mock.new
413+
mock_checkout_session = OpenStruct.new(url: "https://checkout.stripe.com/test")
414+
415+
# Verify payment mode is used (not subscription mode)
416+
mock_payment_processor.expect(:checkout, mock_checkout_session) do |args|
417+
assert_equal "payment", args[:mode], "Credit packs should use payment mode"
418+
assert args[:line_items].present?, "line_items should be present"
419+
420+
# Verify line items structure
421+
line_item = args[:line_items].first
422+
assert line_item[:price_data].present?, "price_data should be present for dynamic pricing"
423+
assert_equal "usd", line_item[:price_data][:currency]
424+
assert_equal 4900, line_item[:price_data][:unit_amount]
425+
assert_equal 1, line_item[:quantity]
426+
427+
true
428+
end
429+
430+
user.stub(:payment_processor, mock_payment_processor) do
431+
pack.create_checkout_session(user)
432+
end
433+
434+
mock_payment_processor.verify
435+
end
436+
437+
test "create_checkout_session passes payment_intent_data for payment mode" do
438+
pack = UsageCredits::CreditPack.new(:pro)
439+
pack.gives(5000)
440+
pack.costs(99.dollars)
441+
442+
user = users(:rich_user)
443+
444+
# Mock the payment processor to verify payment_intent_data is passed
445+
mock_payment_processor = Minitest::Mock.new
446+
mock_checkout_session = OpenStruct.new(url: "https://checkout.stripe.com/test")
447+
448+
# For payment mode, payment_intent_data IS allowed and should be present
449+
mock_payment_processor.expect(:checkout, mock_checkout_session) do |args|
450+
assert_equal "payment", args[:mode]
451+
452+
# payment_intent_data is VALID for payment mode (unlike subscription mode)
453+
assert args.key?(:payment_intent_data), "payment_intent_data should be present for payment mode"
454+
assert args[:payment_intent_data][:metadata].present?, "payment_intent_data metadata should be present"
455+
456+
# Verify metadata contains pack info
457+
metadata = args[:payment_intent_data][:metadata]
458+
assert_equal "credit_pack", metadata[:purchase_type]
459+
assert_equal :pro, metadata[:pack_name]
460+
assert_equal 5000, metadata[:credits]
461+
462+
true
463+
end
464+
465+
user.stub(:payment_processor, mock_payment_processor) do
466+
pack.create_checkout_session(user)
467+
end
468+
469+
mock_payment_processor.verify
470+
end
471+
472+
test "create_checkout_session includes all pack configuration in metadata" do
473+
pack = UsageCredits::CreditPack.new(:enterprise)
474+
pack.gives(10_000)
475+
pack.bonus(2_000)
476+
pack.costs(199.dollars)
477+
478+
user = users(:rich_user)
479+
480+
mock_payment_processor = Minitest::Mock.new
481+
mock_checkout_session = OpenStruct.new(url: "https://checkout.stripe.com/test")
482+
483+
mock_payment_processor.expect(:checkout, mock_checkout_session) do |args|
484+
metadata = args[:payment_intent_data][:metadata]
485+
486+
# Verify all pack configuration is included
487+
assert_equal "credit_pack", metadata[:purchase_type]
488+
assert_equal :enterprise, metadata[:pack_name]
489+
assert_equal 10_000, metadata[:credits]
490+
assert_equal 2_000, metadata[:bonus_credits]
491+
assert_equal 19900, metadata[:price_cents]
492+
assert_equal "USD", metadata[:price_currency]
493+
494+
# Also verify session-level metadata
495+
assert_equal metadata, args[:metadata], "Session metadata should match payment_intent_data metadata"
496+
497+
true
498+
end
499+
500+
user.stub(:payment_processor, mock_payment_processor) do
501+
pack.create_checkout_session(user)
502+
end
503+
504+
mock_payment_processor.verify
505+
end
506+
507+
test "create_checkout_session raises when user has no payment processor" do
508+
pack = UsageCredits::CreditPack.new(:test)
509+
pack.gives(100)
510+
pack.costs(10.dollars)
511+
512+
user_without_payment = Object.new
513+
514+
error = assert_raises(ArgumentError) do
515+
pack.create_checkout_session(user_without_payment)
516+
end
517+
518+
assert_includes error.message, "must have a payment processor"
519+
end
520+
521+
test "create_checkout_session includes product name and description" do
522+
pack = UsageCredits::CreditPack.new(:starter)
523+
pack.gives(1000)
524+
pack.costs(49.dollars)
525+
526+
user = users(:rich_user)
527+
528+
mock_payment_processor = Minitest::Mock.new
529+
mock_checkout_session = OpenStruct.new(url: "https://checkout.stripe.com/test")
530+
531+
mock_payment_processor.expect(:checkout, mock_checkout_session) do |args|
532+
product_data = args[:line_items].first[:price_data][:product_data]
533+
534+
assert_equal "Starter pack", product_data[:name]
535+
assert_equal "1000 credits", product_data[:description]
536+
537+
true
538+
end
539+
540+
user.stub(:payment_processor, mock_payment_processor) do
541+
pack.create_checkout_session(user)
542+
end
543+
544+
mock_payment_processor.verify
545+
end
399546
end

test/models/usage_credits/credit_subscription_plan_test.rb

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -532,4 +532,148 @@ class UsageCredits::CreditSubscriptionPlanTest < ActiveSupport::TestCase
532532

533533
assert_kind_of ActiveSupport::Duration, parsed
534534
end
535+
536+
# ========================================
537+
# STRIPE CHECKOUT SESSION CREATION
538+
# ========================================
539+
540+
test "create_stripe_checkout_session passes subscription_data metadata" do
541+
plan = UsageCredits::CreditSubscriptionPlan.new(:premium)
542+
plan.gives(1000).every(:month)
543+
plan.signup_bonus(200)
544+
plan.trial_includes(50)
545+
plan.stripe_price("price_123")
546+
547+
user = users(:rich_user)
548+
549+
# Mock the payment processor to verify the arguments passed
550+
mock_payment_processor = Minitest::Mock.new
551+
mock_checkout_session = OpenStruct.new(url: "https://checkout.stripe.com/test")
552+
553+
# Expect checkout to be called with subscription_data but NOT payment_intent_data
554+
mock_payment_processor.expect(:checkout, mock_checkout_session) do |args|
555+
assert_equal "subscription", args[:mode]
556+
assert_equal "price_123", args[:line_items].first[:price]
557+
assert_equal 1, args[:line_items].first[:quantity]
558+
assert args[:subscription_data].present?, "subscription_data should be present"
559+
assert args[:subscription_data][:metadata].present?, "subscription_data metadata should be present"
560+
561+
# Verify metadata contains all required fields
562+
metadata = args[:subscription_data][:metadata]
563+
assert_equal "credit_subscription", metadata[:purchase_type]
564+
assert_equal :premium, metadata[:subscription_name]
565+
assert_equal 1000, metadata[:credits_per_period]
566+
assert_equal 200, metadata[:signup_bonus_credits]
567+
assert_equal 50, metadata[:trial_credits]
568+
569+
true
570+
end
571+
572+
user.stub(:payment_processor, mock_payment_processor) do
573+
plan.create_stripe_checkout_session(user, "price_123", "/success", "/cancel")
574+
end
575+
576+
mock_payment_processor.verify
577+
end
578+
579+
test "create_stripe_checkout_session does not pass payment_intent_data for subscription mode" do
580+
plan = UsageCredits::CreditSubscriptionPlan.new(:basic)
581+
plan.gives(500).every(:month)
582+
plan.stripe_price("price_456")
583+
584+
user = users(:rich_user)
585+
586+
# Mock the payment processor to verify payment_intent_data is NOT passed
587+
mock_payment_processor = Minitest::Mock.new
588+
mock_checkout_session = OpenStruct.new(url: "https://checkout.stripe.com/test")
589+
590+
# Verify that payment_intent_data is NOT in the arguments
591+
mock_payment_processor.expect(:checkout, mock_checkout_session) do |args|
592+
# According to Stripe API, payment_intent_data is only allowed for mode: "payment"
593+
# For mode: "subscription", it should not be present
594+
refute args.key?(:payment_intent_data), "payment_intent_data should not be present for subscription mode checkout sessions"
595+
596+
# But subscription_data should be present
597+
assert args.key?(:subscription_data), "subscription_data should be present for subscription mode"
598+
599+
true
600+
end
601+
602+
user.stub(:payment_processor, mock_payment_processor) do
603+
plan.create_stripe_checkout_session(user, "price_456", "/success", "/cancel")
604+
end
605+
606+
mock_payment_processor.verify
607+
end
608+
609+
test "create_checkout_session delegates to create_stripe_checkout_session for stripe processor" do
610+
plan = UsageCredits::CreditSubscriptionPlan.new(:test)
611+
plan.gives(500).every(:month)
612+
plan.stripe_price("price_789")
613+
614+
user = users(:rich_user)
615+
616+
mock_payment_processor = Minitest::Mock.new
617+
mock_checkout_session = OpenStruct.new(url: "https://checkout.stripe.com/test")
618+
619+
mock_payment_processor.expect(:checkout, mock_checkout_session) do |_args|
620+
true
621+
end
622+
623+
user.stub(:payment_processor, mock_payment_processor) do
624+
result = plan.create_checkout_session(
625+
user,
626+
success_url: "/success",
627+
cancel_url: "/cancel",
628+
processor: :stripe
629+
)
630+
631+
assert_equal "https://checkout.stripe.com/test", result.url
632+
end
633+
634+
mock_payment_processor.verify
635+
end
636+
637+
test "create_checkout_session includes all plan configuration in metadata" do
638+
plan = UsageCredits::CreditSubscriptionPlan.new(:enterprise)
639+
plan.gives(10_000).every(:month)
640+
plan.signup_bonus(1_000)
641+
plan.trial_includes(500)
642+
plan.unused_credits(:rollover)
643+
plan.expire_after(30.days)
644+
plan.meta(tier: "enterprise", max_users: 100)
645+
plan.stripe_price("price_enterprise")
646+
647+
user = users(:rich_user)
648+
649+
mock_payment_processor = Minitest::Mock.new
650+
mock_checkout_session = OpenStruct.new(url: "https://checkout.stripe.com/test")
651+
652+
mock_payment_processor.expect(:checkout, mock_checkout_session) do |args|
653+
metadata = args[:subscription_data][:metadata]
654+
655+
# Verify all configuration is included
656+
assert_equal "credit_subscription", metadata[:purchase_type]
657+
assert_equal :enterprise, metadata[:subscription_name]
658+
assert_equal 10_000, metadata[:credits_per_period]
659+
assert_equal 1_000, metadata[:signup_bonus_credits]
660+
assert_equal 500, metadata[:trial_credits]
661+
assert_equal true, metadata[:rollover_enabled]
662+
assert_equal true, metadata[:expire_credits_on_cancel]
663+
assert_equal 30.days.to_i, metadata[:credit_expiration_period]
664+
assert_equal({ tier: "enterprise", max_users: 100 }, metadata[:metadata])
665+
666+
true
667+
end
668+
669+
user.stub(:payment_processor, mock_payment_processor) do
670+
plan.create_checkout_session(
671+
user,
672+
success_url: "/success",
673+
cancel_url: "/cancel"
674+
)
675+
end
676+
677+
mock_payment_processor.verify
678+
end
535679
end

0 commit comments

Comments
 (0)