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