Conversation
Root Cause: In Pay 10+, the full Stripe charge object (including `status` and `amount_captured`) is stored in the `object` column, not in `data`. The `succeeded?` method was reading from `data["status"]` which returns nil in Pay 10+, causing credits to never be fulfilled. Fix: - Add `charge_object_data` helper that checks `object` column first (Pay 10+), then falls back to `data` column (legacy Pay) - Update `succeeded?` to use this backward-compatible helper - This maintains full compatibility with Pay 8.3, 9.0, 10.0, and 11.x Reference: - Issue: #1 - Pay gem line 32: `object: object.to_hash` stores full Stripe charge - Pay UPGRADE.md: "Pay 9.0 to Pay 10.0: Pay has introduced an `object` column... to save a full copy of the Stripe objects" Tests added: - Pay 10+ `object` column scenarios (succeeded, failed, pending, canceled) - Legacy Pay `data` column scenarios - `object` takes precedence over `data` when both present - Fallback to `data` when `object` is empty or nil - Full integration test replicating exact Issue #1 scenario - Edge cases for amount_captured fallback logic Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
|
You have run out of free Bugbot PR reviews for this billing cycle. This will reset on February 6. To receive reviews on all of your PRs, visit the Cursor dashboard to activate Pro and start your 14-day free trial. |
Pull Request Review: Fix credit pack fulfillment not working with Pay 10+ (Issue #1)Overall Assessmentβ Excellent work! This PR addresses a critical bug with a clean, backward-compatible solution and comprehensive test coverage. Summary:
Code Quality & Best Practicesβ Strengths
π Suggestions for Improvement1. Minor: Optimize conditional logic in
|
| Pay Version | Behavior | Status |
|---|---|---|
| Pay 8.3.x | Uses data column |
β |
| Pay 9.0.x | Uses data column |
β |
| Pay 10.x | Uses object column |
β |
| Pay 11.x | Uses object column |
β |
The migration is idempotent and safe to run multiple times.
Documentation
β Outstanding PR Description
Your PR description is exemplary:
- Clear root cause analysis
- Links to upstream Pay gem changes
- Visual tables for version comparison
- Integration test description
- Backward compatibility matrix
π Suggestion: Update README
Consider adding a note in the README about Pay version compatibility. For example, in the "Requirements" section:
**Requirements**
- An ActiveJob backend (Sidekiq, `solid_queue`, etc.) for subscription credit fulfillment
- [`pay`](https://github.com/pay-rails/pay) gem for Stripe/PayPal/Lemon Squeezy integration
- **Note:** `usage_credits` is compatible with Pay 8.3+, including Pay 10+ which introduced the `object` column for storing full Stripe response objects.Additional Suggestions
1. Consider adding a changelog entry
If you maintain a CHANGELOG.md, this fix deserves an entry:
## [Unreleased]
### Fixed
- Credit pack fulfillment not working with Pay 10+ due to data structure changes (Issue #1)
- Pay 10+ stores charge status in `object` column instead of `data` column
- Maintains full backward compatibility with Pay 8.3+2. Follow-up: Document Pay.sync for immediate fulfillment
Based on the issue comments, consider documenting the Pay.sync pattern for immediate fulfillment after checkout redirect:
# In your checkout success controller
def success
session = Stripe::Checkout::Session.retrieve(params[:session_id])
Pay.sync(session.charge) # Immediately sync charge and trigger fulfillment
redirect_to credits_path, notice: "Credits added!"
endThis could prevent future issues like the one reported by @crobbo.
Final Verdict
β APPROVE - Ready to Merge
This PR demonstrates excellent engineering practices:
- Thorough root cause analysis
- Clean, maintainable solution
- Comprehensive test coverage
- Full backward compatibility
- Outstanding documentation
The suggestions above are all optional improvements. The code is production-ready as-is.
Test Results Confirm Quality:
673 runs, 1516 assertions, 0 failures, 0 errors, 0 skips
Great work on identifying and fixing this critical issue! π
Review conducted by: Claude Code (Sonnet 4.5)
Repository guidelines: Reviewed against project standards
object vs data in Pay::Charge)
Change ActiveRecord::Schema version from 8.0 to 7.0 to ensure compatibility with Rails 7.x in CI. Rails 8.x is forward-compatible with 7.0 schema versions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
In Pay 10+, the Stripe charge object is stored in the
objectcolumn, notdata. Thesucceeded?method is reading fromdata["status"], which returnsnilin Pay 10+, causing credits to never be fulfilled.I had already done some work to migrate
usage_creditsto Pay 10+ (#19) but looks like I missed this.We add a
charge_object_datahelper that checksobjectfirst (Pay 10+), then falls back todata(legacy Pay).Summary
Fixes #1 - Credits not being fulfilled after successful credit pack purchase with Pay 10+.
This PR addresses a critical bug where credit packs were not being fulfilled when using Pay gem version 10.0 or later, even though charges were succeeding in Stripe.
Root Cause Analysis
The Problem
User @onurozer identified the issue in Issue #1:
Pay Gem Architecture Change in Version 10.0
Starting with Pay 10.0 (released late 2024), the gem introduced a new
objectcolumn to store the complete Stripe response object. This is documented in Pay's UPGRADE.md:Where Data Lives in Each Version
statusdata["status"]object["status"]amount_captureddata["amount_captured"]object["amount_captured"]data[...]data[...](via store_accessor)Relevant Pay Gem Code
From
pay/app/models/pay/stripe/charge.rbline 32:The
datacolumn now only storesstore_accessorfields likestripe_invoiceandstripe_receipt_url(lines 8-9), NOT the raw Stripe response.Our Code Before This Fix
From
lib/usage_credits/models/concerns/pay_charge_extension.rb:The Fix
Added a new helper method
charge_object_datathat checksobjectfirst (Pay 10+), then falls back todata(legacy Pay):Files Changed
lib/usage_credits/models/concerns/pay_charge_extension.rbcharge_object_datahelper, updatedsucceeded?to use ittest/models/concerns/pay_charge_extension_test.rbtest/dummy/db/migrate/20250416000000_add_object_to_pay_models.rbtest/dummy/db/schema.rbTests Added
Pay 10+ Object Column Tests
succeeded?returns true when status is in object columnsucceeded?returns false for statusfailedin objectsucceeded?returns false for statuspendingin objectsucceeded?returns false for statuscanceledin objectobjecttakes precedence overdatawhen both have valuesLegacy Pay Data Column Tests
succeeded?falls back todatacolumn whenobjectis emptycharge_object_datareturnsdatawhenobjectis nilEdge Cases
amount_capturedcomparison when status is nilIntegration Test (Exact Issue #1 Scenario)
Backward Compatibility
This fix maintains full backward compatibility with all supported Pay versions:
datacolumn (noobjectcolumn exists) βdatacolumn βobjectcolumn βobjectcolumn βTest Results
Related PRs
This issue was not caught by previous Pay upgrade PRs because they focused on:
None of these addressed the
datavsobjectcolumn data structure change.π€ Generated with Claude Code