@@ -18,10 +18,12 @@ class Transaction
def self.resolve_payment_method(customer, payment_method)
EMPromise.all([
+ REDIS.exists("jmp_customer_credit_card_lock-#{customer.customer_id}"),
customer.declines,
payment_method || customer.payment_methods.then(&:default_payment_method)
- ]).then do |(declines, selected_method)|
+ ]).then do |(lock, declines, selected_method)|
raise "Declined" if declines >= 2
+ raise "Too many payments recently" if lock == 1
raise "No valid payment method on file" unless selected_method
selected_method
@@ -29,7 +31,14 @@ class Transaction
end
def self.decline_guard(customer, response)
- return response.transaction if response.success?
+ if response.success?
+ REDIS.setex(
+ "jmp_customer_credit_card_lock-#{customer.customer_id}",
+ 60 * 60 * 24,
+ "1"
+ )
+ return response.transaction
+ end
customer.mark_decline
raise response.message
@@ -18,6 +18,11 @@ class TransactionTest < Minitest::Test
)
def test_sale_fails
+ Transaction::REDIS.expect(
+ :exists,
+ EMPromise.resolve(0),
+ ["jmp_customer_credit_card_lock-test"]
+ )
CustomerFinancials::REDIS.expect(
:get,
EMPromise.resolve("1"),
@@ -50,10 +55,39 @@ class TransactionTest < Minitest::Test
).sync
end
assert_mock CustomerFinancials::REDIS
+ assert_mock Transaction::REDIS
end
em :test_sale_fails
+ def test_sale_locked
+ Transaction::REDIS.expect(
+ :exists,
+ EMPromise.resolve(1),
+ ["jmp_customer_credit_card_lock-test"]
+ )
+ CustomerFinancials::REDIS.expect(
+ :get,
+ EMPromise.resolve("0"),
+ ["jmp_pay_decline-test"]
+ )
+ assert_raises("locked") do
+ Transaction.sale(
+ customer(plan_name: "test_usd"),
+ amount: 123,
+ payment_method: OpenStruct.new(token: "token")
+ ).sync
+ end
+ assert_mock CustomerFinancials::REDIS
+ assert_mock Transaction::REDIS
+ end
+ em :test_sale_locked
+
def test_sale
+ Transaction::REDIS.expect(
+ :exists,
+ EMPromise.resolve(0),
+ ["jmp_customer_credit_card_lock-test"]
+ )
CustomerFinancials::REDIS.expect(
:get,
EMPromise.resolve("1"),
@@ -76,6 +110,11 @@ class TransactionTest < Minitest::Test
options: { submit_for_settlement: true }
}]
)
+ Transaction::REDIS.expect(
+ :setex,
+ EMPromise.resolve(1),
+ ["jmp_customer_credit_card_lock-test", 86400, "1"]
+ )
result = Transaction.sale(
customer(plan_name: "test_usd"),
amount: 123,
@@ -83,6 +122,7 @@ class TransactionTest < Minitest::Test
).sync
assert_kind_of Transaction, result
assert_mock CustomerFinancials::REDIS
+ assert_mock Transaction::REDIS
end
em :test_sale