#!/usr/bin/ruby
# frozen_string_literal: true
# Usage: ./billing_monthly_cronjob \
# '{ healthchecks_url = "https://hc-ping.com/...", plans = ./plans.dhall }'
require "bigdecimal"
require "date"
require "dhall"
require "net/http"
require "pg"
CONFIG = Dhall.load(ARGV[0]).sync
Net::HTTP.post_form(URI("#{CONFIG[:healthchecks_url]}/start"), {})
db = PG.connect(dbname: "jmp")
db.type_map_for_results = PG::BasicTypeMapForResults.new(db)
db.type_map_for_queries = PG::BasicTypeMapForQueries.new(db)
not_renewed = 0
renewed = 0
revenue = BigDecimal.new(0)
RENEW_UNTIL = Date.today >> 1
class Plan
def self.from_name(plan_name)
plan = CONFIG[:plans].find { |p| p[:name].to_s == plan_name }
new(plan) if plan
end
def initialize(plan)
@plan = plan
end
def price
BigDecimal.new(@plan["monthly_price"].to_i) * 0.0001
end
def bill_customer(db, customer_id)
transaction_id = "#{customer_id}-renew-until-#{RENEW_UNTIL}"
db.exec_params(<<-SQL, [customer_id, transaction_id, -price])
INSERT INTO transactions
(customer_id, transaction_id, amount, note)
VALUES
($1, $2, $3, 'Renew account plan')
SQL
end
def renew(db, customer_id, expires_at)
bill_customer(db, customer_id)
params = [RENEW_UNTIL, customer_id, expires_at]
db.exec_params(<<-SQL, params)
UPDATE plan_log SET expires_at=$1
WHERE customer_id=$2 AND expires_at=$3
SQL
end
end
db.transaction do
db.exec(
<<-SQL
SELECT customer_id, plan_name, expires_at, balance
FROM customer_plans INNER JOIN balances USING (customer_id)
WHERE expires_at <= NOW()
SQL
).each do |expired_customer|
plan = Plan.from_name(expired_customer["plan_name"])
if expired_customer["balance"] < plan.price
not_renewed += 1
next
end
plan.renew(
db,
expired_customer["customer_id"],
expired_customer["expires_at"]
)
renewed += 1
revenue += plan.price
end
end
Net::HTTP.post_form(
URI(CONFIG[:healthchecks_url].to_s),
renewed: renewed,
not_renewed: not_renewed,
revenue: revenue.to_s("F")
)