~singpolyma/jmp-pay

cbbbe757f7ea15fbde1a63a12c775044e55d009e — Stephen Paul Weber 1 year, 9 months ago b104530
Copy in account activation logic from sgx-jmp

This is largely duplicated code, but the whole web-activation path should go
away soon.  This fixes web activation to produce the data we actually expect
instead of the hack previously produced.  Instead of an account activation for 5
months, we insert 5 months of balance and then bill for only one month as is reasonable.
2 files changed, 122 insertions(+), 13 deletions(-)

M config.ru
A lib/transaction.rb
M config.ru => config.ru +52 -13
@@ 61,12 61,52 @@ class Plan
		SQL
	end

	def activate(customer_id, months)
		DB.exec_params(
			"INSERT INTO plan_log VALUES ($1, $2, tsrange($3, $4))",
			[customer_id, @plan[:name], Time.now, Date.today >> months]
		)
		true
	def bill_plan(customer_id)
		DB.transaction do
			charge_for_plan(customer_id)
			unless activate_plan_starting_now(customer_id)
				add_one_month_to_current_plan(customer_id)
			end
		end
	end

	def activate_plan_starting_now(customer_id)
		DB.exec(<<~SQL, [customer_id, @plan[:name]]).cmd_tuples.positive?
			INSERT INTO plan_log
				(customer_id, plan_name, date_range)
			VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month'))
			ON CONFLICT DO NOTHING
		SQL
	end

protected

	def charge_for_plan(customer_id)
		params = [
			customer_id,
			"#{customer_id}-bill-#{@plan[:name]}-at-#{Time.now.to_i}",
			-price
		]
		DB.exec(<<~SQL, params)
			INSERT INTO transactions
				(customer_id, transaction_id, created_at, amount)
			VALUES ($1, $2, LOCALTIMESTAMP, $3)
		SQL
	end

	def add_one_month_to_current_plan(customer_id)
		DB.exec(<<~SQL, [customer_id])
			UPDATE plan_log SET date_range=range_merge(
				date_range,
				tsrange(
					LOCALTIMESTAMP,
					GREATEST(upper(date_range), LOCALTIMESTAMP) + '1 month'
				)
			)
			WHERE
				customer_id=$1 AND
				date_range && tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month')
		SQL
	end
end



@@ 140,18 180,18 @@ class CreditCardGateway
	end

	def sale(ip:, **kwargs)
		return false unless decline_guard(ip)
		result = @gateway.transaction.sale(**kwargs)
		return true if result.success?
		return nil unless decline_guard(ip)
		tx = Transaction.sale(**kwargs)
		return tx if tx

		REDIS.incr("jmp_pay_decline-#{@customer_id}")
		REDIS.expire("jmp_pay_decline-#{@customer_id}", 60 * 60 * 24)
		REDIS.incr("jmp_pay_decline-#{ip}")
		REDIS.expire("jmp_pay_decline-#{ip}", 60 * 60 * 24)
		false
		nil
	end

	def buy_plan(plan_name, months, nonce, ip)
	def buy_plan(plan_name, nonce, ip)
		plan = Plan.for(plan_name)
		sale(
			ip: ip,


@@ 159,7 199,7 @@ class CreditCardGateway
			payment_method_nonce: nonce,
			merchant_account_id: plan.merchant_account,
			options: {submit_for_settlement: true}
		) && plan.activate(@customer_id, months)
		)&.insert && plan.bill_plan(@customer_id)
	end

protected


@@ 292,7 332,6 @@ class JmpPay < Roda
					result = DB.transaction do
						Plan.active?(gateway.customer_id) || gateway.buy_plan(
							request.params["plan_name"],
							5,
							request.params["braintree_nonce"],
							request.ip
						)

A lib/transaction.rb => lib/transaction.rb +70 -0
@@ 0,0 1,70 @@
# frozen_string_literal: true

require "bigdecimal"

# Largely copied from sgx-jmp to support web activation more properly
# Goes away when web activation goes away
class Transaction
	def self.sale(gateway, **kwargs)
		response = gateway.transaction.sale(**kwargs)
		response.success? ? new(response.transaction) : nil
	end

	attr_reader :amount

	def initialize(braintree_transaction)
		@customer_id = braintree_transaction.customer_details.id
		@transaction_id = braintree_transaction.id
		@created_at = braintree_transaction.created_at
		@amount = BigDecimal(braintree_transaction.amount, 4)
	end

	def insert
		DB.transaction do
			insert_tx
			insert_bonus
		end
		true
	end

	def bonus
		return BigDecimal(0) if amount <= 15
		amount *
			case amount
			when (15..29.99)
				0.01
			when (30..139.99)
				0.03
			else
				0.05
			end
	end

	def to_s
		plus = " + #{'%.4f' % bonus} bonus"
		"$#{'%.2f' % amount}#{plus if bonus.positive?}"
	end

protected

	def insert_tx
		params = [@customer_id, @transaction_id, @created_at, @amount]
		DB.exec(<<~SQL, params)
			INSERT INTO transactions
				(customer_id, transaction_id, created_at, amount, note)
			VALUES
				($1, $2, $3, $4, 'Credit card payment')
		SQL
	end

	def insert_bonus
		return if bonus <= 0
		params = [@customer_id, "bonus_for_#{@transaction_id}", @created_at, bonus]
		DB.exec(<<~SQL, params)
			INSERT INTO transactions
				(customer_id, transaction_id, created_at, amount, note)
			VALUES
				($1, $2, $3, $4, 'Credit card payment bonus')
		SQL
	end
end