# frozen_string_literal: true
require_relative "expiring_lock"
require_relative "transaction"
class LowBalance
def self.for(customer, transaction_amount=0)
return Locked.new unless customer.registered?
ExpiringLock.new(
"jmp_customer_low_balance-#{customer.billing_customer_id}",
expiry: 60 * 60 * 24 * 7
).with(-> { Locked.new }) do
customer.billing_customer.then do |billing_customer|
for_no_lock(billing_customer, transaction_amount)
end
end
end
def self.for_no_lock(customer, transaction_amount, auto: true)
if auto && customer.auto_top_up_amount.positive?
AutoTopUp.for(customer, transaction_amount)
else
customer.btc_addresses.then do |btc_addresses|
new(customer, btc_addresses, transaction_amount)
end
end
end
def initialize(customer, btc_addresses, transaction_amount=0)
@customer = customer
@btc_addresses = btc_addresses
@transaction_amount = transaction_amount
end
def notify!
m = Blather::Stanza::Message.new
m.from = CONFIG[:notify_from]
m.body =
"Your balance of $#{'%.4f' % @customer.balance} is low." \
"#{pending_cost_for_notification}" \
"#{btc_addresses_for_notification}"
@customer.stanza_to(m)
EMPromise.resolve(0)
end
def pending_cost_for_notification
return unless @transaction_amount&.positive?
return unless @transaction_amount > @customer.balance
"You need an additional " \
"$#{'%.2f' % (@transaction_amount - @customer.balance)} "\
"to complete this transaction."
end
def btc_addresses_for_notification
return if @btc_addresses.empty?
"\nYou can buy credit by sending any amount of Bitcoin to one of " \
"these addresses:\n#{@btc_addresses.join("\n")}"
end
class AutoTopUp
def self.for(customer, target=0)
customer.payment_methods.then(&:default_payment_method).then do |method|
blocked?(method).then do |block|
next AutoTopUp.new(customer, method, target) if block.zero?
log.info("#{customer.customer_id} auto top up blocked")
LowBalance.for_no_lock(customer, target, auto: false)
end
end
end
def self.blocked?(method)
return EMPromise.resolve(1) if method.nil?
REDIS.exists(
"jmp_auto_top_up_block-#{method&.unique_number_identifier}"
)
end
def initialize(customer, method=nil, target=0, margin: 10)
@customer = customer
@method = method
@target = target
@margin = margin
@message = Blather::Stanza::Message.new
@message.from = CONFIG[:notify_from]
end
def top_up_amount
[
(@target + @margin) - @customer.balance,
@customer.auto_top_up_amount
].max
end
def sale
CreditCardSale.create(@customer, amount: top_up_amount)
end
def failed(e)
@method && REDIS.setex(
"jmp_auto_top_up_block-#{@method.unique_number_identifier}",
60 * 60 * 24 * 30,
Time.now
)
@message.body =
"Automatic top-up transaction for " \
"$#{top_up_amount} failed: #{e.message}"
0
end
def notify!
sale.then { |tx|
@message.body =
"Automatic top-up has charged your default " \
"payment method and added #{tx} to your balance."
tx.total
}.catch(&method(:failed)).then { |amount|
@customer.stanza_to(@message)
amount
}
end
end
class Locked
def notify!
EMPromise.resolve(0)
end
end
end