M lib/registration.rb => lib/registration.rb +3 -3
@@ 219,9 219,9 @@ class Registration
def write
Transaction.sale(
- @customer.merchant_account,
- @payment_method,
- CONFIG[:activation_amount]
+ @customer,
+ CONFIG[:activation_amount],
+ @payment_method
).then(
method(:sold),
->(_) { declined }
M lib/transaction.rb => lib/transaction.rb +33 -9
@@ 1,18 1,42 @@
# frozen_string_literal: true
class Transaction
- def self.sale(merchant_account, payment_method, amount)
- BRAINTREE.transaction.sale(
- amount: amount,
- payment_method_token: payment_method.token,
- merchant_account_id: merchant_account,
- options: { submit_for_settlement: true }
- ).then do |response|
- raise response.message unless response.success?
- new(response.transaction)
+ def self.sale(customer, amount, payment_method=nil)
+ REDIS.get("jmp_pay_decline-#{customer.customer_id}").then do |declines|
+ raise "too many declines" if declines.to_i >= 2
+
+ BRAINTREE.transaction.sale(
+ amount: amount,
+ **sale_args_for(customer, payment_method)
+ ).then do |response|
+ decline_guard(customer, response)
+ new(response.transaction)
+ end
end
end
+ def self.decline_guard(customer, response)
+ return if response.success?
+
+ REDIS.incr("jmp_pay_decline-#{customer.customer_id}").then do
+ REDIS.expire("jmp_pay_decline-#{customer.customer_id}", 60 * 60 * 24)
+ end
+ raise response.message
+ end
+
+ def self.sale_args_for(customer, payment_method=nil)
+ {
+ merchant_account_id: customer.merchant_account,
+ options: { submit_for_settlement: true }
+ }.merge(
+ if payment_method
+ { payment_method_token: payment_method.token }
+ else
+ { customer_id: customer.id }
+ end
+ )
+ end
+
attr_reader :amount
def initialize(braintree_transaction)
M sgx_jmp.rb => sgx_jmp.rb +3 -3
@@ 205,17 205,17 @@ command :execute?, node: "buy-credit", sessionid: nil do |iq|
BuyAccountCreditForm.new(customer).add_to_form(reply.form).then { customer }
}.then { |customer|
EMPromise.all([
+ customer,
customer.payment_methods,
- customer.merchant_account,
COMMAND_MANAGER.write(reply)
])
- }.then { |(payment_methods, merchant_account, iq2)|
+ }.then { |(customer, payment_methods, iq2)|
iq = iq2 # This allows the catch to use it also
payment_method = payment_methods.fetch(
iq.form.field("payment_method")&.value.to_i
)
amount = iq.form.field("amount").value.to_s
- Transaction.sale(merchant_account, payment_method, amount)
+ Transaction.sale(customer, amount, payment_method)
}.then { |transaction|
transaction.insert.then { transaction.amount }
}.then { |amount|
M test/test_registration.rb => test/test_registration.rb +12 -12
@@ 214,19 214,19 @@ class RegistrationTest < Minitest::Test
:insert,
EMPromise.resolve(nil)
)
+ customer = Minitest::Mock.new(
+ Customer.new("test", plan_name: "test_usd")
+ )
Registration::Payment::CreditCard::Activate::Transaction.expect(
:sale,
transaction,
[
- "merchant_usd",
- :test_default_method,
- CONFIG[:activation_amount]
+ customer,
+ CONFIG[:activation_amount],
+ :test_default_method
]
)
iq = Blather::Stanza::Iq::Command.new
- customer = Minitest::Mock.new(
- Customer.new("test", plan_name: "test_usd")
- )
customer.expect(:bill_plan, nil)
Registration::Payment::CreditCard::Activate::Finish.expect(
:new,
@@ 247,20 247,20 @@ class RegistrationTest < Minitest::Test
em :test_write
def test_write_declines
+ customer = Minitest::Mock.new(
+ Customer.new("test", plan_name: "test_usd")
+ )
Registration::Payment::CreditCard::Activate::Transaction.expect(
:sale,
EMPromise.reject("declined"),
[
- "merchant_usd",
- :test_default_method,
- CONFIG[:activation_amount]
+ customer,
+ CONFIG[:activation_amount],
+ :test_default_method
]
)
iq = Blather::Stanza::Iq::Command.new
iq.from = "test@example.com"
- customer = Minitest::Mock.new(
- Customer.new("test", plan_name: "test_usd")
- )
result = Minitest::Mock.new
result.expect(:then, nil)
Registration::Payment::CreditCard::Activate::COMMAND_MANAGER.expect(
M test/test_transaction.rb => test/test_transaction.rb +29 -8
@@ 5,6 5,7 @@ require "transaction"
Transaction::DB = Minitest::Mock.new
Transaction::BRAINTREE = Minitest::Mock.new
+Transaction::REDIS = Minitest::Mock.new
class TransactionTest < Minitest::Test
FAKE_BRAINTREE_TRANSACTION =
@@ 16,26 17,46 @@ class TransactionTest < Minitest::Test
)
def test_sale_fails
+ Transaction::REDIS.expect(
+ :get,
+ EMPromise.resolve("1"),
+ ["jmp_pay_decline-test"]
+ )
+ Transaction::REDIS.expect(
+ :incr,
+ EMPromise.resolve(nil),
+ ["jmp_pay_decline-test"]
+ )
+ Transaction::REDIS.expect(
+ :expire,
+ EMPromise.resolve(nil),
+ ["jmp_pay_decline-test", 60 * 60 * 24]
+ )
braintree_transaction = Minitest::Mock.new
Transaction::BRAINTREE.expect(:transaction, braintree_transaction)
braintree_transaction.expect(
:sale,
EMPromise.resolve(
- OpenStruct.new(success?: false)
+ OpenStruct.new(success?: false, message: "declined")
),
[Hash]
)
- assert_raises do
+ assert_raises("declined") do
Transaction.sale(
- "merchant_usd",
- OpenStruct.new(token: "token"),
- 123
+ Customer.new("test", plan_name: "test_usd"),
+ 123,
+ OpenStruct.new(token: "token")
).sync
end
end
em :test_sale_fails
def test_sale
+ Transaction::REDIS.expect(
+ :get,
+ EMPromise.resolve("1"),
+ ["jmp_pay_decline-test"]
+ )
braintree_transaction = Minitest::Mock.new
Transaction::BRAINTREE.expect(:transaction, braintree_transaction)
braintree_transaction.expect(
@@ 54,9 75,9 @@ class TransactionTest < Minitest::Test
}]
)
result = Transaction.sale(
- "merchant_usd",
- OpenStruct.new(token: "token"),
- 123
+ Customer.new("test", plan_name: "test_usd"),
+ 123,
+ OpenStruct.new(token: "token")
).sync
assert_kind_of Transaction, result
end