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