1
0
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:
Sylvain 2020-12-30 10:16:39 +01:00
parent ffc3051444
commit b8319a5124
9 changed files with 42 additions and 42 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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]

View File

@ -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,

View File

@ -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)

View File

@ -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,

View File

@ -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)