From eafa72fb087e95aad697ab78b2b3cad7d97653b0 Mon Sep 17 00:00:00 2001 From: Jhoan Buitrago Date: Mon, 28 Oct 2024 19:39:20 -0500 Subject: [PATCH] Normalize API versions Summary: ------------------------------ This change centralizes version management at the class level, creating a consistent and reusable way to define and access API versions. Unit Tests: ------------------------------ 6155 tests, 80982 assertions, 0 failures, 0 errors, 0 pendings, 0 omissions, 0 notifications 100% passed RuboCop: ------------------------------ 808 files inspected, no offenses detected --- lib/active_merchant/billing.rb | 1 + lib/active_merchant/billing/gateway.rb | 1 + lib/active_merchant/billing/gateways/adyen.rb | 11 +-- .../billing/gateways/braintree_blue.rb | 3 + .../billing/gateways/global_collect.rb | 5 +- lib/active_merchant/versionable.rb | 29 ++++++ test/unit/gateways/adyen_test.rb | 6 ++ test/unit/gateways/braintree_blue_test.rb | 4 + test/unit/gateways/global_collect_test.rb | 22 +++++ test/unit/versionable_test.rb | 89 +++++++++++++++++++ 10 files changed, 165 insertions(+), 6 deletions(-) create mode 100644 lib/active_merchant/versionable.rb create mode 100644 test/unit/versionable_test.rb diff --git a/lib/active_merchant/billing.rb b/lib/active_merchant/billing.rb index 55838882995..998e9c27ddf 100644 --- a/lib/active_merchant/billing.rb +++ b/lib/active_merchant/billing.rb @@ -1,4 +1,5 @@ require 'active_merchant/errors' +require 'active_merchant/versionable' require 'active_merchant/billing/avs_result' require 'active_merchant/billing/cvv_result' diff --git a/lib/active_merchant/billing/gateway.rb b/lib/active_merchant/billing/gateway.rb index 4c8457fa40a..7dd6da50098 100644 --- a/lib/active_merchant/billing/gateway.rb +++ b/lib/active_merchant/billing/gateway.rb @@ -55,6 +55,7 @@ module Billing # :nodoc: class Gateway include PostsData include CreditCardFormatting + include Versionable CREDIT_DEPRECATION_MESSAGE = 'Support for using credit to refund existing transactions is deprecated and will be removed from a future release of ActiveMerchant. Please use the refund method instead.' RECURRING_DEPRECATION_MESSAGE = 'Recurring functionality in ActiveMerchant is deprecated and will be removed in a future version. Please contact the ActiveMerchant maintainers if you have an interest in taking ownership of a separate gem that continues support for it.' diff --git a/lib/active_merchant/billing/gateways/adyen.rb b/lib/active_merchant/billing/gateways/adyen.rb index c06b97c7914..a302e6336a8 100644 --- a/lib/active_merchant/billing/gateways/adyen.rb +++ b/lib/active_merchant/billing/gateways/adyen.rb @@ -17,8 +17,9 @@ class AdyenGateway < Gateway self.homepage_url = 'https://www.adyen.com/' self.display_name = 'Adyen' - PAYMENT_API_VERSION = 'v68' - RECURRING_API_VERSION = 'v68' + version 'v68', :payment_api + version 'v68', :payout_api + version 'v68', :recurring_api STANDARD_ERROR_CODE_MAPPING = { '0' => STANDARD_ERROR_CODE[:processing_error], @@ -870,11 +871,11 @@ def cvv_result_from(response) def endpoint(action) case action when 'disable', 'storeToken' - "Recurring/#{RECURRING_API_VERSION}/#{action}" + "Recurring/#{fetch_version(:recurring_api)}/#{action}" when 'payout' - "Payout/#{PAYMENT_API_VERSION}/#{action}" + "Payout/#{fetch_version(:payout_api)}/#{action}" else - "Payment/#{PAYMENT_API_VERSION}/#{action}" + "Payment/#{fetch_version(:payment_api)}/#{action}" end end diff --git a/lib/active_merchant/billing/gateways/braintree_blue.rb b/lib/active_merchant/billing/gateways/braintree_blue.rb index 0d74be854f1..dc60b596e35 100644 --- a/lib/active_merchant/billing/gateways/braintree_blue.rb +++ b/lib/active_merchant/billing/gateways/braintree_blue.rb @@ -43,6 +43,9 @@ class BraintreeBlueGateway < Gateway self.display_name = 'Braintree (Blue Platform)' + version Braintree::Configuration::API_VERSION + version Braintree::Version::String, :gem + ERROR_CODES = { cannot_refund_if_unsettled: 91506 } diff --git a/lib/active_merchant/billing/gateways/global_collect.rb b/lib/active_merchant/billing/gateways/global_collect.rb index a15c64c46e8..8d317db19a2 100644 --- a/lib/active_merchant/billing/gateways/global_collect.rb +++ b/lib/active_merchant/billing/gateways/global_collect.rb @@ -19,6 +19,9 @@ class GlobalCollectGateway < Gateway self.money_format = :cents self.supported_cardtypes = %i[visa master american_express discover naranja cabal tuya patagonia_365] + version 'v1' + version 'v2', :ogone_direct + def initialize(options = {}) requires!(options, :merchant_id, :api_key_id, :secret_api_key) super @@ -443,7 +446,7 @@ def ogone_direct? end def uri(action, authorization) - version = ogone_direct? ? 'v2' : 'v1' + version = ogone_direct? ? fetch_version(:ogone_direct) : fetch_version uri = "/#{version}/#{@options[:merchant_id]}/" case action when :authorize, :verify diff --git a/lib/active_merchant/versionable.rb b/lib/active_merchant/versionable.rb new file mode 100644 index 00000000000..12f7b759b16 --- /dev/null +++ b/lib/active_merchant/versionable.rb @@ -0,0 +1,29 @@ +module ActiveMerchant + module Versionable + def self.included(base) + if base.respond_to?(:class_attribute) + base.class_attribute :versions, default: {} + base.extend(ClassMethods) + end + end + + module ClassMethods + def inherited(subclass) + super + subclass.versions = {} + end + + def version(version, feature = :default_api) + versions[feature] = version + end + + def fetch_version(feature = :default_api) + versions[feature] + end + end + + def fetch_version(feature = :default_api) + versions[feature] + end + end +end diff --git a/test/unit/gateways/adyen_test.rb b/test/unit/gateways/adyen_test.rb index cd1d92aaf8a..ca61548b8f7 100644 --- a/test/unit/gateways/adyen_test.rb +++ b/test/unit/gateways/adyen_test.rb @@ -144,6 +144,12 @@ def setup # assert response # assert_success response # end + def test_endpoint + assert_equal 'Recurring/v68/disable', @gateway.send(:endpoint, 'disable') + assert_equal 'Recurring/v68/storeToken', @gateway.send(:endpoint, 'storeToken') + assert_equal 'Payout/v68/payout', @gateway.send(:endpoint, 'payout') + assert_equal 'Payment/v68/authorise', @gateway.send(:endpoint, 'authorise') + end def test_supported_card_types assert_equal AdyenGateway.supported_cardtypes, %i[visa master american_express diners_club jcb dankort maestro discover elo naranja cabal unionpay patagonia_365] diff --git a/test/unit/gateways/braintree_blue_test.rb b/test/unit/gateways/braintree_blue_test.rb index 1d74fad7431..9acac32a1b3 100644 --- a/test/unit/gateways/braintree_blue_test.rb +++ b/test/unit/gateways/braintree_blue_test.rb @@ -18,6 +18,10 @@ def teardown $VERBOSE = @old_verbose end + def test_api_version + assert_equal '6', @gateway.fetch_version + end + def test_refund_legacy_method_signature Braintree::TransactionGateway.any_instance.expects(:refund). with('transaction_id', nil). diff --git a/test/unit/gateways/global_collect_test.rb b/test/unit/gateways/global_collect_test.rb index 4fad2a9366f..5301782f237 100644 --- a/test/unit/gateways/global_collect_test.rb +++ b/test/unit/gateways/global_collect_test.rb @@ -49,6 +49,28 @@ def setup ) end + def test_url + url = @gateway.test? ? @gateway.test_url : @gateway.live_url + merchant_id = @gateway.options[:merchant_id] + assert_equal "#{url}/v1/#{merchant_id}/payments", @gateway.send(:url, :authorize, nil) + assert_equal "#{url}/v1/#{merchant_id}/payments", @gateway.send(:url, :verify, nil) + assert_equal "#{url}/v1/#{merchant_id}/payments/0000/approve", @gateway.send(:url, :capture, '0000') + assert_equal "#{url}/v1/#{merchant_id}/payments/0000/refund", @gateway.send(:url, :refund, '0000') + assert_equal "#{url}/v1/#{merchant_id}/payments/0000/cancel", @gateway.send(:url, :void, '0000') + assert_equal "#{url}/v1/#{merchant_id}/payments/0000", @gateway.send(:url, :inquire, '0000') + end + + def test_ogone_url + url = @gateway_direct.test? ? @gateway_direct.ogone_direct_test : @gateway_direct.ogone_direct_live + merchant_id = @gateway_direct.options[:merchant_id] + assert_equal "#{url}/v2/#{merchant_id}/payments", @gateway_direct.send(:url, :authorize, nil) + assert_equal "#{url}/v2/#{merchant_id}/payments", @gateway_direct.send(:url, :verify, nil) + assert_equal "#{url}/v2/#{merchant_id}/payments/0000/capture", @gateway_direct.send(:url, :capture, '0000') + assert_equal "#{url}/v2/#{merchant_id}/payments/0000/refund", @gateway_direct.send(:url, :refund, '0000') + assert_equal "#{url}/v2/#{merchant_id}/payments/0000/cancel", @gateway_direct.send(:url, :void, '0000') + assert_equal "#{url}/v2/#{merchant_id}/payments/0000", @gateway_direct.send(:url, :inquire, '0000') + end + def test_supported_card_types assert_equal GlobalCollectGateway.supported_cardtypes, %i[visa master american_express discover naranja cabal tuya patagonia_365] end diff --git a/test/unit/versionable_test.rb b/test/unit/versionable_test.rb new file mode 100644 index 00000000000..3e8bd5485f6 --- /dev/null +++ b/test/unit/versionable_test.rb @@ -0,0 +1,89 @@ +require 'test_helper' + +class VersionableTest < Test::Unit::TestCase + class ParentClass + include ActiveMerchant::Versionable + end + + class DummyClass < ParentClass + end + + class FakeClass < ParentClass + end + + class DummyChildClass < DummyClass + end + + def setup + @dummy_instance = DummyClass.new + @fake_instance = FakeClass.new + @dummy_child_instance = DummyChildClass.new + end + + def test_class_can_set_and_fetch_default_version + DummyClass.version('1.0') + assert_equal '1.0', DummyClass.fetch_version, 'Class should return the correct version' + end + + def test_class_can_set_and_fetch_custom_feature_version + DummyClass.version('2.0', :custom_api) + DummyClass.version('v2', :some_feature) + assert_equal '2.0', DummyClass.fetch_version(:custom_api), 'Class should return the correct version' + assert_equal 'v2', DummyClass.fetch_version(:some_feature), 'Class should return the correct version' + end + + def test_instance_can_fetch_default_version + DummyClass.version('v3') + assert_equal 'v3', @dummy_instance.fetch_version, 'Instance should return the correct version' + end + + def test_instance_can_fetch_custom_feature_version + DummyClass.version('v4', :custom_api) + DummyClass.version('4.0', :some_feature) + assert_equal 'v4', @dummy_instance.fetch_version(:custom_api), 'Instance should return the correct version' + assert_equal '4.0', @dummy_instance.fetch_version(:some_feature), 'Instance should return the correct version' + end + + def test_fetch_version_returns_nil_for_unset_feature + assert_nil DummyClass.fetch_version(:nonexistent_feature), 'Class should return nil for an unset feature' + assert_nil @dummy_instance.fetch_version(:nonexistent_feature), 'Instance should return nil for an unset feature' + end + + def test_classes_dont_share_versions + # Default key + DummyClass.version('1.0') + FakeClass.version('2.0') + DummyChildClass.version('3.0') + + # Custom key :some_feature + DummyChildClass.version('v5', :some_feature) + FakeClass.version('v3', :some_feature) + DummyClass.version('v4', :some_feature) + + assert_equal '1.0', DummyClass.fetch_version, 'Class should return the correct version' + assert_equal 'v4', DummyClass.fetch_version(:some_feature), 'Class should return the correct version' + assert_equal '2.0', FakeClass.fetch_version, 'Class should return the correct version' + assert_equal 'v3', FakeClass.fetch_version(:some_feature), 'Class should return the correct version' + assert_equal '3.0', DummyChildClass.fetch_version, 'Class should return the correct version' + assert_equal 'v5', DummyChildClass.fetch_version(:some_feature), 'Class should return the correct version' + end + + def test_instances_dont_share_versions + # Default key + DummyClass.version('1.0') + FakeClass.version('2.0') + DummyChildClass.version('3.0') + + # Custom key :some_feature + DummyChildClass.version('v5', :some_feature) + FakeClass.version('v3', :some_feature) + DummyClass.version('v4', :some_feature) + + assert_equal '1.0', @dummy_instance.fetch_version, 'Class should return the correct version' + assert_equal 'v4', @dummy_instance.fetch_version(:some_feature), 'Class should return the correct version' + assert_equal '2.0', @fake_instance.fetch_version, 'Class should return the correct version' + assert_equal 'v3', @fake_instance.fetch_version(:some_feature), 'Class should return the correct version' + assert_equal '3.0', @dummy_child_instance.fetch_version, 'Class should return the correct version' + assert_equal 'v5', @dummy_child_instance.fetch_version(:some_feature), 'Class should return the correct version' + end +end