diff --git a/lib/buy_account_credit_form.rb b/lib/buy_account_credit_form.rb index b6a1da671cb6c4c17886ed433c7dc2ff56e5a828..69b367672708066cd08fa84cd3f52d57236307a4 100644 --- a/lib/buy_account_credit_form.rb +++ b/lib/buy_account_credit_form.rb @@ -1,21 +1,25 @@ # frozen_string_literal: true +require_relative "trust_level_repo" +require_relative "credit_card_sale" + class BuyAccountCreditForm - class AmountValidationError < StandardError - def initialize(amount) - super("amount #{amount} must be more than $15") - end + def self.trust_level_repo(**kwargs) + kwargs[:trust_level_repo] || TrustLevelRepo.new(**kwargs) end def self.for(customer) - customer.payment_methods.then do |payment_methods| - new(customer.balance, payment_methods) + trust_level_repo.find(customer).then do |trust_level| + customer.payment_methods.then do |payment_methods| + new(customer.balance, payment_methods, trust_level.max_top_up_amount) + end end end - def initialize(balance, payment_methods) + def initialize(balance, payment_methods, max_top_up_amount) @balance = balance @payment_methods = payment_methods + @max_top_up_amount = max_top_up_amount end def form @@ -23,13 +27,12 @@ class BuyAccountCreditForm :top_up, balance: @balance, payment_methods: @payment_methods, - range: [15, nil] + range: [15, @max_top_up_amount] ) end def parse(form) amount = form.field("amount")&.value&.to_s - raise AmountValidationError, amount unless amount.to_i >= 15 { payment_method: @payment_methods.fetch( diff --git a/lib/credit_card_sale.rb b/lib/credit_card_sale.rb index 1c886774a31668cde8052e60fb8d9ae10aee7ed2..eeb830fb9681c4b107a7ff8685eee93d07db4a66 100644 --- a/lib/credit_card_sale.rb +++ b/lib/credit_card_sale.rb @@ -6,6 +6,38 @@ require "delegate" require_relative "transaction" require_relative "trust_level_repo" +class TransactionDeclinedError < StandardError; end + +class AmountTooHighError < TransactionDeclinedError + attr_reader :amount, :max_amount + + def initialize(amount, max_amount) + @amount = amount + @max_amount = max_amount + super("Amount $#{amount} exceeds maximum allowed amount of $#{max_amount}") + end +end + +class AmountTooLowError < TransactionDeclinedError + attr_reader :amount, :min_amount + + def initialize(amount, min_amount) + @amount = amount + @min_amount = min_amount + super("Amount $#{amount} is below minimum amount of $#{min_amount}") + end +end + +class DeclinedError < TransactionDeclinedError + attr_reader :declines, :max_declines + + def initialize(declines, max_declines) + @declines = declines + @max_declines = max_declines + super("Transaction declined") + end +end + class CreditCardSale def self.create(*args, transaction_class: Transaction, **kwargs) new(*args, **kwargs).sale.then do |response| @@ -62,10 +94,9 @@ protected REDIS.exists("jmp_customer_credit_card_lock-#{@customer.customer_id}"), @trust_repo.find(@customer), @customer.declines ]).then do |(lock, tl, declines)| - unless tl.credit_card_transaction?(@amount.to_d, declines) - raise "Declined" - end - raise "Too many payments recently" if lock == 1 + raise TransactionDeclinedError, "Too many payments recently" if lock == 1 + + tl.validate_credit_card_transaction!(@amount.to_d, declines) end end @@ -107,7 +138,7 @@ class BraintreeFailure < StandardError attr_reader :response def initialize(response) - super response.message + super(response.message) @response = response end end diff --git a/lib/trust_level.rb b/lib/trust_level.rb index 156fcf0cf9fb9963d09aafc77a40d48eb58ed003..7db31271943fcd366a0986c6af9bf2e5785d9d3a 100644 --- a/lib/trust_level.rb +++ b/lib/trust_level.rb @@ -32,6 +32,10 @@ module TrustLevel new if manual == "Tomb" end + def max_top_up_amount + 0 + end + def write_cdr? false end @@ -44,8 +48,9 @@ module TrustLevel false end - def credit_card_transaction?(*) - false + def validate_credit_card_transaction!(_amount, _declines) + # Give a more ambiguous error so they don't know they're tombed. + raise DeclinedError end def create_subaccount?(*) @@ -62,6 +67,14 @@ module TrustLevel new if manual == "Basement" || (!manual && settled_amount < 10) end + def max_top_up_amount + 35 + end + + def max_declines + 2 + end + def write_cdr? true end @@ -74,8 +87,11 @@ module TrustLevel messages_today < 40 end - def credit_card_transaction?(amount, declines) - amount <= 35 && declines <= 2 + def validate_credit_card_transaction!(amount, declines) + raise DeclinedError.new(declines, max_declines) if declines > max_declines + return unless amount > max_top_up_amount + + raise AmountTooHighError.new(amount, max_top_up_amount) end def create_subaccount?(already_have) @@ -92,6 +108,14 @@ module TrustLevel new if manual == "Paragon" || (!manual && settled_amount > 60) end + def max_top_up_amount + 500 + end + + def max_declines + 3 + end + def write_cdr? true end @@ -104,8 +128,11 @@ module TrustLevel messages_today < 700 end - def credit_card_transaction?(amount, declines) - amount <= 500 && declines <= 3 + def validate_credit_card_transaction!(amount, declines) + raise DeclinedError.new(declines, max_declines) if declines > max_declines + return unless amount > max_top_up_amount + + raise AmountTooHighError.new(amount, max_top_up_amount) end def create_subaccount?(already_have) @@ -134,9 +161,7 @@ module TrustLevel true end - def credit_card_transaction?(*) - true - end + def validate_credit_card_transaction!(*) end def create_subaccount?(*) true @@ -167,6 +192,14 @@ module TrustLevel @max_rate = EXPENSIVE_ROUTE.fetch(plan_name, 0.1) end + def max_top_up_amount + 130 + end + + def max_declines + 2 + end + def write_cdr? true end @@ -179,8 +212,11 @@ module TrustLevel messages_today < 500 end - def credit_card_transaction?(amount, declines) - amount <= 130 && declines <= 2 + def validate_credit_card_transaction!(amount, declines) + raise DeclinedError.new(declines, max_declines) if declines > max_declines + return unless amount > max_top_up_amount + + raise AmountTooHighError.new(amount, max_top_up_amount) end def create_subaccount?(already_have) diff --git a/sgx_jmp.rb b/sgx_jmp.rb index 588f5ba49eb29f4bce72639c956bde84e653869c..4e561b08cadccce35cd0821f94195239d58b53cf 100644 --- a/sgx_jmp.rb +++ b/sgx_jmp.rb @@ -670,7 +670,10 @@ Command.new( end }.then { |transaction| Command.finish("#{transaction} added to your account balance.") - }.catch_only(BuyAccountCreditForm::AmountValidationError) do |e| + }.catch_only( + BuyAccountCreditForm::AmountTooHighError, + BuyAccountCreditForm::AmountTooLowError + ) do |e| Command.finish(e.message, type: :error) end }.register(self).then(&CommandList.method(:register)) diff --git a/test/test_buy_account_credit_form.rb b/test/test_buy_account_credit_form.rb index 5a65387d6b0c2332743fd316dc962a8bad1afc8e..0c05519fdc4166f34b5e4126a3d56924f66f3cf6 100644 --- a/test/test_buy_account_credit_form.rb +++ b/test/test_buy_account_credit_form.rb @@ -3,16 +3,21 @@ require "test_helper" require "buy_account_credit_form" require "customer" +require "credit_card_sale" CustomerFinancials::BRAINTREE = Minitest::Mock.new CustomerFinancials::REDIS = Minitest::Mock.new +TrustLevelRepo::REDIS = Minitest::Mock.new +TrustLevelRepo::DB = Minitest::Mock.new class BuyAccountCreditFormTest < Minitest::Test def setup @payment_method = OpenStruct.new(card_type: "Test", last_4: "1234") + @max_top_up_amount = 130 @form = BuyAccountCreditForm.new( BigDecimal("15.1234"), - PaymentMethods.new([@payment_method]) + PaymentMethods.new([@payment_method]), + @max_top_up_amount ) end @@ -26,10 +31,24 @@ class BuyAccountCreditFormTest < Minitest::Test ["test"] ) + TrustLevelRepo::REDIS.expect( + :get, + EMPromise.resolve("Customer"), + ["jmp_customer_trust_level-test"] + ) + TrustLevelRepo::DB.expect( + :query_one, + EMPromise.resolve({}), + [String, "test"], default: {} + ) + assert_kind_of( BuyAccountCreditForm, BuyAccountCreditForm.for(customer).sync ) + + assert_mock TrustLevelRepo::REDIS + assert_mock TrustLevelRepo::DB end em :test_for @@ -67,14 +86,6 @@ class BuyAccountCreditFormTest < Minitest::Test assert_equal "123", @form.parse(iq_form)[:amount] end - def test_parse_bad_amount - iq_form = Blather::Stanza::X.new - iq_form.fields = [{ var: "amount", value: "1" }] - assert_raises(BuyAccountCreditForm::AmountValidationError) do - @form.parse(iq_form)[:amount] - end - end - def test_parse_payment_method iq_form = Blather::Stanza::X.new iq_form.fields = [ diff --git a/test/test_credit_card_sale.rb b/test/test_credit_card_sale.rb index f2e037b5ee2bd6f705472ff1db672eca9ecc3b1f..deded1504864f8b11fac0c7405bfbe6970c4a0bc 100644 --- a/test/test_credit_card_sale.rb +++ b/test/test_credit_card_sale.rb @@ -9,6 +9,7 @@ CreditCardSale::BRAINTREE = Minitest::Mock.new CreditCardSale::REDIS = Minitest::Mock.new TrustLevelRepo::REDIS = Minitest::Mock.new TrustLevelRepo::DB = Minitest::Mock.new +CustomerFinancials::REDIS = Minitest::Mock.new class CreditCardSaleTest < Minitest::Test FAKE_BRAINTREE_TRANSACTION = @@ -111,6 +112,80 @@ class CreditCardSaleTest < Minitest::Test end em :test_sale_locked + def test_sale_amount_too_high + CreditCardSale::REDIS.expect( + :exists, + EMPromise.resolve(0), + ["jmp_customer_credit_card_lock-test"] + ) + TrustLevelRepo::REDIS.expect( + :get, + EMPromise.resolve("Customer"), + ["jmp_customer_trust_level-test"] + ) + TrustLevelRepo::DB.expect( + :query_one, + EMPromise.resolve({}), + [String, "test"], default: {} + ) + CustomerFinancials::REDIS.expect( + :get, + EMPromise.resolve("0"), + ["jmp_pay_decline-test"] + ) + + assert_raises(AmountTooHighError) do + CreditCardSale.new( + customer(plan_name: "test_usd"), + amount: 131, + payment_method: OpenStruct.new(token: "token") + ).sale.sync + end + + assert_mock CustomerFinancials::REDIS + assert_mock CreditCardSale::REDIS + assert_mock TrustLevelRepo::REDIS + assert_mock TrustLevelRepo::DB + end + em :test_sale_amount_too_high + + def test_sale_too_many_declines + CreditCardSale::REDIS.expect( + :exists, + EMPromise.resolve(0), + ["jmp_customer_credit_card_lock-test"] + ) + TrustLevelRepo::REDIS.expect( + :get, + EMPromise.resolve("Customer"), + ["jmp_customer_trust_level-test"] + ) + TrustLevelRepo::DB.expect( + :query_one, + EMPromise.resolve({}), + [String, "test"], default: {} + ) + CustomerFinancials::REDIS.expect( + :get, + EMPromise.resolve("3"), + ["jmp_pay_decline-test"] + ) + + assert_raises(DeclinedError) do + CreditCardSale.new( + customer(plan_name: "test_usd"), + amount: 50, + payment_method: OpenStruct.new(token: "token") + ).sale.sync + end + + assert_mock CustomerFinancials::REDIS + assert_mock CreditCardSale::REDIS + assert_mock TrustLevelRepo::REDIS + assert_mock TrustLevelRepo::DB + end + em :test_sale_too_many_declines + def test_sale req = stub_request( :post,