# frozen_string_literal: true
require "bigdecimal"
class Transaction
def self.sale(customer, amount:, payment_method: nil)
resolve_payment_method(customer, payment_method).then do |selected_method|
BRAINTREE.transaction.sale(
amount: amount,
merchant_account_id: customer.merchant_account,
options: { submit_for_settlement: true },
payment_method_token: selected_method.token
).then do |response|
new(decline_guard(customer, response))
end
end
end
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 |(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
end
end
def self.decline_guard(customer, response)
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
end
attr_reader :amount
def initialize(braintree_transaction)
@customer_id = braintree_transaction.customer_details.id
@transaction_id = braintree_transaction.id
@created_at = braintree_transaction.created_at
@amount = BigDecimal(braintree_transaction.amount, 4)
end
def insert
EM.promise_fiber do
DB.transaction do
insert_tx
insert_bonus
end
end
end
def total
amount + bonus
end
def bonus
return BigDecimal(0) if amount <= 15
amount *
case amount
when (15..29.99)
0.01
when (30..139.99)
0.03
else
0.05
end
end
def to_s
plus = " + #{'%.4f' % bonus} bonus"
"$#{'%.2f' % amount}#{plus if bonus.positive?}"
end
def settled_after
@created_at + (90 * 24 * 60 * 60)
end
protected
def insert_tx
params = [
@customer_id, @transaction_id, @created_at, settled_after, @amount
]
DB.exec(<<~SQL, params)
INSERT INTO transactions
(customer_id, transaction_id, created_at, settled_after, amount, note)
VALUES
($1, $2, $3, $4, $5, 'Credit card payment')
SQL
end
def insert_bonus
return if bonus <= 0
params = [
@customer_id, "bonus_for_#{@transaction_id}", @created_at,
settled_after, bonus
]
DB.exec(<<~SQL, params)
INSERT INTO transactions
(customer_id, transaction_id, created_at, settled_after, amount, note)
VALUES
($1, $2, $3, $4, $5, 'Credit card payment bonus')
SQL
end
end