Limit size of credit card transaction by trust level

Stephen Paul Weber created

Change summary

lib/transaction.rb       | 14 +++++++-----
lib/trust_level.rb       | 16 +++++++++++++++
test/test_transaction.rb | 44 +++++++++++++++++++++++++++++++++++++++--
3 files changed, 65 insertions(+), 9 deletions(-)

Detailed changes

lib/transaction.rb 🔗

@@ -2,27 +2,29 @@
 
 require "bigdecimal"
 
+require_relative "trust_level_repo"
+
 class Transaction
 	def self.sale(customer, amount:, payment_method: nil)
-		resolve_payment_method(customer, payment_method).then do |selected_method|
+		resolve_payment_method(customer, payment_method, amount).then do |selected|
 			BRAINTREE.transaction.sale(
 				amount: amount,
 				merchant_account_id: customer.merchant_account,
 				options: { submit_for_settlement: true },
-				payment_method_token: selected_method.token
+				payment_method_token: selected.token
 			).then do |response|
 				new(decline_guard(customer, response))
 			end
 		end
 	end
 
-	def self.resolve_payment_method(customer, payment_method)
+	def self.resolve_payment_method(customer, payment_method, amount)
 		EMPromise.all([
 			REDIS.exists("jmp_customer_credit_card_lock-#{customer.customer_id}"),
-			customer.declines,
+			TrustLevelRepo.new.find(customer), customer.declines,
 			payment_method || customer.payment_methods.then(&:default_payment_method)
-		]).then do |(lock, declines, selected_method)|
-			raise "Declined" if declines >= 2
+		]).then do |(lock, tl, declines, selected_method)|
+			raise "Declined" unless tl.credit_card_transaction?(amount, declines)
 			raise "Too many payments recently" if lock == 1
 			raise "No valid payment method on file" unless selected_method
 

lib/trust_level.rb 🔗

@@ -40,6 +40,10 @@ module TrustLevel
 			false
 		end
 
+		def credit_card_transaction?(*)
+			false
+		end
+
 		def to_s
 			"Tomb"
 		end
@@ -58,6 +62,10 @@ module TrustLevel
 			messages_today < 200
 		end
 
+		def credit_card_transaction?(amount, declines)
+			amount <= 35 && declines <= 2
+		end
+
 		def to_s
 			"Basement"
 		end
@@ -76,6 +84,10 @@ module TrustLevel
 			messages_today < 700
 		end
 
+		def credit_card_transaction?(amount, declines)
+			amount <= 500 && declines <= 3
+		end
+
 		def to_s
 			"Paragon"
 		end
@@ -107,6 +119,10 @@ module TrustLevel
 			messages_today < 500
 		end
 
+		def credit_card_transaction?(amount, declines)
+			amount <= 100 && declines <= 2
+		end
+
 		def to_s
 			"Customer"
 		end

test/test_transaction.rb 🔗

@@ -7,6 +7,8 @@ require "transaction"
 Transaction::DB = Minitest::Mock.new
 Transaction::BRAINTREE = Minitest::Mock.new
 Transaction::REDIS = Minitest::Mock.new
+TrustLevelRepo::REDIS = Minitest::Mock.new
+TrustLevelRepo::DB = Minitest::Mock.new
 
 class TransactionTest < Minitest::Test
 	FAKE_BRAINTREE_TRANSACTION =
@@ -23,6 +25,16 @@ class TransactionTest < Minitest::Test
 			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", Hash]
+		)
 		CustomerFinancials::REDIS.expect(
 			:get,
 			EMPromise.resolve("1"),
@@ -50,12 +62,14 @@ class TransactionTest < Minitest::Test
 		assert_raises("declined") do
 			Transaction.sale(
 				customer(plan_name: "test_usd"),
-				amount: 123,
+				amount: 99,
 				payment_method: OpenStruct.new(token: "token")
 			).sync
 		end
 		assert_mock CustomerFinancials::REDIS
 		assert_mock Transaction::REDIS
+		assert_mock TrustLevelRepo::REDIS
+		assert_mock TrustLevelRepo::DB
 	end
 	em :test_sale_fails
 
@@ -65,6 +79,16 @@ class TransactionTest < Minitest::Test
 			EMPromise.resolve(1),
 			["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", Hash]
+		)
 		CustomerFinancials::REDIS.expect(
 			:get,
 			EMPromise.resolve("0"),
@@ -79,6 +103,8 @@ class TransactionTest < Minitest::Test
 		end
 		assert_mock CustomerFinancials::REDIS
 		assert_mock Transaction::REDIS
+		assert_mock TrustLevelRepo::REDIS
+		assert_mock TrustLevelRepo::DB
 	end
 	em :test_sale_locked
 
@@ -88,6 +114,16 @@ class TransactionTest < Minitest::Test
 			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", Hash]
+		)
 		CustomerFinancials::REDIS.expect(
 			:get,
 			EMPromise.resolve("1"),
@@ -104,7 +140,7 @@ class TransactionTest < Minitest::Test
 				)
 			),
 			[{
-				amount: 123,
+				amount: 99,
 				payment_method_token: "token",
 				merchant_account_id: "merchant_usd",
 				options: { submit_for_settlement: true }
@@ -117,12 +153,14 @@ class TransactionTest < Minitest::Test
 		)
 		result = Transaction.sale(
 			customer(plan_name: "test_usd"),
-			amount: 123,
+			amount: 99,
 			payment_method: OpenStruct.new(token: "token")
 		).sync
 		assert_kind_of Transaction, result
 		assert_mock CustomerFinancials::REDIS
 		assert_mock Transaction::REDIS
+		assert_mock TrustLevelRepo::REDIS
+		assert_mock TrustLevelRepo::DB
 	end
 	em :test_sale