transaction.rb

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