* wip
This commit is contained in:
parent
af2e681916
commit
5b578389e8
|
|
@ -8,14 +8,14 @@ ActiveAdmin.register JamRuby::User, :as => 'Students' do
|
|||
config.paginate = true
|
||||
|
||||
def booked_anything(scope)
|
||||
scope.joins(:student_lesson_bookings).uniq
|
||||
scope.joins(:student_lesson_bookings).where('lesson_bookings.status = ?' > LessonBooking::STATUS_APPROVED).uniq
|
||||
end
|
||||
|
||||
scope("Default", default: true) { |scope| booked_anything(scope).order('ready_for_session_at IS NULL DESC') }
|
||||
|
||||
index do
|
||||
column "Name" do |user|
|
||||
link_to teacher.user.name, "#{Rails.application.config.external_root_url}/client#/profile/#{user.id}"
|
||||
link_to user.name, "#{Rails.application.config.external_root_url}/client#/profile/#{user.id}"
|
||||
end
|
||||
column "Email" do |user|
|
||||
user.email
|
||||
|
|
|
|||
|
|
@ -27,6 +27,24 @@ CREATE TABLE lesson_bookings (
|
|||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE charges (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
amount NUMERIC(8,2) NOT NULL,
|
||||
type VARCHAR(64) NOT NULL,
|
||||
sent_billing_notices BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
sent_billing_notices_at TIMESTAMP,
|
||||
last_billing_attempt_at TIMESTAMP,
|
||||
billed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
billed_at TIMESTAMP,
|
||||
post_processed BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
post_processed_at TIMESTAMP,
|
||||
billing_error_reason VARCHAR,
|
||||
billing_error_detail VARCHAR,
|
||||
billing_should_retry BOOLEAN NOT NULL DEFAULT TRUE ,
|
||||
billing_attempts INTEGER NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE lesson_package_purchases (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
|
|
@ -132,4 +150,25 @@ ALTER TABLE users ADD COLUMN stripe_token VARCHAR(200);
|
|||
ALTER TABLE users ADD COLUMN stripe_customer_id VARCHAR(200);
|
||||
ALTER TABLE users ADD COLUMN stripe_zip_code VARCHAR(200);
|
||||
ALTER TABLE sales ADD COLUMN stripe_charge_id VARCHAR(200);
|
||||
ALTER TABLE sale_line_items ADD COLUMN lesson_package_purchase_id VARCHAR(64) REFERENCES lesson_package_purchases(id);
|
||||
ALTER TABLE sale_line_items ADD COLUMN lesson_package_purchase_id VARCHAR(64) REFERENCES lesson_package_purchases(id);
|
||||
|
||||
|
||||
CREATE TABLE teacher_payments (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
teacher_id VARCHAR(64) REFERENCES users(id) NOT NULL,
|
||||
charge_id VARCHAR(64) REFERENCES charges(id) NOT NULL,
|
||||
amount NUMERIC(8,2) NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE teacher_distributions (
|
||||
id VARCHAR(64) PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
teacher_id VARCHAR(64) REFERENCES users(id) NOT NULL,
|
||||
lesson_session_id VARCHAR(64) REFERENCES lesson_sessions(id),
|
||||
teacher_payment_id VARCHAR(64) REFERENCES teacher_payments(id),
|
||||
lesson_payment_purchase_id VARCHAR(64) REFERENCES lesson_package_purchases(id),
|
||||
amount_in_cents INTEGER NOT NULL,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
|
@ -281,6 +281,8 @@ require "jam_ruby/models/jamblaster_pairing_request"
|
|||
require "jam_ruby/models/sale_receipt_ios"
|
||||
require "jam_ruby/models/lesson_session_analyser"
|
||||
require "jam_ruby/models/lesson_session_monthly_price"
|
||||
require "jam_ruby/models/teacher_distribution"
|
||||
require "jam_ruby/models/charge"
|
||||
|
||||
include Jampb
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
module JamRuby
|
||||
class Charge
|
||||
validates :sent_notices, inclusion: {in: [true, false]}
|
||||
|
||||
def max_retries
|
||||
raise "not implemented"
|
||||
end
|
||||
def do_charge
|
||||
raise "not implemented"
|
||||
end
|
||||
|
||||
def bill_lesson
|
||||
|
||||
if !self.billed
|
||||
|
||||
# check if we can bill at the moment
|
||||
if last_billing_attempt_at && (24.hours.ago < last_billing_attempt_at)
|
||||
return
|
||||
end
|
||||
|
||||
if !billing_should_retry
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
# bill the user right now. if it fails, move on; will be tried again
|
||||
self.billing_attempts = self.billing_attempts + 1
|
||||
self.billing_should_retry = self.billing_attempts < max_retries
|
||||
self.last_billing_attempt_at = Time.now
|
||||
self.save(validate: false)
|
||||
|
||||
begin
|
||||
|
||||
do_charge
|
||||
|
||||
if sale.errors.any?
|
||||
self.billing_error_reason = 'sale_error'
|
||||
self.billing_error_detail = sale.errors.inspect
|
||||
line_item = sale.sale_line_items[0]
|
||||
if line_item && line_item.errors.any?
|
||||
self.billing_error_detail = "#{self.billing_error_detail}\n\n#{line_item.errors.inspect}"
|
||||
end
|
||||
self.save(validate: false)
|
||||
return false
|
||||
else
|
||||
self.billed = true
|
||||
self.billed_at = Time.now
|
||||
self.save(validate: false)
|
||||
end
|
||||
rescue Stripe::StripeError => e
|
||||
|
||||
stripe_handler(e)
|
||||
|
||||
subject = "Unable to charge user #{student.email} for lesson #{self.id} (stripe)"
|
||||
body = "teacher=#{teacher.email}\n\nbilling_error_reason=#{billing_error_reason}\n\nbilling_error_detail = #{billing_error_detail}"
|
||||
AdminMailer.alerts({subject: subject, body: body})
|
||||
UserMailer.student_unable_charge(self)
|
||||
return false
|
||||
rescue Exception => e
|
||||
subject = "Unable to charge user #{student.email} for lesson #{self.id} (unhandled)"
|
||||
body = "teacher=#{teacher.email}\n\nbilling_error_reason=#{billing_error_reason}\n\nbilling_error_detail = #{billing_error_detail}"
|
||||
AdminMailer.alerts({subject: subject, body: body})
|
||||
unhandled_handler(e)
|
||||
return false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if !self.sent_billing_notices
|
||||
# If the charge is successful, then we post the charge to the student’s payment history,
|
||||
# and associate the charge with the lesson, so that everyone knows the student has paid, and we send an email
|
||||
|
||||
UserMailer.student_lesson_normal_done(self).deliver
|
||||
UserMailer.teacher_lesson_normal_done(self).deliver
|
||||
|
||||
self.sent_billing_notices = true
|
||||
self.sent_billing_notices_at = Time.now
|
||||
self.post_processed = true
|
||||
self.post_processed_at = Time.now
|
||||
self.save(validate: false)
|
||||
end
|
||||
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
def unhandled_handler(e, reason = 'unhandled_exception')
|
||||
self.billing_error_reason = reason
|
||||
if e.cause
|
||||
self.billing_error_detail = e.cause.to_s + "\n" + e.cause.backtrace.join("\n\t") if e.cause.backtrace
|
||||
self.billing_error_detail << "\n\n"
|
||||
self.billing_error_detail << e.to_s + "\n" + e.backtrace.join("\n\t") if e.backtrace
|
||||
else
|
||||
self.billing_error_detail = e.to_s + "\n" + e.backtrace.join("\n\t") if e.backtrace
|
||||
end
|
||||
self.save(validate: :false)
|
||||
end
|
||||
|
||||
def is_card_declined?
|
||||
billed == false && billing_error_reason == 'card_declined'
|
||||
end
|
||||
|
||||
def is_card_expired?
|
||||
billed == false && billing_error_reason == 'card_expired'
|
||||
end
|
||||
|
||||
def last_billed_at_date
|
||||
last_billing_attempt_at.strftime("%B %d, %Y") if last_billing_attempt_at
|
||||
end
|
||||
def stripe_handler(e)
|
||||
|
||||
msg = e.to_s
|
||||
|
||||
if msg.include?('declined')
|
||||
self.billing_error_reason = 'card_declined'
|
||||
self.billing_error_detail = msg
|
||||
elsif msg.include?('expired')
|
||||
self.billing_error_reason = 'card_expired'
|
||||
self.billing_error_detail = msg
|
||||
elsif msg.include?('processing')
|
||||
self.billing_error_reason = 'processing_error'
|
||||
self.billing_error_detail = msg
|
||||
else
|
||||
self.billing_error_reason = 'stripe'
|
||||
self.billing_error_detail = msg
|
||||
end
|
||||
|
||||
self.save(validate: false)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
module JamRuby
|
||||
class TeacherPayment
|
||||
|
||||
belongs_to :teacher, class: "JamRuby::User"
|
||||
belongs_to :charge, class: "JamRuby::Charge"
|
||||
|
||||
validates :teacher, presence: true
|
||||
validates :charge, presence: true
|
||||
validates :amount, presence: true
|
||||
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
module JamRuby
|
||||
class TeacherPaymentCharge < Charge
|
||||
|
||||
has_one :teacher_payment, class: "JamRuby::TeacherDistribution"
|
||||
|
||||
def max_retries
|
||||
1
|
||||
end
|
||||
|
||||
def teacher
|
||||
@teacher ||= teacher_distribution.teacher
|
||||
end
|
||||
|
||||
def amount_in_cents
|
||||
amount * 100
|
||||
end
|
||||
|
||||
def uncollected_charges
|
||||
T.where(teacher_id: teacher.id).where(distributed: false).where(billed:true).where(type: 'JamRuby::StudentLessonCharge')
|
||||
end
|
||||
def do_charge
|
||||
|
||||
@uncollected_charges = uncollected_charges
|
||||
charge = Stripe::Charge.create(
|
||||
:amount => amount_in_cents,
|
||||
:currency => "usd",
|
||||
:source => APP_CONFIG.stripe_charge_token,
|
||||
:description => construct_description,
|
||||
:destination => teacher.stripe_account_id
|
||||
)
|
||||
|
||||
price_info = {}
|
||||
price_info[:subtotal_in_cents] = subtotal_in_cents
|
||||
price_info[:tax_in_cents] = tax_in_cents
|
||||
price_info[:total_in_cents] = total_in_cents
|
||||
price_info[:currency] = 'USD'
|
||||
price_info[:charge_id] = charge.id
|
||||
price_info[:change] = charge
|
||||
price_info[:purchase] = purchase
|
||||
price_info
|
||||
end
|
||||
|
||||
def construct_description
|
||||
teacher_distribution.
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
@ -166,6 +166,7 @@ if defined?(Bundler)
|
|||
|
||||
config.stripe_secret_key = 'sk_test_cPVRbtr9xbMiqffV8jwibwLA'
|
||||
config.stripe_publishable_key = 'pk_test_9vO8ZnxBpb9Udb0paruV3qLv'
|
||||
config.stripe_charge_token = '#XXX#'
|
||||
|
||||
if Rails.env == 'production'
|
||||
config.desk_url = 'https://jamkazam.desk.com'
|
||||
|
|
|
|||
Loading…
Reference in New Issue