Skip to content

Commit 8706e12

Browse files
Merge upstream and update generated code for v2204 and
2 parents 74693c1 + f52faa4 commit 8706e12

7 files changed

Lines changed: 317 additions & 9 deletions

File tree

.github/workflows/ci.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ jobs:
5454
strategy:
5555
matrix:
5656
# following https://docs.stripe.com/sdks/versioning?lang=ruby#stripe-sdk-language-version-support-policy
57-
ruby-version: [2.6, 2.7, '3.0', 3.1, 3.2, 3.3, 3.4, jruby-9.4.0.0, truffleruby-25.0.0]
57+
ruby-version: [2.7, '3.0', 3.1, 3.2, 3.3, 3.4, jruby-9.4.0.0, truffleruby-25.0.0]
5858
steps:
5959
- uses: extractions/setup-just@v2
6060
- uses: actions/checkout@v3
@@ -72,8 +72,7 @@ jobs:
7272
name: Publish
7373
if: >-
7474
((github.event_name == 'workflow_dispatch') || (github.event_name == 'push')) &&
75-
startsWith(github.ref, 'refs/tags/v') &&
76-
endsWith(github.actor, '-stripe')
75+
startsWith(github.ref, 'refs/tags/v')
7776
needs: [build, test]
7877
runs-on: ubuntu-22.04
7978
permissions:

.rubocop.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ inherit_from: .rubocop_todo.yml
22

33
AllCops:
44
DisplayCopNames: true
5-
TargetRubyVersion: 2.6
5+
TargetRubyVersion: 2.7
66
SuggestExtensions: false
77

88
Layout/CaseIndentation:

CODEGEN_VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
273184f052dd3c191b1993098365d0c2437d2cb4
1+
a88a25f98754c8d1414ef03c9d8fc76b915fc181

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,9 @@ gem build stripe.gemspec
4040

4141
### Requirements
4242

43-
Per our [Language Version Support Policy](https://docs.stripe.com/sdks/versioning?lang=ruby#stripe-sdk-language-version-support-policy), we currently support **Ruby 2.6+**.
43+
Per our [Language Version Support Policy](https://docs.stripe.com/sdks/versioning?lang=ruby#stripe-sdk-language-version-support-policy), we currently support **Ruby 2.7+**.
4444

45-
Support for Ruby 2.6 and 2.7 is deprecated and will be removed in upcoming major versions. Read more and see the full schedule in the docs: https://docs.stripe.com/sdks/versioning?lang=ruby#stripe-sdk-language-version-support-policy
45+
Support for Ruby 2.7 is deprecated and will be removed in upcoming major versions. Read more and see the full schedule in the docs: https://docs.stripe.com/sdks/versioning?lang=ruby#stripe-sdk-language-version-support-policy
4646

4747
### Bundler
4848

lib/stripe/request_params.rb

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ def attr_accessor(*names)
3131
end
3232

3333
def to_h
34+
encodings = self.class.field_encodings
3435
instance_variables.each_with_object({}) do |var, hash|
3536
# _explicitly_set_keys is set as an instance variable.
3637
# Ignore the var if it is _explicitly_set_keys itself.
@@ -50,6 +51,76 @@ def to_h
5051
else
5152
value
5253
end
54+
55+
# Coerce fields based on encoding schema (int64_string, nested objects, arrays)
56+
encoding = encodings[key]
57+
hash[key] = self.class.coerce_value(hash[key], encoding) if encoding
58+
end
59+
end
60+
61+
def self.field_encodings
62+
@field_encodings ||= {}
63+
end
64+
65+
# Recursively coerce a value based on its field encoding schema.
66+
# Handles :int64_string leaves, { kind: :object, fields: ... } nesting,
67+
# and { kind: :array, element: ... } for arrays.
68+
def self.coerce_value(value, encoding)
69+
return value if value.nil?
70+
71+
case encoding
72+
when :int64_string
73+
coerce_int64_string(value)
74+
when Hash
75+
coerce_composite(value, encoding)
76+
else
77+
value
78+
end
79+
end
80+
81+
private_class_method def self.coerce_int64_string(value)
82+
case value
83+
when Integer then value.to_s
84+
when Array then value.map { |v| v.is_a?(Integer) ? v.to_s : v }
85+
else value
86+
end
87+
end
88+
89+
private_class_method def self.coerce_composite(value, encoding)
90+
case encoding[:kind]
91+
when :object
92+
coerce_object(value, encoding[:fields] || {})
93+
when :array
94+
return value unless value.is_a?(Array)
95+
96+
value.map { |v| coerce_value(v, encoding[:element]) }
97+
else
98+
value
99+
end
100+
end
101+
102+
private_class_method def self.coerce_object(value, fields_schema)
103+
return value unless value.is_a?(Hash)
104+
105+
value.each_with_object({}) do |(k, v), result|
106+
field_encoding = fields_schema[k.to_sym]
107+
result[k] = field_encoding ? coerce_value(v, field_encoding) : v
108+
end
109+
end
110+
111+
# Coerce a plain Hash using this class's field_encodings.
112+
# Called from generated service methods when params is a Hash
113+
# (not a RequestParams instance) to ensure int64_string fields
114+
# are serialized as strings on the wire.
115+
def self.coerce_params(params)
116+
return params unless params.is_a?(Hash)
117+
118+
encodings = field_encodings
119+
return params if encodings.empty?
120+
121+
params.each_with_object({}) do |(k, v), result|
122+
encoding = encodings[k.to_sym]
123+
result[k] = encoding ? coerce_value(v, encoding) : v
53124
end
54125
end
55126
end

stripe.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ require "stripe/version"
77
Gem::Specification.new do |s|
88
s.name = "stripe"
99
s.version = Stripe::VERSION
10-
s.required_ruby_version = ">= 2.6.0"
10+
s.required_ruby_version = ">= 2.7.0"
1111
s.summary = "Ruby bindings for the Stripe API"
1212
s.description = "Stripe is the easiest way to accept payments online. " \
1313
"See https://stripe.com for details."

test/stripe/request_params_test.rb

Lines changed: 239 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,47 @@ def initialize(games: nil)
2121
end
2222
end
2323

24+
# Used in ".coerce_params" context
25+
class Int64Params < Stripe::RequestParams
26+
attr_accessor :amount, :name, :items
27+
28+
def initialize(amount: nil, name: nil, items: nil)
29+
@amount = amount
30+
@name = name
31+
@items = items
32+
end
33+
34+
def self.field_encodings
35+
{
36+
amount: :int64_string,
37+
items: { kind: :array, element: { kind: :object, fields: { qty: :int64_string } } },
38+
}
39+
end
40+
end
41+
42+
# Used in ".coerce_params" context
43+
class PlainParams < Stripe::RequestParams
44+
attr_accessor :foo
45+
46+
def initialize(foo: nil)
47+
@foo = foo
48+
end
49+
end
50+
51+
# Used in "#to_h with field_encodings" context
52+
class AmountParams < Stripe::RequestParams
53+
attr_accessor :amount, :label
54+
55+
def initialize(amount: nil, label: nil)
56+
@amount = amount
57+
@label = label
58+
end
59+
60+
def self.field_encodings
61+
{ amount: :int64_string }
62+
end
63+
end
64+
2465
context "#to_h" do
2566
should "convert to hash" do
2667
params = FooCreateParams.new(
@@ -261,7 +302,204 @@ def initialize(games: nil)
261302
end
262303
end
263304

264-
context "RequestParams explicit key tracking" do
305+
context ".coerce_value" do
306+
should "return nil as-is" do
307+
assert_nil Stripe::RequestParams.coerce_value(nil, :int64_string)
308+
assert_nil Stripe::RequestParams.coerce_value(nil, { kind: :object, fields: {} })
309+
end
310+
311+
should "convert Integer to String for int64_string encoding" do
312+
assert_equal "42", Stripe::RequestParams.coerce_value(42, :int64_string)
313+
assert_equal "0", Stripe::RequestParams.coerce_value(0, :int64_string)
314+
assert_equal "-7", Stripe::RequestParams.coerce_value(-7, :int64_string)
315+
end
316+
317+
should "pass through String for int64_string encoding" do
318+
assert_equal "42", Stripe::RequestParams.coerce_value("42", :int64_string)
319+
assert_equal "hello", Stripe::RequestParams.coerce_value("hello", :int64_string)
320+
end
321+
322+
should "convert Array of Integers for int64_string encoding" do
323+
assert_equal %w[1 2 3], Stripe::RequestParams.coerce_value([1, 2, 3], :int64_string)
324+
end
325+
326+
should "handle mixed Array for int64_string encoding" do
327+
assert_equal %w[1 already 3], Stripe::RequestParams.coerce_value([1, "already", 3], :int64_string)
328+
end
329+
330+
should "return value as-is for unknown encoding symbol" do
331+
assert_equal 42, Stripe::RequestParams.coerce_value(42, :unknown_encoding)
332+
assert_equal "hello", Stripe::RequestParams.coerce_value("hello", :something_else)
333+
end
334+
335+
should "coerce object fields recursively" do
336+
encoding = {
337+
kind: :object,
338+
fields: { amount: :int64_string, name: nil },
339+
}
340+
input = { amount: 100, name: "test", extra: "untouched" }
341+
expected = { amount: "100", name: "test", extra: "untouched" }
342+
assert_equal expected, Stripe::RequestParams.coerce_value(input, encoding)
343+
end
344+
345+
should "coerce object fields with string keys" do
346+
encoding = {
347+
kind: :object,
348+
fields: { amount: :int64_string, name: nil },
349+
}
350+
input = { "amount" => 100, "name" => "test", "extra" => "untouched" }
351+
expected = { "amount" => "100", "name" => "test", "extra" => "untouched" }
352+
assert_equal expected, Stripe::RequestParams.coerce_value(input, encoding)
353+
end
354+
355+
should "coerce deeply nested objects with string keys" do
356+
encoding = {
357+
kind: :object,
358+
fields: {
359+
inner: { kind: :object, fields: { val: :int64_string } },
360+
},
361+
}
362+
input = { "inner" => { "val" => 99, "other" => "keep" }, "top" => "also keep" }
363+
expected = { "inner" => { "val" => "99", "other" => "keep" }, "top" => "also keep" }
364+
assert_equal expected, Stripe::RequestParams.coerce_value(input, encoding)
365+
end
366+
367+
should "coerce array of objects with string keys" do
368+
encoding = {
369+
kind: :array,
370+
element: { kind: :object, fields: { id: :int64_string } },
371+
}
372+
input = [{ "id" => 1, "name" => "a" }, { "id" => 2, "name" => "b" }]
373+
expected = [{ "id" => "1", "name" => "a" }, { "id" => "2", "name" => "b" }]
374+
assert_equal expected, Stripe::RequestParams.coerce_value(input, encoding)
375+
end
376+
377+
should "return non-Hash as-is for object encoding" do
378+
encoding = { kind: :object, fields: { amount: :int64_string } }
379+
assert_equal "not a hash", Stripe::RequestParams.coerce_value("not a hash", encoding)
380+
assert_equal 42, Stripe::RequestParams.coerce_value(42, encoding)
381+
end
382+
383+
should "coerce array elements" do
384+
encoding = { kind: :array, element: :int64_string }
385+
assert_equal %w[1 2 3], Stripe::RequestParams.coerce_value([1, 2, 3], encoding)
386+
end
387+
388+
should "return non-Array as-is for array encoding" do
389+
encoding = { kind: :array, element: :int64_string }
390+
assert_equal "not an array", Stripe::RequestParams.coerce_value("not an array", encoding)
391+
end
392+
393+
should "coerce array of objects" do
394+
encoding = {
395+
kind: :array,
396+
element: { kind: :object, fields: { id: :int64_string } },
397+
}
398+
input = [{ id: 1, name: "a" }, { id: 2, name: "b" }]
399+
expected = [{ id: "1", name: "a" }, { id: "2", name: "b" }]
400+
assert_equal expected, Stripe::RequestParams.coerce_value(input, encoding)
401+
end
402+
403+
should "coerce deeply nested object-in-object" do
404+
encoding = {
405+
kind: :object,
406+
fields: {
407+
inner: { kind: :object, fields: { val: :int64_string } },
408+
},
409+
}
410+
input = { inner: { val: 99, other: "keep" }, top: "also keep" }
411+
expected = { inner: { val: "99", other: "keep" }, top: "also keep" }
412+
assert_equal expected, Stripe::RequestParams.coerce_value(input, encoding)
413+
end
414+
415+
should "handle object encoding with missing fields key" do
416+
encoding = { kind: :object }
417+
input = { amount: 100 }
418+
assert_equal({ amount: 100 }, Stripe::RequestParams.coerce_value(input, encoding))
419+
end
420+
421+
should "return value as-is for unknown composite kind" do
422+
encoding = { kind: :unknown }
423+
assert_equal 42, Stripe::RequestParams.coerce_value(42, encoding)
424+
end
425+
end
426+
427+
context ".coerce_params" do
428+
should "return non-Hash input as-is" do
429+
assert_nil Int64Params.coerce_params(nil)
430+
assert_equal "string", Int64Params.coerce_params("string")
431+
assert_equal 42, Int64Params.coerce_params(42)
432+
end
433+
434+
should "return Hash as-is when no encodings are defined" do
435+
input = { foo: 123, bar: "hello" }
436+
assert_equal input, PlainParams.coerce_params(input)
437+
end
438+
439+
should "coerce fields with encodings and pass through others" do
440+
input = { amount: 500, name: "test" }
441+
expected = { amount: "500", name: "test" }
442+
assert_equal expected, Int64Params.coerce_params(input)
443+
end
444+
445+
should "coerce nested array-of-objects encoding" do
446+
input = {
447+
amount: 42,
448+
name: "order",
449+
items: [{ qty: 10, label: "a" }, { qty: 20, label: "b" }],
450+
}
451+
expected = {
452+
amount: "42",
453+
name: "order",
454+
items: [{ qty: "10", label: "a" }, { qty: "20", label: "b" }],
455+
}
456+
assert_equal expected, Int64Params.coerce_params(input)
457+
end
458+
459+
should "handle nil values in encoded fields" do
460+
input = { amount: nil, name: "test" }
461+
expected = { amount: nil, name: "test" }
462+
assert_equal expected, Int64Params.coerce_params(input)
463+
end
464+
465+
should "coerce fields with string keys" do
466+
input = { "amount" => 500, "name" => "test" }
467+
expected = { "amount" => "500", "name" => "test" }
468+
assert_equal expected, Int64Params.coerce_params(input)
469+
end
470+
471+
should "coerce nested array-of-objects with string keys" do
472+
input = {
473+
"amount" => 42,
474+
"name" => "order",
475+
"items" => [{ "qty" => 10, "label" => "a" }, { "qty" => 20, "label" => "b" }],
476+
}
477+
expected = {
478+
"amount" => "42",
479+
"name" => "order",
480+
"items" => [{ "qty" => "10", "label" => "a" }, { "qty" => "20", "label" => "b" }],
481+
}
482+
assert_equal expected, Int64Params.coerce_params(input)
483+
end
484+
end
485+
486+
context "#to_h with field_encodings" do
487+
should "coerce int64_string fields when serializing via to_h" do
488+
params = AmountParams.new(amount: 9_999_999_999, label: "big")
489+
result = params.to_h
490+
assert_equal "9999999999", result[:amount]
491+
assert_equal "big", result[:label]
492+
end
493+
494+
should "leave non-integer amount alone in to_h" do
495+
params = AmountParams.new(amount: "already_string", label: "ok")
496+
result = params.to_h
497+
assert_equal "already_string", result[:amount]
498+
assert_equal "ok", result[:label]
499+
end
500+
end
501+
502+
context "V2 RequestParams explicit key tracking" do
265503
should "only serialize explicitly set fields for V2 classes" do
266504
params = Stripe::V2::Billing::MeterEventCreateParams.new(
267505
event_name: "my_event",

0 commit comments

Comments
 (0)