diff --git a/lib/active_merchant/billing/credit_card.rb b/lib/active_merchant/billing/credit_card.rb index 81f978e5eab..a14bc726c6a 100644 --- a/lib/active_merchant/billing/credit_card.rb +++ b/lib/active_merchant/billing/credit_card.rb @@ -18,6 +18,7 @@ module Billing #:nodoc: # * Dankort # * Maestro # * Forbrugsforeningen + # * Elo # # For testing purposes, use the 'bogus' credit card brand. This skips the vast majority of # validations, allowing you to focus on your core concerns until you're ready to be more concerned @@ -88,6 +89,7 @@ def number=(value) # * +'dankort'+ # * +'maestro'+ # * +'forbrugsforeningen'+ + # * +'elo'+ # # Or, if you wish to test your implementation, +'bogus'+. # diff --git a/lib/active_merchant/billing/credit_card_methods.rb b/lib/active_merchant/billing/credit_card_methods.rb index ac87c50c4f4..47ad881355f 100644 --- a/lib/active_merchant/billing/credit_card_methods.rb +++ b/lib/active_merchant/billing/credit_card_methods.rb @@ -5,6 +5,7 @@ module CreditCardMethods CARD_COMPANY_DETECTORS = { 'visa' => ->(num) { num =~ /^4\d{12}(\d{3})?(\d{3})?$/ }, 'master' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 6), MASTERCARD_RANGES) }, + 'elo' => ->(num) { num&.size == 16 && in_bin_range?(num.slice(0, 6), ELO_RANGES) }, 'discover' => ->(num) { num =~ /^(6011|65\d{2}|64[4-9]\d)\d{12,15}|(62\d{14,17})$/ }, 'american_express' => ->(num) { num =~ /^3[47]\d{13}$/ }, 'diners_club' => ->(num) { num =~ /^3(0[0-5]|[68]\d)\d{11}$/ }, @@ -66,6 +67,18 @@ module CreditCardMethods (670000..679999), ] + # https://dev.elo.com.br/apis/tabela-de-bins, download csv from left sidebar + ELO_RANGES = [ + 506707..506708, 506715..506715, 506718..506722, 506724..506724, 506726..506736, 506739..506739, 506741..506743, + 506745..506747, 506753..506753, 506774..506776, 506778..506778, 509000..509001, 509003..509003, 509007..509007, + 509020..509022, 509035..509035, 509039..509042, 509045..509045, 509048..509048, 509051..509071, 509073..509074, + 509077..509080, 509084..509084, 509091..509094, 509098..509098, 509100..509100, 509104..509104, 509106..509109, + 627780..627780, 636368..636368, 650031..650033, 650035..650045, 650047..650047, 650406..650410, 650434..650436, + 650439..650439, 650485..650504, 650506..650530, 650577..650580, 650582..650591, 650721..650727, 650901..650922, + 650928..650928, 650938..650939, 650946..650948, 650954..650955, 650962..650963, 650967..650967, 650971..650971, + 651652..651667, 651675..651678, 655000..655010, 655012..655015, 655051..655052, 655056..655057 + ] + def self.included(base) base.extend(ClassMethods) end diff --git a/lib/active_merchant/billing/gateways/adyen.rb b/lib/active_merchant/billing/gateways/adyen.rb index 46e455479fc..73789688028 100644 --- a/lib/active_merchant/billing/gateways/adyen.rb +++ b/lib/active_merchant/billing/gateways/adyen.rb @@ -9,7 +9,7 @@ class AdyenGateway < Gateway self.supported_countries = ['AT', 'AU', 'BE', 'BG', 'BR', 'CH', 'CY', 'CZ', 'DE', 'DK', 'EE', 'ES', 'FI', 'FR', 'GB', 'GI', 'GR', 'HK', 'HU', 'IE', 'IS', 'IT', 'LI', 'LT', 'LU', 'LV', 'MC', 'MT', 'MX', 'NL', 'NO', 'PL', 'PT', 'RO', 'SE', 'SG', 'SK', 'SI', 'US'] self.default_currency = 'USD' - self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb, :dankort, :maestro, :discover] + self.supported_cardtypes = [:visa, :master, :american_express, :diners_club, :jcb, :dankort, :maestro, :discover, :elo] self.money_format = :cents diff --git a/test/remote/gateways/remote_adyen_test.rb b/test/remote/gateways/remote_adyen_test.rb index 340bbfde7a9..9c964702ae7 100644 --- a/test/remote/gateways/remote_adyen_test.rb +++ b/test/remote/gateways/remote_adyen_test.rb @@ -15,6 +15,15 @@ def setup :brand => 'visa' ) + @elo_credit_card = credit_card('5066 9911 1111 1118', + :month => 10, + :year => 2020, + :first_name => 'John', + :last_name => 'Smith', + :verification_value => '737', + :brand => 'elo' + ) + @three_ds_enrolled_card = credit_card('4212345678901237', brand: :visa) @declined_card = credit_card('4000300011112220') @@ -158,6 +167,12 @@ def test_successful_purchase_with_google_pay assert_equal '[capture-received]', response.message end + def test_successful_purchase_with_elo_card + response = @gateway.purchase(@amount, @elo_credit_card, @options.merge(currency: 'BRL')) + assert_success response + assert_equal '[capture-received]', response.message + end + def test_failed_purchase response = @gateway.purchase(@amount, @declined_card, @options) assert_failure response @@ -173,6 +188,15 @@ def test_successful_authorize_and_capture assert_equal '[capture-received]', capture.message end + def test_successful_authorize_and_capture_with_elo_card + auth = @gateway.authorize(@amount, @elo_credit_card, @options) + assert_success auth + + assert capture = @gateway.capture(@amount, auth.authorization) + assert_success capture + assert_equal '[capture-received]', capture.message + end + def test_partial_capture auth = @gateway.authorize(@amount, @credit_card, @options) assert_success auth @@ -196,6 +220,15 @@ def test_successful_refund assert_equal '[refund-received]', refund.message end + def test_successful_refund_with_elo_card + purchase = @gateway.purchase(@amount, @elo_credit_card, @options) + assert_success purchase + + assert refund = @gateway.refund(@amount, purchase.authorization) + assert_success refund + assert_equal '[refund-received]', refund.message + end + def test_partial_refund purchase = @gateway.purchase(@amount, @credit_card, @options) assert_success purchase @@ -219,6 +252,15 @@ def test_successful_void assert_equal '[cancel-received]', void.message end + def test_successful_void_with_elo_card + auth = @gateway.authorize(@amount, @elo_credit_card, @options) + assert_success auth + + assert void = @gateway.void(auth.authorization) + assert_success void + assert_equal '[cancel-received]', void.message + end + def test_failed_void response = @gateway.void('') assert_failure response @@ -233,6 +275,14 @@ def test_successful_store assert_equal 'Authorised', response.message end + def test_successful_store_with_elo_card + assert response = @gateway.store(@elo_credit_card, @options) + + assert_success response + assert !response.authorization.split('#')[2].nil? + assert_equal 'Authorised', response.message + end + def test_failed_store assert response = @gateway.store(@declined_card, @options) @@ -249,6 +299,15 @@ def test_successful_purchase_using_stored_card assert_equal '[capture-received]', response.message end + def test_successful_purchase_using_stored_elo_card + assert store_response = @gateway.store(@elo_credit_card, @options) + assert_success store_response + + response = @gateway.purchase(@amount, store_response.authorization, @options) + assert_success response + assert_equal '[capture-received]', response.message + end + def test_successful_authorize_using_stored_card assert store_response = @gateway.store(@credit_card, @options) assert_success store_response diff --git a/test/unit/credit_card_methods_test.rb b/test/unit/credit_card_methods_test.rb index da6211e1a7f..24e643e8eb4 100644 --- a/test/unit/credit_card_methods_test.rb +++ b/test/unit/credit_card_methods_test.rb @@ -132,6 +132,12 @@ def test_should_detect_vr_card assert_equal 'vr', CreditCard.brand?('63703644957644') end + def test_should_detect_elo_card + assert_equal 'elo', CreditCard.brand?('5090510000000000') + assert_equal 'elo', CreditCard.brand?('5067530000000000') + assert_equal 'elo', CreditCard.brand?('6509550000000000') + end + def test_should_detect_when_an_argument_brand_does_not_match_calculated_brand assert CreditCard.matching_brand?('4175001000000000', 'visa') assert_false CreditCard.matching_brand?('4175001000000000', 'master') diff --git a/test/unit/gateways/adyen_test.rb b/test/unit/gateways/adyen_test.rb index 3ebb510da71..9c6a759b5f6 100644 --- a/test/unit/gateways/adyen_test.rb +++ b/test/unit/gateways/adyen_test.rb @@ -19,6 +19,15 @@ def setup :brand => 'visa' ) + @elo_credit_card = credit_card('5066 9911 1111 1118', + :month => 10, + :year => 2020, + :first_name => 'John', + :last_name => 'Smith', + :verification_value => '737', + :brand => 'elo' + ) + @three_ds_enrolled_card = credit_card('4212345678901237', brand: :visa) @apple_pay_card = network_tokenization_credit_card('4111111111111111', @@ -117,6 +126,15 @@ def test_successful_purchase assert response.test? end + def test_successful_purchase_with_elo_card + response = stub_comms do + @gateway.purchase(@amount, @elo_credit_card, @options) + end.respond_with(successful_authorize_with_elo_response, successful_capture_with_elo_repsonse) + assert_success response + assert_equal '8835511210681145#8835511210689965#', response.authorization + assert response.test? + end + def test_successful_maestro_purchase response = stub_comms do @gateway.purchase(@amount, @credit_card, @options.merge({selected_brand: 'maestro', overwrite_brand: 'true'})) @@ -448,6 +466,25 @@ def failed_purchase_response RESPONSE end + def successful_authorize_with_elo_response + <<-RESPONSE + { + "pspReference":"8835511210681145", + "resultCode":"Authorised", + "authCode":"98696" + } + RESPONSE + end + + def successful_capture_with_elo_repsonse + <<-RESPONSE + { + "pspReference":"8835511210689965", + "response":"[capture-received]" + } + RESPONSE + end + def successful_authorize_response <<-RESPONSE {