From bb67c39f5b5420e4fed6ccba1ae055a56839652a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serge=20H=C3=A4nni?= Date: Tue, 15 Sep 2026 17:43:50 +0200 Subject: [PATCH 1/4] Add payment service provider models --- .../payment_service_provider/logpay.rb | 19 +++++++++++++ .../payment_service_provider/stripe.rb | 23 ++++++++++++++++ lib/ioki/model/operator/provider.rb | 7 +++++ .../payment_service_provider/logpay.rb | 23 ++++++++++++++++ .../payment_service_provider/stripe.rb | 27 +++++++++++++++++++ lib/ioki/model/passenger/provider.rb | 6 +++++ .../payment_service_provider/logpay.rb | 23 ++++++++++++++++ .../payment_service_provider/stripe.rb | 27 +++++++++++++++++++ lib/ioki/model/platform/provider.rb | 7 +++++ 9 files changed, 162 insertions(+) create mode 100644 lib/ioki/model/operator/payment_service_provider/logpay.rb create mode 100644 lib/ioki/model/operator/payment_service_provider/stripe.rb create mode 100644 lib/ioki/model/passenger/payment_service_provider/logpay.rb create mode 100644 lib/ioki/model/passenger/payment_service_provider/stripe.rb create mode 100644 lib/ioki/model/platform/payment_service_provider/logpay.rb create mode 100644 lib/ioki/model/platform/payment_service_provider/stripe.rb diff --git a/lib/ioki/model/operator/payment_service_provider/logpay.rb b/lib/ioki/model/operator/payment_service_provider/logpay.rb new file mode 100644 index 00000000..68c037bd --- /dev/null +++ b/lib/ioki/model/operator/payment_service_provider/logpay.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Ioki + module Model + module Operator + module PaymentServiceProvider + class Logpay < Base + attribute :type, + on: :read, + type: :string + + attribute :logpay_payment_method_types, + on: :read, + type: :array + end + end + end + end +end diff --git a/lib/ioki/model/operator/payment_service_provider/stripe.rb b/lib/ioki/model/operator/payment_service_provider/stripe.rb new file mode 100644 index 00000000..d1d48556 --- /dev/null +++ b/lib/ioki/model/operator/payment_service_provider/stripe.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Ioki + module Model + module Operator + module PaymentServiceProvider + class Stripe < Base + attribute :type, + on: :read, + type: :string + + attribute :google_pay_supported, + on: :read, + type: :boolean + + attribute :stripe_payment_method_types, + on: :read, + type: :array + end + end + end + end +end diff --git a/lib/ioki/model/operator/provider.rb b/lib/ioki/model/operator/provider.rb index 80202b70..c1096e79 100644 --- a/lib/ioki/model/operator/provider.rb +++ b/lib/ioki/model/operator/provider.rb @@ -66,6 +66,13 @@ class Provider < Base on: :read, type: :string + attribute :payment_service_provider, + on: :read, + type: [ + Ioki::Model::Operator::PaymentServiceProvider::Stripe, + Ioki::Model::Operator::PaymentServiceProvider::Logpay + ] + attribute :products, on: :read, type: :array, diff --git a/lib/ioki/model/passenger/payment_service_provider/logpay.rb b/lib/ioki/model/passenger/payment_service_provider/logpay.rb new file mode 100644 index 00000000..ffa81092 --- /dev/null +++ b/lib/ioki/model/passenger/payment_service_provider/logpay.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Ioki + module Model + module Passenger + module PaymentServiceProvider + class Logpay < Base + attribute :type, + on: :read, + type: :string + + attribute :logpay_payment_method_types, + on: :read, + type: :array + + attribute :use_legacy_paypal_flow, + on: :read, + type: :boolean + end + end + end + end +end diff --git a/lib/ioki/model/passenger/payment_service_provider/stripe.rb b/lib/ioki/model/passenger/payment_service_provider/stripe.rb new file mode 100644 index 00000000..fd4519ba --- /dev/null +++ b/lib/ioki/model/passenger/payment_service_provider/stripe.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Ioki + module Model + module Passenger + module PaymentServiceProvider + class Stripe < Base + attribute :type, + on: :read, + type: :string + + attribute :google_pay_supported, + on: :read, + type: :boolean + + attribute :stripe_account_id, + on: :read, + type: :string + + attribute :stripe_payment_method_types, + on: :read, + type: :array + end + end + end + end +end diff --git a/lib/ioki/model/passenger/provider.rb b/lib/ioki/model/passenger/provider.rb index ac1d3e9e..773046fa 100644 --- a/lib/ioki/model/passenger/provider.rb +++ b/lib/ioki/model/passenger/provider.rb @@ -20,6 +20,12 @@ class Provider < Base attribute :name, on: :read, type: :string attribute :personal_discount_payment_method_types, on: :read, type: :array attribute :postal_code, on: :read, type: :string + attribute :payment_service_provider, + on: :read, + type: [ + Ioki::Model::Passenger::PaymentServiceProvider::Stripe, + Ioki::Model::Passenger::PaymentServiceProvider::Logpay + ] attribute :psp, on: :read, type: :string attribute :retry_payment_method_types, on: :read, type: :array deprecated_attribute :ride_payment_method_types, on: :read, type: :array diff --git a/lib/ioki/model/platform/payment_service_provider/logpay.rb b/lib/ioki/model/platform/payment_service_provider/logpay.rb new file mode 100644 index 00000000..6d5a952e --- /dev/null +++ b/lib/ioki/model/platform/payment_service_provider/logpay.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Ioki + module Model + module Platform + module PaymentServiceProvider + class Logpay < Base + attribute :type, + on: :read, + type: :string + + attribute :logpay_payment_method_types, + on: :read, + type: :array + + attribute :use_legacy_paypal_flow, + on: :read, + type: :boolean + end + end + end + end +end diff --git a/lib/ioki/model/platform/payment_service_provider/stripe.rb b/lib/ioki/model/platform/payment_service_provider/stripe.rb new file mode 100644 index 00000000..b6f93943 --- /dev/null +++ b/lib/ioki/model/platform/payment_service_provider/stripe.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +module Ioki + module Model + module Platform + module PaymentServiceProvider + class Stripe < Base + attribute :type, + on: :read, + type: :string + + attribute :google_pay_supported, + on: :read, + type: :boolean + + attribute :stripe_account_id, + on: :read, + type: :string + + attribute :stripe_payment_method_types, + on: :read, + type: :array + end + end + end + end +end diff --git a/lib/ioki/model/platform/provider.rb b/lib/ioki/model/platform/provider.rb index 344e5185..d5748e41 100644 --- a/lib/ioki/model/platform/provider.rb +++ b/lib/ioki/model/platform/provider.rb @@ -76,6 +76,13 @@ class Provider < Base on: :read, type: :string + attribute :payment_service_provider, + on: :read, + type: [ + Ioki::Model::Platform::PaymentServiceProvider::Stripe, + Ioki::Model::Platform::PaymentServiceProvider::Logpay + ] + attribute :privacy_policy_url, on: :read, type: :string From 0ed1bae686677642ad4e64930904ceb2961ada0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serge=20H=C3=A4nni?= Date: Tue, 15 Sep 2026 17:57:51 +0200 Subject: [PATCH 2/4] Add support for nested models in the platform API Also adapt the operator API logic to match the others. Considering only files is safer than skipping directories. --- lib/ioki/model.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/ioki/model.rb b/lib/ioki/model.rb index 94bcb61e..928807c0 100644 --- a/lib/ioki/model.rb +++ b/lib/ioki/model.rb @@ -7,8 +7,9 @@ require 'ioki/model/platform/base' require 'ioki/model/webhooks/base' -Dir[File.join(__dir__, 'model', 'platform', '*')].reject do |filename| - filename == File.join(__dir__, 'model', 'platform', 'base.rb') +Dir[File.join(__dir__, 'model', 'platform', '**', '*')].reject do |filename| + filename == File.join(__dir__, 'model', 'platform', 'base.rb') || + !File.file?(filename) end.sort.each { |file| require file } Dir[File.join(__dir__, 'model', 'passenger', '**', '*')].reject do |filename| filename == File.join(__dir__, 'model', 'passenger', 'base.rb') || @@ -18,7 +19,8 @@ filename == File.join(__dir__, 'model', 'driver', 'base.rb') end.sort.each { |file| require file } Dir[File.join(__dir__, 'model', 'operator', '**', '*')].reject do |filename| - filename == File.join(__dir__, 'model', 'operator', 'base.rb') || File.directory?(filename) + filename == File.join(__dir__, 'model', 'operator', 'base.rb') || + !File.file?(filename) end.sort.each { |file| require file } Dir[File.join(__dir__, 'model', 'webhooks', '*')].reject do |filename| filename == File.join(__dir__, 'model', 'webhooks', 'base.rb') From 0e353c6ee665d925fb8d0427e4e82e9354048fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serge=20H=C3=A4nni?= Date: Tue, 15 Sep 2026 18:38:49 +0200 Subject: [PATCH 3/4] Add payment method attribute to passenger API --- lib/ioki/model/passenger/payment_method.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/ioki/model/passenger/payment_method.rb b/lib/ioki/model/passenger/payment_method.rb index 9710ff6d..ede1e327 100644 --- a/lib/ioki/model/passenger/payment_method.rb +++ b/lib/ioki/model/passenger/payment_method.rb @@ -9,6 +9,7 @@ class PaymentMethod < Base attribute :created_at, on: :read, type: :date_time attribute :updated_at, on: :read, type: :date_time attribute :payment_method_type, on: :create, type: :string + attribute :requires_paypal_secure_element, on: :read, type: :boolean attribute :attached, on: :read, type: :boolean attribute :summary, on: :read, type: :object, class_name: 'PaymentMethodSummary' From 54f00a4262429f6c23413e79876e1c8bd1eaebc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Serge=20H=C3=A4nni?= Date: Wed, 16 Sep 2026 17:32:40 +0200 Subject: [PATCH 4/4] Fix parsing deeply nested models Only relevant if multiple class names are passed in an array. --- lib/ioki/model/base.rb | 9 +++++++ lib/ioki/model/operator/provider.rb | 9 +++---- lib/ioki/model/passenger/provider.rb | 9 +++---- lib/ioki/model/platform/provider.rb | 9 +++---- spec/ioki/model/base_spec.rb | 35 ++++++++++++++++++++++++++++ 5 files changed, 59 insertions(+), 12 deletions(-) diff --git a/lib/ioki/model/base.rb b/lib/ioki/model/base.rb index b1cdc735..1834556f 100644 --- a/lib/ioki/model/base.rb +++ b/lib/ioki/model/base.rb @@ -257,6 +257,15 @@ def class_name_from_value_type(class_name, value) return class_name_from_value_type if class_name.include?(class_name_from_value_type) + # The above behavior only considers the last part of the type (split by + # `/`). To keep backwards compatibility, the following implementation + # using the whole type (to support deeply nested models) is added as a + # fallback only. + type_string = value_type(value) + class_name_from_value_type = Support.camelize(type_string) + + return class_name_from_value_type if class_name.include?(class_name_from_value_type) + nil end diff --git a/lib/ioki/model/operator/provider.rb b/lib/ioki/model/operator/provider.rb index c1096e79..18f000b5 100644 --- a/lib/ioki/model/operator/provider.rb +++ b/lib/ioki/model/operator/provider.rb @@ -67,10 +67,11 @@ class Provider < Base type: :string attribute :payment_service_provider, - on: :read, - type: [ - Ioki::Model::Operator::PaymentServiceProvider::Stripe, - Ioki::Model::Operator::PaymentServiceProvider::Logpay + on: :read, + type: :object, + class_name: [ + 'PaymentServiceProvider::Stripe', + 'PaymentServiceProvider::Logpay' ] attribute :products, diff --git a/lib/ioki/model/passenger/provider.rb b/lib/ioki/model/passenger/provider.rb index 773046fa..ec8e0219 100644 --- a/lib/ioki/model/passenger/provider.rb +++ b/lib/ioki/model/passenger/provider.rb @@ -21,10 +21,11 @@ class Provider < Base attribute :personal_discount_payment_method_types, on: :read, type: :array attribute :postal_code, on: :read, type: :string attribute :payment_service_provider, - on: :read, - type: [ - Ioki::Model::Passenger::PaymentServiceProvider::Stripe, - Ioki::Model::Passenger::PaymentServiceProvider::Logpay + on: :read, + type: :object, + class_name: [ + 'PaymentServiceProvider::Stripe', + 'PaymentServiceProvider::Logpay' ] attribute :psp, on: :read, type: :string attribute :retry_payment_method_types, on: :read, type: :array diff --git a/lib/ioki/model/platform/provider.rb b/lib/ioki/model/platform/provider.rb index d5748e41..665fec31 100644 --- a/lib/ioki/model/platform/provider.rb +++ b/lib/ioki/model/platform/provider.rb @@ -77,10 +77,11 @@ class Provider < Base type: :string attribute :payment_service_provider, - on: :read, - type: [ - Ioki::Model::Platform::PaymentServiceProvider::Stripe, - Ioki::Model::Platform::PaymentServiceProvider::Logpay + on: :read, + type: :object, + class_name: [ + 'PaymentServiceProvider::Stripe', + 'PaymentServiceProvider::Logpay' ] attribute :privacy_policy_url, diff --git a/spec/ioki/model/base_spec.rb b/spec/ioki/model/base_spec.rb index 9b209e80..0b3b5f67 100644 --- a/spec/ioki/model/base_spec.rb +++ b/spec/ioki/model/base_spec.rb @@ -156,6 +156,41 @@ end end + describe 'for an attribute of type :object with nested class_name' do + let(:example_referenced_class) do + Class.new(Ioki::Model::Base) do + attribute :bar, type: :integer, on: :read + end + end + let(:example_other_class) do + Class.new(Ioki::Model::Base) do + attribute :bar, type: :integer, on: :read + end + end + + let(:example_class) do + Class.new(Ioki::Model::Base) do + attribute :foo, type: :object, class_name: ['Example::ReferencedClass', 'Example::OtherClass'], on: :read + end + end + + before do + stub_const('Ioki::Model::Example::ReferencedClass', example_referenced_class) + stub_const('Ioki::Model::Example::OtherClass', example_other_class) + stub_const('Ioki::Model::ExampleObjectClassName', example_class) + end + + it 'parses the class correctly' do + model = example_class.new({ foo: { type: 'example/referenced_class', bar: 42 } }) + expect(model.foo).to be_a(example_referenced_class) + expect(model.foo.bar).to eq(42) + + model = example_class.new({ foo: { type: 'example/other_class', bar: 42 } }) + expect(model.foo).to be_a(example_other_class) + expect(model.foo.bar).to eq(42) + end + end + describe 'for an attribute of type :array with a class_name' do let(:example_referenced_class) do Class.new(Ioki::Model::Base) do