-
Notifications
You must be signed in to change notification settings - Fork 687
Migration guide for v19
This version uses API version 2026-03-25.dahlia. If the format of this API version looks new to you, see our new API release process.
Please review our API changelog for 2026-03-25.dahlia to understand all the breaking changes to the Stripe API, the reasons behind them and potential alternatives.
The Ruby SDK specific changelog for v19 has the corresponding changes in the SDKs as well as SDK specific breaking changes.
Ruby 2.6 is no longer supported. The gem now requires Ruby >= 2.7.0.
Action required: Upgrade to Ruby 2.7 or later before updating to the latest SDK version. For more information, see our language version support policy.
Note that this is the last major release that supports Ruby 2.7. Please make plans to update to at least 2.8 (and ideally Ruby 3.3 or higher) before September 2026.
Previously, passing a V2 event notification payload to Stripe::Webhook.construct_event (or a V1 event payload to StripeClient#parse_event_notification) would silently succeed and return a malformed object. Both methods now raise ArgumentError with a message pointing you to the correct method.
Before (silently broken):
# V2 payload accidentally passed to V1 method — returned garbage, no error
event = Stripe::Webhook.construct_event(v2_payload, sig_header, secret)After (loud error with guidance):
# Now raises ArgumentError
event = Stripe::Webhook.construct_event(v2_payload, sig_header, secret)The fix — use the right method for each event version:
# V1 events (object: "event")
event = Stripe::Webhook.construct_event(payload, sig_header, secret)
# V2 event notifications (object: "v2.core.event")
event = client.parse_event_notification(payload, sig_header, secret)All API fields marked as decimal_string now use BigDecimal instead of String in both request parameters and response objects. The SDK handles bidirectional coercion:
-
Responses: String values from the API (e.g.,
"9.99") are automatically converted toBigDecimal. -
Requests:
BigDecimal,Integer,Float, andStringvalues are all accepted and converted to plain decimal notation strings on the wire.
Affected fields (across V1 and V2 resources):
| Resource | Fields |
|---|---|
Checkout::Session |
fx_rate (on currency_conversion) |
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
|
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
|
Before:
# Reading — was String
session = Stripe::Checkout::Session.retrieve("cs_test_xxx")
rate = session.currency_conversion.fx_rate
puts rate.class # => String
puts rate # => "1.23"
# Writing — passed String
Stripe::Climate::Order.create(
product: "climsku_xxx",
metric_tons: "5.5"
)After:
# Reading — now BigDecimal
session = Stripe::Checkout::Session.retrieve("cs_test_xxx")
rate = session.currency_conversion.fx_rate
puts rate.class # => BigDecimal
puts rate # => 0.123e1 (BigDecimal display)
puts rate.to_s("F") # => "1.23" (plain decimal string)
# Writing — BigDecimal is preferred, but String/Integer/Float still work
Stripe::Climate::Order.create(
product: "climsku_xxx",
metric_tons: BigDecimal("5.5")
)
# These also work (coerced automatically):
metric_tons: "5.5" # String → still accepted
metric_tons: 5.5 # Float → coerced (but may lose precision)
metric_tons: 5 # Integer → coercedMigration pattern:
# String comparison — update to use BigDecimal
# Before:
if line_item.unit_amount_decimal == "0.50"
# After:
if line_item.unit_amount_decimal == BigDecimal("0.50")
# String formatting — use to_s("F") for display
# Before:
puts "Rate: #{rate}" # "1.23"
# After:
puts "Rate: #{rate.to_s("F")}" # "1.23"
# Arithmetic — works naturally with BigDecimal
total = line_item.unit_amount_decimal * line_item.quantity_decimal
# Returns BigDecimal, no precision lossThe bigdecimal gem is now an explicit runtime dependency in stripe.gemspec.
Action required: If you previously vendored or pinned bigdecimal, ensure there are no conflicts.
The Stripe::Util.objects_to_ids method now accepts an optional serialize_empty: keyword argument. When true, nil values in hashes are preserved instead of being stripped.
Before:
Stripe::Util.objects_to_ids(params)After:
# Existing behavior preserved (strip nils):
Stripe::Util.objects_to_ids(params)
# Preserve nils for V2 JSON bodies:
Stripe::Util.objects_to_ids(params, serialize_empty: true)V2 resources previously generated a separate, per-field Amount class for every monetary amount property (e.g., OutboundPayment.Amount, AnnualRevenue.Amount). These duplicates have been replaced with a single shared Amount type. The fields (value and currency) are identical — only the type name and import path changed.
Amount fields now use the shared Stripe::V2::Amount class. Sorbet type signatures reflect the change.
# Before — nested class per resource
# amount was typed as Stripe::V2::Core::Account::Identity::BusinessDetails::AnnualRevenue::Amount
# After — shared Amount type
account = client.v2.core.accounts.retrieve("acct_123")
amount = account.identity.business_details.annual_revenue.amount
# amount is now a Stripe::V2::Amount
puts amount.value # Integer
puts amount.currency # String, e.g. "usd"If you reference the type directly in Sorbet signatures:
# Before
sig { params(amt: Stripe::V2::Core::Account::Identity::BusinessDetails::AnnualRevenue::Amount).void }
# After
sig { params(amt: Stripe::V2::Amount).void }