@@ 0,0 1,93 @@
+#!/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")
+)