transaction.rb

  1# frozen_string_literal: true
  2
  3require "bigdecimal"
  4
  5require_relative "trust_level_repo"
  6
  7class Transaction
  8	def self.sale(customer, amount:, payment_method: nil)
  9		resolve_payment_method(customer, payment_method, amount).then do |selected|
 10			BRAINTREE.transaction.sale(
 11				amount: amount,
 12				merchant_account_id: customer.merchant_account,
 13				options: { submit_for_settlement: true },
 14				payment_method_token: selected.token
 15			).then do |response|
 16				new(decline_guard(customer, response))
 17			end
 18		end
 19	end
 20
 21	def self.resolve_payment_method(customer, payment_method, amount)
 22		EMPromise.all([
 23			REDIS.exists("jmp_customer_credit_card_lock-#{customer.customer_id}"),
 24			TrustLevelRepo.new.find(customer), customer.declines,
 25			payment_method || customer.payment_methods.then(&:default_payment_method)
 26		]).then do |(lock, tl, declines, selected_method)|
 27			raise "Declined" unless tl.credit_card_transaction?(amount, declines)
 28			raise "Too many payments recently" if lock == 1
 29			raise "No valid payment method on file" unless selected_method
 30
 31			selected_method
 32		end
 33	end
 34
 35	def self.decline_guard(customer, response)
 36		if response.success?
 37			REDIS.setex(
 38				"jmp_customer_credit_card_lock-#{customer.customer_id}",
 39				60 * 60 * 24,
 40				"1"
 41			)
 42			return response.transaction
 43		end
 44
 45		customer.mark_decline
 46		raise response.message
 47	end
 48
 49	attr_reader :amount
 50
 51	def initialize(braintree_transaction)
 52		@customer_id = braintree_transaction.customer_details.id
 53		@transaction_id = braintree_transaction.id
 54		@created_at = braintree_transaction.created_at
 55		@amount = BigDecimal(braintree_transaction.amount, 4)
 56	end
 57
 58	def insert
 59		EM.promise_fiber do
 60			DB.transaction do
 61				insert_tx
 62				insert_bonus
 63			end
 64		end
 65	end
 66
 67	def total
 68		amount + bonus
 69	end
 70
 71	def bonus
 72		return BigDecimal(0) if amount <= 15
 73
 74		amount *
 75			case amount
 76			when (15..29.99)
 77				0.01
 78			when (30..139.99)
 79				0.03
 80			else
 81				0.05
 82			end
 83	end
 84
 85	def to_s
 86		plus = " + #{'%.4f' % bonus} bonus"
 87		"$#{'%.2f' % amount}#{plus if bonus.positive?}"
 88	end
 89
 90	def settled_after
 91		@created_at + (90 * 24 * 60 * 60)
 92	end
 93
 94protected
 95
 96	def insert_tx
 97		params = [
 98			@customer_id, @transaction_id, @created_at, settled_after, @amount
 99		]
100		DB.exec(<<~SQL, params)
101			INSERT INTO transactions
102				(customer_id, transaction_id, created_at, settled_after, amount, note)
103			VALUES
104				($1, $2, $3, $4, $5, 'Credit card payment')
105		SQL
106	end
107
108	def insert_bonus
109		return if bonus <= 0
110
111		params = [
112			@customer_id, "bonus_for_#{@transaction_id}", @created_at,
113			settled_after, bonus
114		]
115		DB.exec(<<~SQL, params)
116			INSERT INTO transactions
117				(customer_id, transaction_id, created_at, settled_after, amount, note)
118			VALUES
119				($1, $2, $3, $4, $5, 'Credit card payment bonus')
120		SQL
121	end
122end