1# frozen_string_literal: true
2
3require_relative "expiring_lock"
4require_relative "transaction"
5
6class LowBalance
7 def self.for(customer)
8 return Locked.new unless customer.registered?
9
10 ExpiringLock.new(
11 "jmp_customer_low_balance-#{customer.billing_customer_id}",
12 expiry: 60 * 60 * 24 * 7
13 ).with(-> { Locked.new }) do
14 customer.billing_customer.then(&method(:for_no_lock))
15 end
16 end
17
18 def self.for_no_lock(customer, auto: true)
19 if auto && customer.auto_top_up_amount.positive?
20 AutoTopUp.for(customer)
21 else
22 customer.btc_addresses.then do |btc_addresses|
23 new(customer, btc_addresses)
24 end
25 end
26 end
27
28 def initialize(customer, btc_addresses)
29 @customer = customer
30 @btc_addresses = btc_addresses
31 end
32
33 def notify!
34 m = Blather::Stanza::Message.new
35 m.from = CONFIG[:notify_from]
36 m.body =
37 "Your balance of $#{'%.4f' % @customer.balance} is low." \
38 "#{btc_addresses_for_notification}"
39 @customer.stanza_to(m)
40 EMPromise.resolve(0)
41 end
42
43 def btc_addresses_for_notification
44 return if @btc_addresses.empty?
45
46 "\nYou can buy credit by sending any amount of Bitcoin to one of " \
47 "these addresses:\n#{@btc_addresses.join("\n")}"
48 end
49
50 class AutoTopUp
51 def self.for(customer)
52 customer.payment_methods.then(&:default_payment_method).then do |method|
53 blocked?(method).then do |block|
54 next AutoTopUp.new(customer, method) if block.zero?
55
56 log.info("#{customer.customer_id} auto top up blocked")
57 LowBalance.for_no_lock(customer, auto: false)
58 end
59 end
60 end
61
62 def self.blocked?(method)
63 return EMPromise.resolve(1) if method.nil?
64
65 REDIS.exists(
66 "jmp_auto_top_up_block-#{method&.unique_number_identifier}"
67 )
68 end
69
70 def initialize(customer, method=nil)
71 @customer = customer
72 @method = method
73 @message = Blather::Stanza::Message.new
74 @message.from = CONFIG[:notify_from]
75 end
76
77 def sale
78 Transaction.sale(
79 @customer,
80 amount: @customer.auto_top_up_amount
81 ).then do |tx|
82 tx.insert.then { tx }
83 end
84 end
85
86 def failed(e)
87 @method && REDIS.setex(
88 "jmp_auto_top_up_block-#{@method.unique_number_identifier}",
89 60 * 60 * 24 * 30,
90 Time.now
91 )
92 @message.body =
93 "Automatic top-up transaction for " \
94 "$#{@customer.auto_top_up_amount} failed: #{e.message}"
95 0
96 end
97
98 def notify!
99 sale.then { |tx|
100 @message.body =
101 "Automatic top-up has charged your default " \
102 "payment method and added #{tx} to your balance."
103 tx.total
104 }.catch(&method(:failed)).then { |amount|
105 @customer.stanza_to(@message)
106 amount
107 }
108 end
109 end
110
111 class Locked
112 def notify!
113 EMPromise.resolve(0)
114 end
115 end
116end