~singpolyma/jmp-pay

37b09a44d8b9d9d85e45810cf5006b2968856965 — Stephen Paul Weber 1 year, 9 months ago ba4d587
Create initial monthly billing cronjob

Renews all expired plans where there is enough balance to do so.

Sets expires_at on all renewed plans to one month from today, so even if they
were very expired they are up to date the moment they pay.
1 files changed, 93 insertions(+), 0 deletions(-)

A bin/billing_monthly_cronjob
A bin/billing_monthly_cronjob => bin/billing_monthly_cronjob +93 -0
@@ 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")
)