From 35897491765c0aa74350ea0134c3521e3c395ea1 Mon Sep 17 00:00:00 2001 From: Stephen Paul Weber Date: Wed, 3 Mar 2021 15:30:40 -0500 Subject: [PATCH] Activate pending plan for customer when they pay enough BTC This is effectively the "BTC auto-accept" new way. If they send enough to activate (as set by activation_amount in config) and they have a pending plan in redis and no plan in the db, then we buy them that plan. We can't use the normal way to notify the user, because they likely haven't bought a phone number yet. Eventually we will be able to tell new-signup about this and have it inserted into the flow there, I think? Not sure what we want to do transitionally. --- bin/process_pending_btc_transactions | 111 +++++++++++++++++++++++---- 1 file changed, 98 insertions(+), 13 deletions(-) diff --git a/bin/process_pending_btc_transactions b/bin/process_pending_btc_transactions index 99cf838a88cf7b2d855253412e45c08c21e2fda4..1fc8cbe4a5d3cb32ac0673be7dc96bda61430ee2 100755 --- a/bin/process_pending_btc_transactions +++ b/bin/process_pending_btc_transactions @@ -10,7 +10,8 @@ # target = \(tel: Text) -> "${tel}@cheogram.com" # }, # electrum = env:ELECTRUM_CONFIG, -# plans = ./plans.dhall +# plans = ./plans.dhall, +# activation_amount = 10000 # }' require "bigdecimal" @@ -63,19 +64,36 @@ btc_sell_price[:CAD] = BigDecimal.new( btc_sell_price[:USD] = btc_sell_price[:CAD] * cad_to_usd class Plan - def self.for_customer(customer_id) - row = DB.exec_params(<<-SQL, [customer_id]).first + def self.for_customer(customer) + row = DB.exec_params(<<-SQL, [customer.id]).first SELECT plan_name FROM customer_plans WHERE customer_id=$1 LIMIT 1 SQL - return unless row - plan = CONFIG[:plans].find { |p| p["plan_name"] = row["plan_name"] } - new(plan) if plan + from_name(customer, row&.[]("plan_name")) end - def initialize(plan) + def self.pending_for_customer(customer) + from_name( + customer, + REDIS.get("pending_plan_for-#{customer.id}"), + klass: Pending + ) + end + + def self.from_name(customer, plan_name, klass: Plan) + return unless plan_name + plan = CONFIG[:plans].find { |p| p[:name] == plan_name } + klass.new(customer, plan) if plan + end + + def initialize(customer, plan) + @customer = customer @plan = plan end + def name + @plan[:name] + end + def currency @plan[:currency] end @@ -84,6 +102,60 @@ class Plan bonus = (0.050167 * fiat_amount) - (currency == :CAD ? 1 : cad_to_usd) return bonus.round(4, :floor) if bonus > 0 end + + def price + BigDecimal.new(@plan[:monthly_price].to_i) * 0.0001 + end + + def insert(start:, expire:) + params = [@customer.id, name, start, expire] + DB.exec_params(<<-SQL, params) + INSERT INTO plan_log + (customer_id, plan_name, starts_at, expires_at) + VALUES + ($1, $2, $3, $4) + SQL + end + + def activate_any_pending_plan!; end + + class Pending < Plan + def initialize(customer, plan) + super + @go_until = Date.today >> 1 + end + + def activation_amount + camnt = BigDecimal.new(CONFIG[:activation_amount].to_i) * 0.0001 + [camnt, price].max + end + + def activate_any_pending_plan! + if @customer.balance < activation_amount + @customer.notify( + "Your account could not be activated because your " \ + "balance of $#{@customer.balance} is less that the " \ + "required activation amount of $#{activation_amount}. " \ + "Please buy more credit to have your account activated." + ) + else + charge_insert_notify + end + end + + protected + + def charge_insert_notify + @customer.add_transaction( + "activate_#{name}_until_#{@go_until}", + -price, + "Activate pending plan" + ) + insert(start: Date.today, expire: @go_until) + REDIS.del("pending_plan_for-#{@customer.id}") + # TODO: @customer.notify("Your account has been activated") + end + end end class Customer @@ -91,6 +163,10 @@ class Customer @customer_id = customer_id end + def id + @customer_id + end + def notify(body) jid = REDIS.get("jmp_customer_jid-#{@customer_id}") tel = REDIS.lindex("catapult_cred-#{jid}", 3) @@ -101,7 +177,18 @@ class Customer end def plan - Plan.for_customer(@customer_id) + Plan.for_customer(self) || pending_plan + end + + def pending_plan + Plan.pending_for_customer(self) + end + + def balance + result = DB.exec_params(<<-SQL, [@customer_id]).first&.[]("balance") + SELECT balance FROM balances WHERE customer_id=$1 + SQL + result || BigDecimal.new(0) end def add_btc_credit(txid, fiat_amount, cad_to_usd) @@ -122,8 +209,6 @@ class Customer ].compact.join) end -protected - def add_transaction(id, amount, note) DB.exec_params(<<-SQL, [@customer_id, id, amount, note]) INSERT INTO transactions @@ -146,13 +231,13 @@ REDIS.hgetall("pending_btc_transactions").each do |(txid, customer_id)| end DB.transaction do customer = Customer.new(customer_id) - plan = customer.plan - if plan + if (plan = customer.plan) amount = btc * btc_sell_price.fetch(plan.currency).round(4, :floor) customer.add_btc_credit(txid, amount, cad_to_usd) + customer.plan.activate_any_pending_plan! + REDIS.hdel("pending_btc_transactions", txid) else warn "No plan for #{customer_id} cannot save #{txid}" end end - REDIS.hdel("pending_btc_transactions", txid) end