diff --git a/lib/buy_account_credit_form.rb b/lib/buy_account_credit_form.rb index 69b367672708066cd08fa84cd3f52d57236307a4..367a95933a8fd36683842029c078489e6aa2b1f8 100644 --- a/lib/buy_account_credit_form.rb +++ b/lib/buy_account_credit_form.rb @@ -4,10 +4,19 @@ require_relative "trust_level_repo" require_relative "credit_card_sale" class BuyAccountCreditForm + # Returns a TrustLevelRepo instance, allowing for dependency injection. + # Either creates a new instance given kwargs or returns the existing instance. + # @param kwargs [Hash] keyword arguments. + # @option kwargs [TrustLevelRepo] :trust_level_repo An existing TrustLevelRepo instance. + # @return [TrustLevelRepo] An instance of TrustLevelRepo. def self.trust_level_repo(**kwargs) kwargs[:trust_level_repo] || TrustLevelRepo.new(**kwargs) end + # Factory method to create a BuyAccountCreditForm for a given customer. + # It fetches the customer's trust level to determine the maximum top-up amount. + # @param customer [Customer] The customer for whom the form is being created. + # @return [EMPromise] A promise that resolves with the new form instance. def self.for(customer) trust_level_repo.find(customer).then do |trust_level| customer.payment_methods.then do |payment_methods| @@ -16,12 +25,20 @@ class BuyAccountCreditForm end end + # Initializes a new BuyAccountCreditForm. + # @param balance [BigDecimal] The current balance of the customer. + # @param payment_methods [PaymentMethods] The available payment methods for the customer. + # @param max_top_up_amount [Numeric] The maximum amount the customer is allowed to top up, based on their trust level. def initialize(balance, payment_methods, max_top_up_amount) @balance = balance @payment_methods = payment_methods @max_top_up_amount = max_top_up_amount end + # Generates the form template for topping up account credit. + # The form will include a range for the amount field, constrained by a minimum of $15 + # and the customer's specific maximum top-up amount. + # @return [FormTemplate::OneRender] The rendered form template. def form FormTemplate.render( :top_up, diff --git a/lib/credit_card_sale.rb b/lib/credit_card_sale.rb index eeb830fb9681c4b107a7ff8685eee93d07db4a66..780efcc6cd9995ac318a5dca43455fc2528bd38b 100644 --- a/lib/credit_card_sale.rb +++ b/lib/credit_card_sale.rb @@ -8,9 +8,16 @@ require_relative "trust_level_repo" class TransactionDeclinedError < StandardError; end +# Error raised when a transaction amount exceeds the maximum allowed limit. class AmountTooHighError < TransactionDeclinedError - attr_reader :amount, :max_amount - + # @return [Numeric] The transaction amount that was too high. + attr_reader :amount + # @return [Numeric] The maximum amount allowed for the transaction. + attr_reader :max_amount + + # Initializes a new AmountTooHighError. + # @param amount [Numeric] The transaction amount. + # @param max_amount [Numeric] The maximum allowed amount. def initialize(amount, max_amount) @amount = amount @max_amount = max_amount @@ -18,9 +25,16 @@ class AmountTooHighError < TransactionDeclinedError end end +# Error raised when a transaction amount is below the minimum required limit. class AmountTooLowError < TransactionDeclinedError - attr_reader :amount, :min_amount - + # @return [Numeric] The transaction amount that was too low. + attr_reader :amount + # @return [Numeric] The minimum amount required for the transaction. + attr_reader :min_amount + + # Initializes a new AmountTooLowError. + # @param amount [Numeric] The transaction amount. + # @param min_amount [Numeric] The minimum required amount. def initialize(amount, min_amount) @amount = amount @min_amount = min_amount @@ -28,9 +42,16 @@ class AmountTooLowError < TransactionDeclinedError end end +# Error raised when a transaction is declined, potentially due to exceeding decline limits. class DeclinedError < TransactionDeclinedError - attr_reader :declines, :max_declines - + # @return [Integer, nil] The number of declines the customer has. + attr_reader :declines + # @return [Integer, nil] The maximum number of declines allowed. + attr_reader :max_declines + + # Initializes a new DeclinedError. + # @param declines [Integer, nil] The current number of declines. + # @param max_declines [Integer, nil] The maximum allowed declines. def initialize(declines, max_declines) @declines = declines @max_declines = max_declines @@ -89,6 +110,12 @@ class CreditCardSale protected + # Validates the transaction against customer locks, trust level, and decline history. + # @raise [TransactionDeclinedError] if the customer has made too many payments recently. + # @raise [AmountTooHighError] if the amount exceeds the trust level's maximum top-up amount. + # @raise [AmountTooLowError] if the amount is below any applicable minimum. + # @raise [DeclinedError] if the transaction is declined due to too many previous declines or other trust level restrictions. + # @return [EMPromise] A promise that resolves if validation passes, or rejects with an error. def validate! EMPromise.all([ REDIS.exists("jmp_customer_credit_card_lock-#{@customer.customer_id}"), diff --git a/lib/trust_level.rb b/lib/trust_level.rb index 7db31271943fcd366a0986c6af9bf2e5785d9d3a..cb48bf39a62dc3afcdcb9a3fd9d6c3a0c2e30251 100644 --- a/lib/trust_level.rb +++ b/lib/trust_level.rb @@ -32,6 +32,8 @@ module TrustLevel new if manual == "Tomb" end + # The maximum amount a user at Tomb trust level can top up. + # @return [Integer] Always 0 for Tomb level. def max_top_up_amount 0 end @@ -48,6 +50,11 @@ module TrustLevel false end + # Validates a credit card transaction for a Tomb trust level user. + # Users at this level cannot make credit card transactions. + # @param _amount [BigDecimal] The amount of the transaction (ignored). + # @param _declines [Integer] The number of recent declines (ignored). + # @raise [DeclinedError] Always raised to prevent transactions. def validate_credit_card_transaction!(_amount, _declines) # Give a more ambiguous error so they don't know they're tombed. raise DeclinedError @@ -67,10 +74,15 @@ module TrustLevel new if manual == "Basement" || (!manual && settled_amount < 10) end + # The maximum amount a user at Basement trust level can top up. + # @return [Integer] def max_top_up_amount 35 end + # The maximum number of credit card declines allowed for a Basement user + # before further transactions are blocked. + # @return [Integer] def max_declines 2 end @@ -87,6 +99,11 @@ module TrustLevel messages_today < 40 end + # Validates a credit card transaction for a Basement trust level user. + # @param amount [BigDecimal] The amount of the transaction. + # @param declines [Integer] The number of recent declines for the customer. + # @raise [DeclinedError] if the number of declines exceeds `max_declines`. + # @raise [AmountTooHighError] if the transaction amount exceeds `max_top_up_amount`. def validate_credit_card_transaction!(amount, declines) raise DeclinedError.new(declines, max_declines) if declines > max_declines return unless amount > max_top_up_amount @@ -108,10 +125,15 @@ module TrustLevel new if manual == "Paragon" || (!manual && settled_amount > 60) end + # The maximum amount a user at Paragon trust level can top up. + # @return [Integer] def max_top_up_amount 500 end + # The maximum number of credit card declines allowed for a Paragon user + # before further transactions are blocked. + # @return [Integer] def max_declines 3 end @@ -128,6 +150,11 @@ module TrustLevel messages_today < 700 end + # Validates a credit card transaction for a Paragon trust level user. + # @param amount [BigDecimal] The amount of the transaction. + # @param declines [Integer] The number of recent declines for the customer. + # @raise [DeclinedError] if the number of declines exceeds `max_declines`. + # @raise [AmountTooHighError] if the transaction amount exceeds `max_top_up_amount`. def validate_credit_card_transaction!(amount, declines) raise DeclinedError.new(declines, max_declines) if declines > max_declines return unless amount > max_top_up_amount @@ -161,6 +188,11 @@ module TrustLevel true end + # Validates a credit card transaction for an Olympias trust level user. + # Users at this level have no restrictions on credit card transactions through this method. + # @param _amount [BigDecimal] The amount of the transaction (ignored). + # @param _declines [Integer] The number of recent declines (ignored). + # @return [void] def validate_credit_card_transaction!(*) end def create_subaccount?(*) @@ -192,10 +224,15 @@ module TrustLevel @max_rate = EXPENSIVE_ROUTE.fetch(plan_name, 0.1) end + # The maximum amount a user at Customer trust level can top up. + # @return [Integer] def max_top_up_amount 130 end + # The maximum number of credit card declines allowed for a Customer user + # before further transactions are blocked. + # @return [Integer] def max_declines 2 end @@ -212,6 +249,11 @@ module TrustLevel messages_today < 500 end + # Validates a credit card transaction for a Customer trust level user. + # @param amount [BigDecimal] The amount of the transaction. + # @param declines [Integer] The number of recent declines for the customer. + # @raise [DeclinedError] if the number of declines exceeds `max_declines`. + # @raise [AmountTooHighError] if the transaction amount exceeds `max_top_up_amount`. def validate_credit_card_transaction!(amount, declines) raise DeclinedError.new(declines, max_declines) if declines > max_declines return unless amount > max_top_up_amount