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