1# frozen_string_literal: true
2
3require "bigdecimal"
4require "bigdecimal/util"
5
6require_relative "trust_level_repo"
7
8class Transaction
9 def self.sale(customer, amount:, payment_method: nil)
10 resolve_payment_method(customer, payment_method, amount).then do |selected|
11 BRAINTREE.transaction.sale(
12 amount: amount,
13 merchant_account_id: customer.merchant_account,
14 options: { submit_for_settlement: true },
15 payment_method_token: selected.token
16 ).then do |response|
17 new(decline_guard(customer, response))
18 end
19 end
20 end
21
22 def self.resolve_payment_method(customer, payment_method, amount)
23 EMPromise.all([
24 REDIS.exists("jmp_customer_credit_card_lock-#{customer.customer_id}"),
25 TrustLevelRepo.new.find(customer), customer.declines,
26 payment_method || customer.payment_methods.then(&:default_payment_method)
27 ]).then do |(lock, tl, declines, selected_method)|
28 raise "Declined" unless tl.credit_card_transaction?(amount.to_d, declines)
29 raise "Too many payments recently" if lock == 1
30 raise "No valid payment method on file" unless selected_method
31
32 selected_method
33 end
34 end
35
36 def self.decline_guard(customer, response)
37 if response.success?
38 REDIS.setex(
39 "jmp_customer_credit_card_lock-#{customer.customer_id}",
40 60 * 60 * 24,
41 "1"
42 )
43 return response.transaction
44 end
45
46 customer.mark_decline
47 raise response.message
48 end
49
50 attr_reader :amount
51
52 def initialize(braintree_transaction)
53 @customer_id = braintree_transaction.customer_details.id
54 @transaction_id = braintree_transaction.id
55 @created_at = braintree_transaction.created_at
56 @amount = BigDecimal(braintree_transaction.amount, 4)
57 end
58
59 def insert
60 EM.promise_fiber do
61 DB.transaction do
62 insert_tx
63 insert_bonus
64 end
65 end
66 end
67
68 def total
69 amount + bonus
70 end
71
72 def bonus
73 return BigDecimal(0) if amount <= 15
74
75 amount *
76 case amount
77 when (15..29.99)
78 0.01
79 when (30..139.99)
80 0.03
81 else
82 0.05
83 end
84 end
85
86 def to_s
87 plus = " + #{'%.4f' % bonus} bonus"
88 "$#{'%.2f' % amount}#{plus if bonus.positive?}"
89 end
90
91 def settled_after
92 @created_at + (90 * 24 * 60 * 60)
93 end
94
95protected
96
97 def insert_tx
98 params = [
99 @customer_id, @transaction_id, @created_at, settled_after, @amount
100 ]
101 DB.exec(<<~SQL, params)
102 INSERT INTO transactions
103 (customer_id, transaction_id, created_at, settled_after, amount, note)
104 VALUES
105 ($1, $2, $3, $4, $5, 'Credit card payment')
106 SQL
107 end
108
109 def insert_bonus
110 return if bonus <= 0
111
112 params = [
113 @customer_id, "bonus_for_#{@transaction_id}", @created_at,
114 settled_after, bonus
115 ]
116 DB.exec(<<~SQL, params)
117 INSERT INTO transactions
118 (customer_id, transaction_id, created_at, settled_after, amount, note)
119 VALUES
120 ($1, $2, $3, $4, $5, 'Credit card payment bonus')
121 SQL
122 end
123end