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