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