Skip to content

Add decimal_string coercion for v1 and v2 API fields#1829

Merged
jar-stripe merged 5 commits intomasterfrom
jar/decimal-tier2-ruby
Mar 24, 2026
Merged

Add decimal_string coercion for v1 and v2 API fields#1829
jar-stripe merged 5 commits intomasterfrom
jar/decimal-tier2-ruby

Conversation

@jar-stripe
Copy link
Contributor

@jar-stripe jar-stripe commented Mar 20, 2026

Why?

The Stripe API uses decimal_string fields — high-precision decimal values that travel as JSON strings on the wire (e.g. "9.99") to avoid floating-point loss. This PR adds bidirectional coercion so these fields are transparent to SDK users: pass a BigDecimal in, get a BigDecimal back.

Coercion fires for both v1 and v2 resources. The codegen-emitted field_encodings metadata drives which fields get coerced; resources without decimal fields are unaffected. This matches other SDK behavior (Java, Go, .NET).

What?

Runtime (hand-written):

  • lib/stripe/request_params.rb: Adds :decimal_string case to coerce_value and a coerce_decimal_string private method. Handles BigDecimal, String, Integer, Float, and Array inputs; uses to_s("F") to produce plain decimal notation (not scientific).
  • lib/stripe/stripe_object.rb: Adds self.field_encodings class method and response-side coercion in update_attributes — when field_encodings[field] == :decimal_string, converts string API values to BigDecimal.
  • stripe.gemspec: Adds bigdecimal as an explicit runtime dependency (demoted from default gem in Ruby 3.4).
  • Tests in test/stripe/request_params_test.rb and test/stripe/stripe_object_decimal_test.rb.

Generated:

  • All resources with decimal_string fields (v1 and v2) now emit a field_encodings class method listing those fields.
  • primitiveDecimalType changed from String to BigDecimal, so typed params and response types use BigDecimal instead of String.

Review consideration — bigdecimal gemspec dependency:
This is stripe-ruby's first runtime dependency. bigdecimal was a default gem through Ruby 3.3 but was demoted starting in Ruby 3.4.0, so without this declaration users on Ruby 3.4+ would get a LoadError. The gem ships with every supported Ruby version we target (2.7 through JRuby 9.4 and TruffleRuby 25.0), so this is a declaration rather than a new install.

See Also

Changelog

  • ⚠️ Breaking change: All decimal_string fields changed type from String to BigDecimal in both request params and response objects. Code that reads or writes these fields as String will need to use BigDecimal instead. Affected fields across v1 and v2 APIs:
    • Checkout::Session: fx_rate
    • Climate::Order: metric_tons; Climate::Product: metric_tons_available
    • CreditNoteLineItem: unit_amount_decimal
    • InvoiceItem: quantity_decimal, unit_amount_decimal
    • InvoiceLineItem: quantity_decimal, unit_amount_decimal
    • Issuing::Authorization / Issuing::Transaction (and TestHelpers): quantity_decimal, unit_cost_decimal, gross_amount_decimal, local_amount_decimal, national_amount_decimal
    • Plan: amount_decimal, flat_amount_decimal, unit_amount_decimal
    • Price: unit_amount_decimal, flat_amount_decimal (including currency_options and tiers)
    • V2::Core::Account / V2::Core::AccountPerson: percent_ownership
    • Request params on Invoice, Product, Quote, Subscription, SubscriptionItem, SubscriptionSchedule, PaymentLink: unit_amount_decimal, flat_amount_decimal, quantity_decimal (where applicable)

jar-stripe and others added 2 commits March 20, 2026 14:27
bigdecimal was demoted from a default gem to a bundled gem in Ruby
3.4.0. Without declaring it, users on Ruby 3.4.0+ see a deprecation
warning and future Ruby versions may raise LoadError entirely.

This is stripe-ruby's first runtime dependency. It's required because
decimal_string field coercion (added in this branch) loads BigDecimal
in stripe_object.rb for all response deserialization.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
…zation

Request-side (RequestParams):
- Add :decimal_string case to coerce_value
- Add coerce_decimal_string private class method: BigDecimal → to_s("F"),
  String passthrough, Integer/Float → to_s, Array recursion

Response-side (StripeObject):
- Add self.field_encodings class method defaulting to {}
- In update_attributes, coerce string values to BigDecimal when
  field_encodings declares :decimal_string for the field

Both files: require "bigdecimal"

Tests:
- request_params_test.rb: 8 new tests for :decimal_string encoding
- stripe_object_decimal_test.rb: 8 new tests for response coercion
  and non-regression

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
Copilot AI review requested due to automatic review settings March 20, 2026 23:09
@jar-stripe jar-stripe requested a review from a team as a code owner March 20, 2026 23:09
@jar-stripe jar-stripe requested review from xavdid-stripe and removed request for a team March 20, 2026 23:09
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds an explicit runtime dependency on bigdecimal to ensure decimal_string coercion support won’t raise LoadError on Ruby versions where bigdecimal is no longer a default gem (Ruby 3.4+).

Changes:

  • Declares bigdecimal as a runtime dependency in the gemspec.
  • Documents the Ruby 3.4+ rationale directly in stripe.gemspec.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Member

@xavdid-stripe xavdid-stripe left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the new dependency, but there don't appear to be any of the described changes in this PR?

… v2 resources

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Committed-By-Agent: claude
@jar-stripe jar-stripe changed the title Add decimal_string coercion for V2 API fields Add decimal_string coercion for v1 and v2 API fields Mar 21, 2026
@jar-stripe
Copy link
Contributor Author

I see the new dependency, but there don't appear to be any of the described changes in this PR?

This one is weird. Here's the change: https://github.com/stripe/stripe-ruby/pull/1829/files#diff-825edb8c5aafbbacf7da7d78303f6c12e6eaa2a2a5333a0360387a88a0d70bfe. It's actually a standard gem that is no longer "bundled" in future versions of Ruby so we need to declare it in the gemspec.

@jar-stripe jar-stripe enabled auto-merge (squash) March 24, 2026 22:08
@jar-stripe jar-stripe merged commit 31a009a into master Mar 24, 2026
13 checks passed
@jar-stripe jar-stripe deleted the jar/decimal-tier2-ruby branch March 24, 2026 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants