mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-17 11:54:22 +01:00
handle percent coupons w/ stripe subscriptions
This commit is contained in:
parent
ffc3051444
commit
b8319a5124
@ -4,6 +4,7 @@
|
||||
- Refactored theme builder to use scss files
|
||||
- Updated stripe gem to 5.21.0
|
||||
- Architecture documentation
|
||||
- Improved coupon creation/deletion workflow
|
||||
- Fix a bug: unable to access embedded plan views
|
||||
- Fix a bug: warning message overflow in credit wallet modal
|
||||
- Fix a bug: when using a cash coupon, the amount shown in the statistics is invalid
|
||||
|
@ -5,7 +5,7 @@ class Coupon < ApplicationRecord
|
||||
has_many :invoices
|
||||
has_many :payment_schedule
|
||||
|
||||
after_commit :create_stripe_coupon, on: [:create]
|
||||
after_save :create_stripe_coupon, on: [:create]
|
||||
after_commit :delete_stripe_coupon, on: [:destroy]
|
||||
|
||||
validates :name, presence: true
|
||||
@ -94,7 +94,7 @@ class Coupon < ApplicationRecord
|
||||
private
|
||||
|
||||
def create_stripe_coupon
|
||||
StripeWorker.perform_async(:create_stripe_coupon, id)
|
||||
StripeService.create_stripe_coupon(id)
|
||||
end
|
||||
|
||||
def delete_stripe_coupon
|
||||
|
@ -52,7 +52,7 @@ class PaymentSchedule < PaymentDocument
|
||||
def post_save(setup_intent_id)
|
||||
return unless payment_method == 'stripe'
|
||||
|
||||
StripeService.create_stripe_subscription(id, setup_intent_id)
|
||||
StripeService.create_stripe_subscription(self, setup_intent_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -45,9 +45,7 @@ class Reservation < ApplicationRecord
|
||||
def generate_subscription
|
||||
return unless plan_id
|
||||
|
||||
self.subscription = Subscription.find_or_initialize_by(statistic_profile_id: statistic_profile_id)
|
||||
subscription.attributes = { plan_id: plan_id, statistic_profile_id: statistic_profile_id, expiration_date: nil }
|
||||
|
||||
self.subscription = Subscription.new(plan_id: plan_id, statistic_profile_id: statistic_profile_id, expiration_date: nil)
|
||||
subscription.init_save
|
||||
subscription
|
||||
end
|
||||
|
@ -20,8 +20,8 @@ class PaymentScheduleService
|
||||
ps = PaymentSchedule.new(scheduled: plan, total: price + other_items, coupon: coupon)
|
||||
deadlines = plan.duration / 1.month
|
||||
per_month = (price / deadlines).truncate
|
||||
adjustment = if per_month * deadlines != price
|
||||
price - (per_month * deadlines)
|
||||
adjustment = if per_month * deadlines + other_items.truncate != ps.total
|
||||
ps.total - (per_month * deadlines + other_items.truncate)
|
||||
else
|
||||
0
|
||||
end
|
||||
@ -30,9 +30,9 @@ class PaymentScheduleService
|
||||
date = DateTime.current + i.months
|
||||
details = { recurring: per_month }
|
||||
amount = if i.zero?
|
||||
details[:adjustment] = adjustment
|
||||
details[:other_items] = other_items
|
||||
per_month + adjustment + other_items
|
||||
details[:adjustment] = adjustment.truncate
|
||||
details[:other_items] = other_items.truncate
|
||||
per_month + adjustment.truncate + other_items.truncate
|
||||
else
|
||||
per_month
|
||||
end
|
||||
@ -48,7 +48,7 @@ class PaymentScheduleService
|
||||
|
||||
def create(subscription, total, coupon: nil, operator: nil, payment_method: nil, reservation: nil, user: nil, setup_intent_id: nil)
|
||||
subscription = reservation.generate_subscription if !subscription && reservation&.plan_id
|
||||
raise InvalidSubscriptionError unless subscription
|
||||
raise InvalidSubscriptionError unless subscription&.persisted?
|
||||
|
||||
schedule = compute(subscription.plan, total, coupon)
|
||||
ps = schedule[:payment_schedule]
|
||||
|
@ -25,16 +25,16 @@ class Reservations::Reserve
|
||||
operator_profile_id: operator_profile_id,
|
||||
user: user,
|
||||
payment_method: payment_method,
|
||||
coupon_code: payment_details[:coupon],
|
||||
coupon: payment_details[:coupon],
|
||||
setup_intent_id: intent_id)
|
||||
else
|
||||
generate_invoice(reservation, operator_profile_id, payment_details, intent_id)
|
||||
end
|
||||
WalletService.debit_user_wallet(payment, user, reservation)
|
||||
payment.save
|
||||
reservation.save
|
||||
payment.post_save(intent_id)
|
||||
reservation.post_save
|
||||
payment.save
|
||||
payment.post_save(intent_id)
|
||||
end
|
||||
true
|
||||
end
|
||||
@ -44,10 +44,9 @@ class Reservations::Reserve
|
||||
##
|
||||
# Generate the invoice for the given reservation+subscription
|
||||
##
|
||||
def generate_schedule(reservation: nil, total: nil, operator_profile_id: nil, user: nil, payment_method: nil, coupon_code: nil,
|
||||
def generate_schedule(reservation: nil, total: nil, operator_profile_id: nil, user: nil, payment_method: nil, coupon: nil,
|
||||
setup_intent_id: nil)
|
||||
operator = InvoicingProfile.find(operator_profile_id)&.user
|
||||
coupon = Coupon.find_by(code: coupon_code) unless coupon_code.nil?
|
||||
|
||||
PaymentScheduleService.new.create(
|
||||
nil,
|
||||
|
@ -5,9 +5,8 @@ class StripeService
|
||||
class << self
|
||||
|
||||
# Create the provided PaymentSchedule on Stripe, using the Subscription API
|
||||
def create_stripe_subscription(payment_schedule_id, setup_intent_id)
|
||||
def create_stripe_subscription(payment_schedule, setup_intent_id)
|
||||
stripe_key = Setting.get('stripe_secret_key')
|
||||
payment_schedule = PaymentSchedule.find(payment_schedule_id)
|
||||
first_item = payment_schedule.ordered_items.first
|
||||
|
||||
case payment_schedule.scheduled_type
|
||||
@ -35,8 +34,8 @@ class StripeService
|
||||
stp_subscription = Stripe::Subscription.create({
|
||||
customer: payment_schedule.invoicing_profile.user.stp_customer_id,
|
||||
cancel_at: subscription.expiration_date.to_i,
|
||||
promotion_code: payment_schedule.coupon&.code,
|
||||
add_invoice_items: items,
|
||||
coupon: payment_schedule.coupon&.code,
|
||||
items: [
|
||||
{ price: price[:id] }
|
||||
],
|
||||
@ -45,6 +44,25 @@ class StripeService
|
||||
payment_schedule.update_attributes(stp_subscription_id: stp_subscription.id)
|
||||
end
|
||||
|
||||
def create_stripe_coupon(coupon_id)
|
||||
coupon = Coupon.find(coupon_id)
|
||||
stp_coupon = {
|
||||
id: coupon.code,
|
||||
duration: coupon.validity_per_user
|
||||
}
|
||||
if coupon.type == 'percent_off'
|
||||
stp_coupon[:percent_off] = coupon.percent_off
|
||||
elsif coupon.type == 'amount_off'
|
||||
stp_coupon[:amount_off] = coupon.amount_off
|
||||
stp_coupon[:currency] = Setting.get('stripe_currency')
|
||||
end
|
||||
|
||||
stp_coupon[:redeem_by] = coupon.valid_until.to_i unless coupon.valid_until.nil?
|
||||
stp_coupon[:max_redemptions] = coupon.max_usages unless coupon.max_usages.nil?
|
||||
|
||||
Stripe::Coupon.create(stp_coupon, api_key: Setting.get('stripe_secret_key'))
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subscription_invoice_items(payment_schedule, subscription, first_item, reservable_stp_id)
|
||||
|
@ -24,13 +24,15 @@ class Subscriptions::Subscribe
|
||||
|
||||
ActiveRecord::Base.transaction do
|
||||
subscription.init_save
|
||||
raise InvalidSubscriptionError unless subscription&.persisted?
|
||||
|
||||
payment = if schedule
|
||||
generate_schedule(subscription: subscription,
|
||||
total: payment_details[:before_coupon],
|
||||
operator_profile_id: operator_profile_id,
|
||||
user: user,
|
||||
payment_method: payment_method,
|
||||
coupon_code: payment_details[:coupon],
|
||||
coupon: payment_details[:coupon],
|
||||
setup_intent_id: intent_id)
|
||||
else
|
||||
generate_invoice(subscription, operator_profile_id, payment_details, intent_id)
|
||||
@ -75,10 +77,9 @@ class Subscriptions::Subscribe
|
||||
##
|
||||
# Generate the invoice for the given subscription
|
||||
##
|
||||
def generate_schedule(subscription: nil, total: nil, operator_profile_id: nil, user: nil, payment_method: nil, coupon_code: nil,
|
||||
def generate_schedule(subscription: nil, total: nil, operator_profile_id: nil, user: nil, payment_method: nil, coupon: nil,
|
||||
setup_intent_id: nil)
|
||||
operator = InvoicingProfile.find(operator_profile_id)&.user
|
||||
coupon = Coupon.find_by(code: coupon_code) unless coupon_code.nil?
|
||||
|
||||
PaymentScheduleService.new.create(
|
||||
subscription,
|
||||
|
@ -21,28 +21,11 @@ class StripeWorker
|
||||
user.update_columns(stp_customer_id: customer.id)
|
||||
end
|
||||
|
||||
def create_stripe_coupon(coupon_id)
|
||||
coupon = Coupon.find(coupon_id)
|
||||
stp_coupon = {
|
||||
id: coupon.code,
|
||||
duration: coupon.validity_per_user
|
||||
}
|
||||
if coupon.type == 'percent_off'
|
||||
stp_coupon[:percent_off] = coupon.percent_off
|
||||
elsif coupon.type == 'amount_off'
|
||||
stp_coupon[:amount_off] = coupon.amount_off
|
||||
stp_coupon[:currency] = Setting.get('stripe_currency')
|
||||
end
|
||||
|
||||
stp_coupon[:redeem_by] = coupon.valid_until.to_i unless coupon.valid_until.nil?
|
||||
stp_coupon[:max_redemptions] = coupon.max_usages unless coupon.max_usages.nil?
|
||||
|
||||
Stripe::Coupon.create(stp_coupon, api_key: Setting.get('stripe_secret_key'))
|
||||
end
|
||||
|
||||
def delete_stripe_coupon(coupon_code)
|
||||
cpn = Stripe::Coupon.retrieve(coupon_code, api_key: Setting.get('stripe_secret_key'))
|
||||
cpn.delete
|
||||
rescue Stripe::InvalidRequestError => e
|
||||
STDERR.puts "WARNING: Unable to delete the coupon on Stripe: #{e}"
|
||||
end
|
||||
|
||||
def create_or_update_stp_product(class_name, id)
|
||||
|
Loading…
x
Reference in New Issue
Block a user