transaction.rb

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