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