mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-21 15:54:22 +01:00
WIP: refactor the price computation system
This commit is contained in:
parent
26dfbef5e1
commit
e456ddc7c9
@ -19,13 +19,19 @@ class API::PayzenController < API::PaymentsController
|
|||||||
|
|
||||||
def create_payment
|
def create_payment
|
||||||
amount = card_amount
|
amount = card_amount
|
||||||
@id = PayZen::Helper.generate_ref(cart_items_params, params[:customer])
|
@id = PayZen::Helper.generate_ref(cart_items_params, params[:customer_id])
|
||||||
|
|
||||||
client = PayZen::Charge.new
|
client = PayZen::Charge.new
|
||||||
@result = client.create_payment(amount: amount[:amount],
|
@result = client.create_payment(amount: amount[:amount],
|
||||||
order_id: @id,
|
order_id: @id,
|
||||||
customer: PayZen::Helper.generate_customer(params[:customer_id]))
|
customer: PayZen::Helper.generate_customer(params[:customer_id]))
|
||||||
@result
|
end
|
||||||
|
|
||||||
|
def create_token
|
||||||
|
@id = PayZen::Helper.generate_ref(cart_items_params, params[:customer_id])
|
||||||
|
client = PayZen::Charge.new
|
||||||
|
@result = client.create_token(order_id: @id,
|
||||||
|
customer: PayZen::Helper.generate_customer(params[:customer_id]))
|
||||||
end
|
end
|
||||||
|
|
||||||
def check_hash
|
def check_hash
|
||||||
|
@ -7,22 +7,27 @@ import { CheckHashResponse, ConfirmPaymentResponse, CreatePaymentResponse, SdkTe
|
|||||||
export default class PayzenAPI {
|
export default class PayzenAPI {
|
||||||
|
|
||||||
static async chargeSDKTest(baseURL: string, username: string, password: string): Promise<SdkTestResponse> {
|
static async chargeSDKTest(baseURL: string, username: string, password: string): Promise<SdkTestResponse> {
|
||||||
const res: AxiosResponse = await apiClient.post('/api/payzen/sdk_test', { base_url: baseURL, username, password });
|
const res: AxiosResponse<SdkTestResponse> = await apiClient.post('/api/payzen/sdk_test', { base_url: baseURL, username, password });
|
||||||
return res?.data;
|
return res?.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async chargeCreatePayment(cartItems: CartItems, customer: User): Promise<CreatePaymentResponse> {
|
static async chargeCreatePayment(cartItems: CartItems, customer: User): Promise<CreatePaymentResponse> {
|
||||||
const res: AxiosResponse = await apiClient.post('/api/payzen/create_payment', { cart_items: cartItems, customer_id: customer.id });
|
const res: AxiosResponse<CreatePaymentResponse> = await apiClient.post('/api/payzen/create_payment', { cart_items: cartItems, customer_id: customer.id });
|
||||||
|
return res?.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
static async chargeCreateToken(cartItems: CartItems, customer: User): Promise<any> {
|
||||||
|
const res: AxiosResponse = await apiClient.post('/api/payzen/create_token', { cart_items: cartItems, customer_id: customer.id });
|
||||||
return res?.data;
|
return res?.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async checkHash(algorithm: string, hashKey: string, hash: string, data: string): Promise<CheckHashResponse> {
|
static async checkHash(algorithm: string, hashKey: string, hash: string, data: string): Promise<CheckHashResponse> {
|
||||||
const res: AxiosResponse = await apiClient.post('/api/payzen/check_hash', { algorithm, hash_key: hashKey, hash, data });
|
const res: AxiosResponse<CheckHashResponse> = await apiClient.post('/api/payzen/check_hash', { algorithm, hash_key: hashKey, hash, data });
|
||||||
return res?.data;
|
return res?.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async confirm(orderId: string, cartItems: CartItems): Promise<ConfirmPaymentResponse> {
|
static async confirm(orderId: string, cartItems: CartItems): Promise<ConfirmPaymentResponse> {
|
||||||
const res: AxiosResponse = await apiClient.post('/api/payzen/confirm_payment', { cart_items: cartItems, order_id: orderId });
|
const res: AxiosResponse<ConfirmPaymentResponse> = await apiClient.post('/api/payzen/confirm_payment', { cart_items: cartItems, order_id: orderId });
|
||||||
return res?.data;
|
return res?.data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,14 +15,17 @@ export interface IntentConfirmation {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export enum PaymentMethod {
|
export enum PaymentMethod {
|
||||||
Stripe = 'stripe',
|
Card = 'card',
|
||||||
Other = ''
|
Other = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CartItems {
|
export interface CartItems {
|
||||||
|
customer_id: number,
|
||||||
reservation?: Reservation,
|
reservation?: Reservation,
|
||||||
subscription?: SubscriptionRequest,
|
subscription?: SubscriptionRequest,
|
||||||
coupon_code?: string
|
coupon_code?: string,
|
||||||
|
payment_schedule?: boolean,
|
||||||
|
payment_method: PaymentMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface UpdateCardResponse {
|
export interface UpdateCardResponse {
|
||||||
|
@ -7,13 +7,10 @@ export interface ReservationSlot {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface Reservation {
|
export interface Reservation {
|
||||||
user_id: number,
|
|
||||||
reservable_id: number,
|
reservable_id: number,
|
||||||
reservable_type: string,
|
reservable_type: string,
|
||||||
slots_attributes: Array<ReservationSlot>,
|
slots_attributes: Array<ReservationSlot>,
|
||||||
plan_id?: number,
|
|
||||||
nb_reserve_places?: number,
|
nb_reserve_places?: number,
|
||||||
payment_schedule?: boolean,
|
|
||||||
tickets_attributes?: {
|
tickets_attributes?: {
|
||||||
event_price_category_id: number,
|
event_price_category_id: number,
|
||||||
booked: boolean,
|
booked: boolean,
|
||||||
|
@ -11,8 +11,5 @@ export interface Subscription {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export interface SubscriptionRequest {
|
export interface SubscriptionRequest {
|
||||||
plan_id: number,
|
plan_id: number
|
||||||
user_id: number,
|
|
||||||
payment_schedule: boolean,
|
|
||||||
payment_method: PaymentMethod
|
|
||||||
}
|
}
|
||||||
|
13
app/models/cart_item/base_item.rb
Normal file
13
app/models/cart_item/base_item.rb
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Items that can be added to the shopping cart
|
||||||
|
module CartItem; end
|
||||||
|
|
||||||
|
# This is an abstract class implemented by classes that can be added to the shopping cart
|
||||||
|
class CartItem::BaseItem
|
||||||
|
self.abstract_class = true
|
||||||
|
|
||||||
|
def price
|
||||||
|
{ elements: {}, amount: 0 }
|
||||||
|
end
|
||||||
|
end
|
26
app/models/cart_item/coupon.rb
Normal file
26
app/models/cart_item/coupon.rb
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# A discount coupon applied to the whole shopping cart
|
||||||
|
class CartItem::Coupon
|
||||||
|
|
||||||
|
# @param coupon {String|Coupon} may be nil or empty string if no coupons are applied
|
||||||
|
def initialize(customer, operator, coupon)
|
||||||
|
@customer = customer
|
||||||
|
@operator = operator
|
||||||
|
@coupon = coupon
|
||||||
|
end
|
||||||
|
|
||||||
|
def coupon
|
||||||
|
cs = CouponService.new
|
||||||
|
cs.validate(@coupon, @customer.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def price(cart_total = 0)
|
||||||
|
cs = CouponService.new
|
||||||
|
new_total = cs.apply(cart_total, coupon)
|
||||||
|
|
||||||
|
amount = new_total - cart_total
|
||||||
|
|
||||||
|
{ amount: amount, total_with_coupon: new_total, total_without_coupon: cart_total }
|
||||||
|
end
|
||||||
|
end
|
36
app/models/cart_item/event_reservation.rb
Normal file
36
app/models/cart_item/event_reservation.rb
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# An event reservation added to the shopping cart
|
||||||
|
class CartItem::EventReservation < CartItem::Reservation
|
||||||
|
# @param normal_tickets {Number} number of tickets at the normal price
|
||||||
|
# @param other_tickets {Array<{booked: Number, event_price_category_id: Number}>}
|
||||||
|
def initialize(customer, operator, event, slots, normal_tickets: 0, other_tickets: [])
|
||||||
|
raise TypeError unless event.class == Event
|
||||||
|
|
||||||
|
super(customer, operator, event, slots)
|
||||||
|
@normal_tickets = normal_tickets
|
||||||
|
@other_tickets = other_tickets
|
||||||
|
end
|
||||||
|
|
||||||
|
def price
|
||||||
|
amount = @reservable.amount * @normal_tickets
|
||||||
|
is_privileged = @operator.admin? || (@operator.manager? && @operator.id != @customer.id)
|
||||||
|
|
||||||
|
@other_tickets.each do |ticket|
|
||||||
|
amount += ticket[:booked] * EventPriceCategory.find(ticket[:event_price_category_id]).amount
|
||||||
|
end
|
||||||
|
|
||||||
|
elements = { slots: [] }
|
||||||
|
|
||||||
|
@slots.each do |slot|
|
||||||
|
amount += get_slot_price(amount,
|
||||||
|
slot,
|
||||||
|
is_privileged,
|
||||||
|
elements: elements,
|
||||||
|
is_division: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
{ elements: elements, amount: amount }
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
23
app/models/cart_item/machine_reservation.rb
Normal file
23
app/models/cart_item/machine_reservation.rb
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# A machine reservation added to the shopping cart
|
||||||
|
class CartItem::MachineReservation < CartItem::Reservation
|
||||||
|
# @param plan {Plan} a subscription bought at the same time of the reservation OR an already running subscription
|
||||||
|
# @param new_subscription {Boolean} true is new subscription is being bought at the same time of the reservation
|
||||||
|
def initialize(customer, operator, machine, slots, plan: nil, new_subscription: false)
|
||||||
|
raise TypeError unless machine.class == Machine
|
||||||
|
|
||||||
|
super(customer, operator, machine, slots)
|
||||||
|
@plan = plan
|
||||||
|
@new_subscription = new_subscription
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def credits
|
||||||
|
return 0 if @plan.nil?
|
||||||
|
|
||||||
|
machine_credit = @plan.machine_credits.find { |credit| credit.creditable_id == @reservable.id }
|
||||||
|
credits_hours(machine_credit, @new_subscription)
|
||||||
|
end
|
||||||
|
end
|
28
app/models/cart_item/payment_schedule.rb
Normal file
28
app/models/cart_item/payment_schedule.rb
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# A payment schedule applied to plan in the shopping cart
|
||||||
|
class CartItem::PaymentSchedule
|
||||||
|
def initialize(plan, coupon, requested)
|
||||||
|
raise TypeError unless coupon.class == CartItem::Coupon
|
||||||
|
|
||||||
|
@plan = plan
|
||||||
|
@coupon = coupon
|
||||||
|
@requested = requested
|
||||||
|
end
|
||||||
|
|
||||||
|
def schedule(total, total_without_coupon)
|
||||||
|
schedule = if @requested && @plan.monthly_payment
|
||||||
|
PaymentScheduleService.new.compute(@plan, total_without_coupon, coupon: @coupon.coupon)
|
||||||
|
else
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
|
||||||
|
total_amount = if schedule
|
||||||
|
schedule[:items][0].amount
|
||||||
|
else
|
||||||
|
total
|
||||||
|
end
|
||||||
|
|
||||||
|
{ schedule: schedule, total: total_amount }
|
||||||
|
end
|
||||||
|
end
|
81
app/models/cart_item/reservation.rb
Normal file
81
app/models/cart_item/reservation.rb
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
MINUTES_PER_HOUR = 60.0
|
||||||
|
SECONDS_PER_MINUTE = 60.0
|
||||||
|
|
||||||
|
GET_SLOT_PRICE_DEFAULT_OPTS = { has_credits: false, elements: nil, is_division: true }.freeze
|
||||||
|
|
||||||
|
# A generic reservation added to the shopping cart
|
||||||
|
class CartItem::Reservation < CartItem::BaseItem
|
||||||
|
def initialize(customer, operator, reservable, slots)
|
||||||
|
@customer = customer
|
||||||
|
@operator = operator
|
||||||
|
@reservable = reservable
|
||||||
|
@slots = slots
|
||||||
|
end
|
||||||
|
|
||||||
|
def price
|
||||||
|
base_amount = @reservable.prices.find_by(group_id: @customer.group_id, plan_id: @plan.try(:id)).amount
|
||||||
|
is_privileged = @operator.admin? || (@operator.manager? && @operator.id != @customer.id)
|
||||||
|
|
||||||
|
elements = { slots: [] }
|
||||||
|
amount = 0
|
||||||
|
|
||||||
|
hours_available = credits
|
||||||
|
@slots.each_with_index do |slot, index|
|
||||||
|
amount += get_slot_price(base_amount, slot, is_privileged, elements: elements, has_credits: (index < hours_available))
|
||||||
|
end
|
||||||
|
|
||||||
|
{ elements: elements, amount: amount }
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def credits
|
||||||
|
0
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Compute the price of a single slot, according to the base price and the ability for an admin
|
||||||
|
# to offer the slot.
|
||||||
|
# @param hourly_rate {Number} base price of a slot
|
||||||
|
# @param slot {Hash} Slot object
|
||||||
|
# @param is_privileged {Boolean} true if the current user has a privileged role (admin or manager)
|
||||||
|
# @param [options] {Hash} optional parameters, allowing the following options:
|
||||||
|
# - elements {Array} if provided the resulting price will be append into elements.slots
|
||||||
|
# - has_credits {Boolean} true if the user still has credits for the given slot, false if not provided
|
||||||
|
# - is_division {boolean} false if the slot covers a full availability, true if it is a subdivision (default)
|
||||||
|
# @return {Number} price of the slot
|
||||||
|
##
|
||||||
|
def get_slot_price(hourly_rate, slot, is_privileged, options = {})
|
||||||
|
options = GET_SLOT_PRICE_DEFAULT_OPTS.merge(options)
|
||||||
|
|
||||||
|
slot_rate = options[:has_credits] || (slot[:offered] && is_privileged) ? 0 : hourly_rate
|
||||||
|
real_price = if options[:is_division]
|
||||||
|
(slot_rate / MINUTES_PER_HOUR) * ((slot[:end_at].to_time - slot[:start_at].to_time) / SECONDS_PER_MINUTE)
|
||||||
|
else
|
||||||
|
slot_rate
|
||||||
|
end
|
||||||
|
|
||||||
|
unless options[:elements].nil?
|
||||||
|
options[:elements][:slots].push(
|
||||||
|
start_at: slot[:start_at],
|
||||||
|
price: real_price,
|
||||||
|
promo: (slot_rate != hourly_rate)
|
||||||
|
)
|
||||||
|
end
|
||||||
|
real_price
|
||||||
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# Compute the number of remaining hours in the users current credits (for machine or space)
|
||||||
|
##
|
||||||
|
def credits_hours(credits, new_plan_being_bought = false)
|
||||||
|
hours_available = credits.hours
|
||||||
|
unless new_plan_being_bought
|
||||||
|
user_credit = @customer.users_credits.find_by(credit_id: credits.id)
|
||||||
|
hours_available = credits.hours - user_credit.hours_used if user_credit
|
||||||
|
end
|
||||||
|
hours_available
|
||||||
|
end
|
||||||
|
end
|
23
app/models/cart_item/space_reservation.rb
Normal file
23
app/models/cart_item/space_reservation.rb
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# A space reservation added to the shopping cart
|
||||||
|
class CartItem::SpaceReservation < CartItem::Reservation
|
||||||
|
# @param plan {Plan} a subscription bought at the same time of the reservation OR an already running subscription
|
||||||
|
# @param new_subscription {Boolean} true is new subscription is being bought at the same time of the reservation
|
||||||
|
def initialize(customer, operator, space, slots, plan: nil, new_subscription: false)
|
||||||
|
raise TypeError unless space.class == Space
|
||||||
|
|
||||||
|
super(customer, operator, space, slots)
|
||||||
|
@plan = plan
|
||||||
|
@new_subscription = new_subscription
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def credits
|
||||||
|
return 0 if @plan.nil?
|
||||||
|
|
||||||
|
space_credit = @plan.space_credits.find { |credit| credit.creditable_id == @reservable.id }
|
||||||
|
credits_hours(space_credit, @new_subscription)
|
||||||
|
end
|
||||||
|
end
|
17
app/models/cart_item/subscription.rb
Normal file
17
app/models/cart_item/subscription.rb
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# A subscription added to the shopping cart
|
||||||
|
class CartItem::Subscription < CartItem::BaseItem
|
||||||
|
def initialize(plan)
|
||||||
|
raise TypeError unless plan.class == Plan
|
||||||
|
|
||||||
|
@plan = plan
|
||||||
|
end
|
||||||
|
|
||||||
|
def price
|
||||||
|
amount = @plan.amount
|
||||||
|
elements = { plan: amount }
|
||||||
|
|
||||||
|
{ elements: elements, amount: amount }
|
||||||
|
end
|
||||||
|
end
|
43
app/models/cart_item/training_reservation.rb
Normal file
43
app/models/cart_item/training_reservation.rb
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# A training reservation added to the shopping cart
|
||||||
|
class CartItem::TrainingReservation < CartItem::Reservation
|
||||||
|
# @param plan {Plan} a subscription bought at the same time of the reservation OR an already running subscription
|
||||||
|
# @param new_subscription {Boolean} true is new subscription is being bought at the same time of the reservation
|
||||||
|
def initialize(customer, operator, training, slots, plan: nil, new_subscription: false)
|
||||||
|
raise TypeError unless training.class == Training
|
||||||
|
|
||||||
|
super(customer, operator, training, slots)
|
||||||
|
@plan = plan
|
||||||
|
@new_subscription = new_subscription
|
||||||
|
end
|
||||||
|
|
||||||
|
def price
|
||||||
|
base_amount = @reservable.amount_by_group(@customer.group_id, plan_id: @plan.try(:id)).amount
|
||||||
|
is_privileged = @operator.admin? || (@operator.manager? && @operator.id != @customer.id)
|
||||||
|
|
||||||
|
elements = { slots: [] }
|
||||||
|
amount = 0
|
||||||
|
|
||||||
|
hours_available = credits
|
||||||
|
@slots.each do |slot|
|
||||||
|
amount += get_slot_price(base_amount,
|
||||||
|
slot,
|
||||||
|
is_privileged,
|
||||||
|
elements: elements,
|
||||||
|
has_credits: (@user.training_credits.size < hours_available),
|
||||||
|
is_division: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
{ elements: elements, amount: amount }
|
||||||
|
end
|
||||||
|
|
||||||
|
protected
|
||||||
|
|
||||||
|
def credits
|
||||||
|
return 0 if @plan.nil?
|
||||||
|
|
||||||
|
is_creditable = @plan.training_credits.select { |credit| credit.creditable_id == @reservable.id }.any?
|
||||||
|
is_creditable ? @plan.training_credit_nb : 0
|
||||||
|
end
|
||||||
|
end
|
44
app/models/shopping_cart.rb
Normal file
44
app/models/shopping_cart.rb
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Stores data about a shopping data
|
||||||
|
class ShoppingCart
|
||||||
|
attr_accessor :customer, :payment_method, :items, :coupon, :payment_schedule
|
||||||
|
|
||||||
|
# @param items {Array<CartItem::BaseItem>}
|
||||||
|
# @param coupon {CartItem::Coupon}
|
||||||
|
# @param payment_schedule {CartItem::PaymentSchedule}
|
||||||
|
# @param customer {User}
|
||||||
|
def initialize(customer, coupon, payment_method = '', items: [], payment_schedule: nil)
|
||||||
|
raise TypeError unless customer.class == User
|
||||||
|
|
||||||
|
@customer = customer
|
||||||
|
@payment_method = payment_method
|
||||||
|
@items = items
|
||||||
|
@coupon = coupon
|
||||||
|
@payment_schedule = payment_schedule
|
||||||
|
end
|
||||||
|
|
||||||
|
def total
|
||||||
|
total_amount = 0
|
||||||
|
all_elements = { slots: [] }
|
||||||
|
|
||||||
|
@items.map(&:price).each do |price|
|
||||||
|
total_amount += price[:amount]
|
||||||
|
all_elements.merge(price[:elements]) do |_key, old_val, new_val|
|
||||||
|
old_val | new_val
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
coupon_info = @coupon.price(total_amount)
|
||||||
|
schedule_info = @payment_schedule.schedule(coupon_info[:total_with_coupon], coupon_info[:total_without_coupon])
|
||||||
|
|
||||||
|
# return result
|
||||||
|
{
|
||||||
|
elements: all_elements,
|
||||||
|
total: schedule_info[:total].to_i,
|
||||||
|
before_coupon: coupon_info[:total_without_coupon].to_i,
|
||||||
|
coupon: @coupon.coupon,
|
||||||
|
schedule: schedule_info[:schedule]
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
83
app/services/cart_service.rb
Normal file
83
app/services/cart_service.rb
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# Provides methods for working with cart items
|
||||||
|
class CartService
|
||||||
|
def initialize(operator)
|
||||||
|
@operator = operator
|
||||||
|
end
|
||||||
|
|
||||||
|
def from_hash(cart_items)
|
||||||
|
items = []
|
||||||
|
plan_info = plan(cart_items)
|
||||||
|
|
||||||
|
@customer = User.find(cart_items[:customer_id])
|
||||||
|
|
||||||
|
items.push(CartItem::Subscription.new(plan_info[:plan])) if cart_items[:subscription]
|
||||||
|
items.push(reservable_from_hash(cart_items[:reservation], plan_info)) if cart_items[:reservation]
|
||||||
|
|
||||||
|
coupon = CartItem::Coupon.new(@customer, @operator, cart_items[:coupon_code])
|
||||||
|
schedule = CartItem::PaymentSchedule.new(plan_info[:plan], coupon, cart_items[:payment_schedule])
|
||||||
|
|
||||||
|
ShoppingCart.new(
|
||||||
|
@customer,
|
||||||
|
coupon,
|
||||||
|
cart_items[:payment_method],
|
||||||
|
items: items,
|
||||||
|
payment_schedule: schedule
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def plan(cart_items)
|
||||||
|
plan = if @customer.subscribed_plan
|
||||||
|
new_plan_being_bought = false
|
||||||
|
@customer.subscribed_plan
|
||||||
|
elsif cart_items[:subscription]
|
||||||
|
new_plan_being_bought = true
|
||||||
|
Plan.find(cart_items[:subscription][:plan_id])
|
||||||
|
else
|
||||||
|
new_plan_being_bought = false
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
{ plan: plan, new_subscription: new_plan_being_bought }
|
||||||
|
end
|
||||||
|
|
||||||
|
def reservable_from_hash(cart_item, plan_info)
|
||||||
|
return nil if cart_item[:reservable_id].blank?
|
||||||
|
|
||||||
|
reservable = cart_item[:reservable_type].constantize.find(cart_item[:reservable_id])
|
||||||
|
case reservable
|
||||||
|
when Machine
|
||||||
|
CartItem::MachineReservation.new(@customer,
|
||||||
|
@operator,
|
||||||
|
reservable,
|
||||||
|
cart_item[:slots_attributes],
|
||||||
|
plan: plan_info[:plan],
|
||||||
|
new_subscription: plan_info[:new_subscription])
|
||||||
|
when Training
|
||||||
|
CartItem::TrainingReservation.new(@customer,
|
||||||
|
@operator,
|
||||||
|
reservable,
|
||||||
|
cart_item[:slots_attributes],
|
||||||
|
plan: plan_info[:plan],
|
||||||
|
new_subscription: plan_info[:new_subscription])
|
||||||
|
when Event
|
||||||
|
CartItem::EventReservation.new(@customer,
|
||||||
|
@operator,
|
||||||
|
reservable,
|
||||||
|
cart_item[:slots_attributes],
|
||||||
|
normal_tickets: cart_item[:nb_reserve_places],
|
||||||
|
other_tickets: cart_item[:tickets_attributes])
|
||||||
|
when Space
|
||||||
|
CartItem::SpaceReservation.new(@customer,
|
||||||
|
@operator,
|
||||||
|
reservable,
|
||||||
|
cart_item[:slots_attributes],
|
||||||
|
plan: plan_info[:plan],
|
||||||
|
new_subscription: plan_info[:new_subscription])
|
||||||
|
else
|
||||||
|
raise NotImplementedError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
@ -12,17 +12,19 @@ class CouponService
|
|||||||
# @param user_id {Number} user's id against the coupon will be tested for usability
|
# @param user_id {Number} user's id against the coupon will be tested for usability
|
||||||
# @return {Number}
|
# @return {Number}
|
||||||
##
|
##
|
||||||
def apply(total, coupon, user_id = nil)
|
def apply(total, coupon = nil, user_id = nil)
|
||||||
price = total
|
price = total
|
||||||
|
|
||||||
coupon_object = nil
|
coupon_object = if coupon.instance_of? Coupon
|
||||||
if coupon.instance_of? Coupon
|
coupon
|
||||||
coupon_object = coupon
|
|
||||||
elsif coupon.instance_of? String
|
elsif coupon.instance_of? String
|
||||||
coupon_object = Coupon.find_by(code: coupon)
|
Coupon.find_by(code: coupon)
|
||||||
|
else
|
||||||
|
nil
|
||||||
end
|
end
|
||||||
|
|
||||||
unless coupon_object.nil?
|
return price if coupon_object.nil?
|
||||||
|
|
||||||
if coupon_object.status(user_id, total) == 'active'
|
if coupon_object.status(user_id, total) == 'active'
|
||||||
if coupon_object.type == 'percent_off'
|
if coupon_object.type == 'percent_off'
|
||||||
price -= (price * coupon_object.percent_off / 100.00).truncate
|
price -= (price * coupon_object.percent_off / 100.00).truncate
|
||||||
@ -31,7 +33,6 @@ class CouponService
|
|||||||
price -= coupon_object.amount_off if coupon_object.amount_off <= price
|
price -= coupon_object.amount_off if coupon_object.amount_off <= price
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
price
|
price
|
||||||
end
|
end
|
||||||
|
@ -184,6 +184,7 @@ Rails.application.routes.draw do
|
|||||||
post 'payzen/create_payment' => 'payzen#create_payment'
|
post 'payzen/create_payment' => 'payzen#create_payment'
|
||||||
post 'payzen/confirm_payment' => 'payzen#confirm_payment'
|
post 'payzen/confirm_payment' => 'payzen#confirm_payment'
|
||||||
post 'payzen/check_hash' => 'payzen#check_hash'
|
post 'payzen/check_hash' => 'payzen#check_hash'
|
||||||
|
post 'payzen/create_token' => 'payzen#create_token'
|
||||||
|
|
||||||
# FabAnalytics
|
# FabAnalytics
|
||||||
get 'analytics/data' => 'analytics#data'
|
get 'analytics/data' => 'analytics#data'
|
||||||
|
@ -32,5 +32,19 @@ class PayZen::Charge < PayZen::Client
|
|||||||
contrib: contrib,
|
contrib: contrib,
|
||||||
customer: customer)
|
customer: customer)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
##
|
||||||
|
# @see https://payzen.io/en-EN/rest/V4.0/api/playground/Charge/CreateToken/
|
||||||
|
##
|
||||||
|
def create_token(currency: Setting.get('payzen_currency'),
|
||||||
|
order_id: nil,
|
||||||
|
contrib: "fab-manager #{Version.current}",
|
||||||
|
customer: nil)
|
||||||
|
post('/Charge/CreateToken',
|
||||||
|
currency: currency,
|
||||||
|
orderId: order_id,
|
||||||
|
contrib: contrib,
|
||||||
|
customer: customer)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -52,6 +52,17 @@ class PayZen::Helper
|
|||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
## Generate a hash map compatible with PayZen 'V4/Customer/ShoppingCart'
|
||||||
|
def generate_shopping_cart(cart_items, customer)
|
||||||
|
{
|
||||||
|
cartItemInfo: cart_items.map do |type, value|
|
||||||
|
{
|
||||||
|
productAmount: item.
|
||||||
|
productType: customer.organization? ? 'SERVICE_FOR_BUSINESS' : 'SERVICE_FOR_INDIVIDUAL',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
## Check the PayZen signature for integrity
|
## Check the PayZen signature for integrity
|
||||||
def check_hash(algorithm, hash_key, hash_proof, data, key = nil)
|
def check_hash(algorithm, hash_key, hash_proof, data, key = nil)
|
||||||
supported_hash_algorithm = ['sha256_hmac']
|
supported_hash_algorithm = ['sha256_hmac']
|
||||||
|
Loading…
x
Reference in New Issue
Block a user