@@ 61,12 61,52 @@ class Plan
SQL
end
- def activate(customer_id, months)
- DB.exec_params(
- "INSERT INTO plan_log VALUES ($1, $2, tsrange($3, $4))",
- [customer_id, @plan[:name], Time.now, Date.today >> months]
- )
- true
+ def bill_plan(customer_id)
+ DB.transaction do
+ charge_for_plan(customer_id)
+ unless activate_plan_starting_now(customer_id)
+ add_one_month_to_current_plan(customer_id)
+ end
+ end
+ end
+
+ def activate_plan_starting_now(customer_id)
+ DB.exec(<<~SQL, [customer_id, @plan[:name]]).cmd_tuples.positive?
+ INSERT INTO plan_log
+ (customer_id, plan_name, date_range)
+ VALUES ($1, $2, tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month'))
+ ON CONFLICT DO NOTHING
+ SQL
+ end
+
+protected
+
+ def charge_for_plan(customer_id)
+ params = [
+ customer_id,
+ "#{customer_id}-bill-#{@plan[:name]}-at-#{Time.now.to_i}",
+ -price
+ ]
+ DB.exec(<<~SQL, params)
+ INSERT INTO transactions
+ (customer_id, transaction_id, created_at, amount)
+ VALUES ($1, $2, LOCALTIMESTAMP, $3)
+ SQL
+ end
+
+ def add_one_month_to_current_plan(customer_id)
+ DB.exec(<<~SQL, [customer_id])
+ UPDATE plan_log SET date_range=range_merge(
+ date_range,
+ tsrange(
+ LOCALTIMESTAMP,
+ GREATEST(upper(date_range), LOCALTIMESTAMP) + '1 month'
+ )
+ )
+ WHERE
+ customer_id=$1 AND
+ date_range && tsrange(LOCALTIMESTAMP, LOCALTIMESTAMP + '1 month')
+ SQL
end
end
@@ 140,18 180,18 @@ class CreditCardGateway
end
def sale(ip:, **kwargs)
- return false unless decline_guard(ip)
- result = @gateway.transaction.sale(**kwargs)
- return true if result.success?
+ return nil unless decline_guard(ip)
+ tx = Transaction.sale(**kwargs)
+ return tx if tx
REDIS.incr("jmp_pay_decline-#{@customer_id}")
REDIS.expire("jmp_pay_decline-#{@customer_id}", 60 * 60 * 24)
REDIS.incr("jmp_pay_decline-#{ip}")
REDIS.expire("jmp_pay_decline-#{ip}", 60 * 60 * 24)
- false
+ nil
end
- def buy_plan(plan_name, months, nonce, ip)
+ def buy_plan(plan_name, nonce, ip)
plan = Plan.for(plan_name)
sale(
ip: ip,
@@ 159,7 199,7 @@ class CreditCardGateway
payment_method_nonce: nonce,
merchant_account_id: plan.merchant_account,
options: {submit_for_settlement: true}
- ) && plan.activate(@customer_id, months)
+ )&.insert && plan.bill_plan(@customer_id)
end
protected
@@ 292,7 332,6 @@ class JmpPay < Roda
result = DB.transaction do
Plan.active?(gateway.customer_id) || gateway.buy_plan(
request.params["plan_name"],
- 5,
request.params["braintree_nonce"],
request.ip
)
@@ 0,0 1,70 @@
+# frozen_string_literal: true
+
+require "bigdecimal"
+
+# Largely copied from sgx-jmp to support web activation more properly
+# Goes away when web activation goes away
+class Transaction
+ def self.sale(gateway, **kwargs)
+ response = gateway.transaction.sale(**kwargs)
+ response.success? ? new(response.transaction) : nil
+ 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
+ DB.transaction do
+ insert_tx
+ insert_bonus
+ end
+ true
+ 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
+
+protected
+
+ def insert_tx
+ params = [@customer_id, @transaction_id, @created_at, @amount]
+ DB.exec(<<~SQL, params)
+ INSERT INTO transactions
+ (customer_id, transaction_id, created_at, amount, note)
+ VALUES
+ ($1, $2, $3, $4, 'Credit card payment')
+ SQL
+ end
+
+ def insert_bonus
+ return if bonus <= 0
+ params = [@customer_id, "bonus_for_#{@transaction_id}", @created_at, bonus]
+ DB.exec(<<~SQL, params)
+ INSERT INTO transactions
+ (customer_id, transaction_id, created_at, amount, note)
+ VALUES
+ ($1, $2, $3, $4, 'Credit card payment bonus')
+ SQL
+ end
+end