transaction.rb

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