#!/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") )