# frozen_string_literal: true require "bigdecimal" require_relative "trust_level_repo" class Transaction def self.sale(customer, amount:, payment_method: nil) resolve_payment_method(customer, payment_method, amount).then do |selected| BRAINTREE.transaction.sale( amount: amount, merchant_account_id: customer.merchant_account, options: { submit_for_settlement: true }, payment_method_token: selected.token ).then do |response| new(decline_guard(customer, response)) end end end def self.resolve_payment_method(customer, payment_method, amount) EMPromise.all([ REDIS.exists("jmp_customer_credit_card_lock-#{customer.customer_id}"), TrustLevelRepo.new.find(customer), customer.declines, payment_method || customer.payment_methods.then(&:default_payment_method) ]).then do |(lock, tl, declines, selected_method)| raise "Declined" unless tl.credit_card_transaction?(amount, declines) raise "Too many payments recently" if lock == 1 raise "No valid payment method on file" unless selected_method selected_method end end def self.decline_guard(customer, response) if response.success? REDIS.setex( "jmp_customer_credit_card_lock-#{customer.customer_id}", 60 * 60 * 24, "1" ) return response.transaction end customer.mark_decline raise response.message end attr_reader :amount def initialize(braintree_transaction) @customer_id = braintree_transaction.customer_details.id @transaction_id = braintree_transaction.id @created_at = braintree_transaction.created_at @amount = BigDecimal(braintree_transaction.amount, 4) end def insert EM.promise_fiber do DB.transaction do insert_tx insert_bonus end end end def total amount + bonus end def bonus return BigDecimal(0) if amount <= 15 amount * case amount when (15..29.99) 0.01 when (30..139.99) 0.03 else 0.05 end end def to_s plus = " + #{'%.4f' % bonus} bonus" "$#{'%.2f' % amount}#{plus if bonus.positive?}" end def settled_after @created_at + (90 * 24 * 60 * 60) end protected def insert_tx params = [ @customer_id, @transaction_id, @created_at, settled_after, @amount ] DB.exec(<<~SQL, params) INSERT INTO transactions (customer_id, transaction_id, created_at, settled_after, amount, note) VALUES ($1, $2, $3, $4, $5, 'Credit card payment') SQL end def insert_bonus return if bonus <= 0 params = [ @customer_id, "bonus_for_#{@transaction_id}", @created_at, settled_after, bonus ] DB.exec(<<~SQL, params) INSERT INTO transactions (customer_id, transaction_id, created_at, settled_after, amount, note) VALUES ($1, $2, $3, $4, $5, 'Credit card payment bonus') SQL end end