mirror of
https://github.com/LaCasemate/fab-manager.git
synced 2025-02-19 13:54:25 +01:00
create payment schedules on payzen
Also: make generic the creation of products on remote gateway Also: make generic the call to gateway specific actions
This commit is contained in:
parent
5f47624d4e
commit
e3187460ea
@ -61,11 +61,6 @@ class API::PayzenController < API::PaymentsController
|
||||
render json: e, status: :unprocessable_entity
|
||||
end
|
||||
|
||||
def confirm_payment_schedule
|
||||
# TODO
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def on_reservation_success(order_id, details)
|
||||
|
5
app/exceptions/payment_gateway_error.rb
Normal file
5
app/exceptions/payment_gateway_error.rb
Normal file
@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Raised when an an error occurred with any payment gateway
|
||||
class PaymentGatewayError < StandardError
|
||||
end
|
@ -1,6 +1,6 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Raised when an an error occurred with the PayZen payment gateway
|
||||
class PayzenError < StandardError
|
||||
class PayzenError < PaymentGatewayError
|
||||
end
|
||||
|
||||
|
@ -36,9 +36,4 @@ export default class PayzenAPI {
|
||||
const res: AxiosResponse<ConfirmPaymentResponse> = await apiClient.post('/api/payzen/confirm_payment', { cart_items: cartItems, order_id: orderId });
|
||||
return res?.data;
|
||||
}
|
||||
|
||||
static async confirmSchedule(orderId: string, cartItems: CartItems): Promise<any> {
|
||||
const res: AxiosResponse<any> = await apiClient.post('/api/payzen/confirm_payment_schedule', { cart_items: cartItems, order_id: orderId });
|
||||
return res?.data;
|
||||
}
|
||||
}
|
||||
|
@ -63,7 +63,7 @@ export const PayzenForm: React.FC<GatewayFormProps> = ({ onSubmit, onSuccess, on
|
||||
const transaction = event.clientAnswer.transactions[0];
|
||||
|
||||
if (event.clientAnswer.orderStatus === 'PAID') {
|
||||
confirm(event).then((confirmation) => {
|
||||
PayzenAPI.confirm(event.clientAnswer.orderDetails.orderId, cartItems).then((confirmation) => {
|
||||
PayZenKR.current.removeForms().then(() => {
|
||||
onSuccess(confirmation);
|
||||
});
|
||||
@ -77,17 +77,6 @@ export const PayzenForm: React.FC<GatewayFormProps> = ({ onSubmit, onSuccess, on
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Ask the API to confirm the processed transaction, depending on the current transaction (schedule or not).
|
||||
*/
|
||||
const confirm = async (paymentAnswer: ProcessPaymentAnswer): Promise<any> => {
|
||||
if (paymentSchedule) {
|
||||
return await PayzenAPI.confirm(paymentAnswer.clientAnswer.orderDetails.orderId, cartItems);
|
||||
} else {
|
||||
return await PayzenAPI.confirmSchedule(paymentAnswer.clientAnswer.orderDetails.orderId, cartItems);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback triggered when the PayZen form was entirely loaded and displayed
|
||||
* @see https://docs.lyra.com/fr/rest/V4.0/javascript/features/reference.html#%C3%89v%C3%A9nements
|
||||
|
@ -5,8 +5,8 @@ class Coupon < ApplicationRecord
|
||||
has_many :invoices
|
||||
has_many :payment_schedule
|
||||
|
||||
after_create :create_stripe_coupon
|
||||
after_commit :delete_stripe_coupon, on: [:destroy]
|
||||
after_create :create_gateway_coupon
|
||||
after_commit :delete_gateway_coupon, on: [:destroy]
|
||||
|
||||
validates :name, presence: true
|
||||
validates :code, presence: true
|
||||
@ -97,12 +97,12 @@ class Coupon < ApplicationRecord
|
||||
|
||||
private
|
||||
|
||||
def create_stripe_coupon
|
||||
StripeService.create_stripe_coupon(id)
|
||||
def create_gateway_coupon
|
||||
PaymentGatewayService.create_coupon(id)
|
||||
end
|
||||
|
||||
def delete_stripe_coupon
|
||||
StripeWorker.perform_async(:delete_stripe_coupon, code)
|
||||
def delete_gateway_coupon
|
||||
PaymentGatewayService.delete_coupon(id)
|
||||
end
|
||||
|
||||
end
|
||||
|
@ -163,7 +163,7 @@ class Invoice < PaymentDocument
|
||||
end
|
||||
|
||||
def paid_by_card?
|
||||
!payment_gateway_object.nil? || %w[stripe payzen].include?(payment_method)
|
||||
!payment_gateway_object.nil? && payment_method == 'card'
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -31,8 +31,8 @@ class Machine < ApplicationRecord
|
||||
|
||||
after_create :create_statistic_subtype
|
||||
after_create :create_machine_prices
|
||||
after_create :update_stripe_product
|
||||
after_update :update_stripe_product, if: :saved_change_to_name?
|
||||
after_create :update_gateway_product
|
||||
after_update :update_gateway_product, if: :saved_change_to_name?
|
||||
after_update :update_statistic_subtype, if: :saved_change_to_name?
|
||||
after_destroy :remove_statistic_subtype
|
||||
|
||||
@ -79,7 +79,7 @@ class Machine < ApplicationRecord
|
||||
|
||||
private
|
||||
|
||||
def update_stripe_product
|
||||
StripeWorker.perform_async(:create_or_update_stp_product, Machine.name, id)
|
||||
def update_gateway_product
|
||||
PaymentGatewayService.create_or_update_product(Machine.name, id)
|
||||
end
|
||||
end
|
||||
|
@ -70,10 +70,10 @@ class PaymentSchedule < PaymentDocument
|
||||
payment_schedule_items
|
||||
end
|
||||
|
||||
def post_save(setup_intent_id)
|
||||
def post_save(gateway_method_id)
|
||||
return unless payment_method == 'card'
|
||||
|
||||
StripeService.create_stripe_subscription(self, setup_intent_id)
|
||||
PaymentGatewayService.new.create_subscription(self, gateway_method_id)
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -24,8 +24,8 @@ class Plan < ApplicationRecord
|
||||
after_create :create_spaces_prices
|
||||
after_create :create_statistic_type
|
||||
after_create :set_name
|
||||
after_create :update_stripe_product
|
||||
after_update :update_stripe_product, if: :saved_change_to_base_name?
|
||||
after_create :update_gateway_product
|
||||
after_update :update_gateway_product, if: :saved_change_to_base_name?
|
||||
|
||||
validates :amount, :group, :base_name, presence: true
|
||||
validates :interval_count, numericality: { only_integer: true, greater_than_or_equal_to: 1 }
|
||||
@ -130,7 +130,7 @@ class Plan < ApplicationRecord
|
||||
update_columns(name: human_readable_name)
|
||||
end
|
||||
|
||||
def update_stripe_product
|
||||
StripeWorker.perform_async(:create_or_update_stp_product, Plan.name, id)
|
||||
def update_gateway_product
|
||||
PaymentGatewayService.create_or_update_product(Plan.name, id)
|
||||
end
|
||||
end
|
||||
|
@ -28,8 +28,8 @@ class Space < ApplicationRecord
|
||||
|
||||
after_create :create_statistic_subtype
|
||||
after_create :create_space_prices
|
||||
after_create :update_stripe_product
|
||||
after_update :update_stripe_product, if: :saved_change_to_name?
|
||||
after_create :update_gateway_product
|
||||
after_update :update_gateway_product, if: :saved_change_to_name?
|
||||
after_update :update_statistic_subtype, if: :saved_change_to_name?
|
||||
after_destroy :remove_statistic_subtype
|
||||
|
||||
@ -67,7 +67,7 @@ class Space < ApplicationRecord
|
||||
|
||||
private
|
||||
|
||||
def update_stripe_product
|
||||
StripeWorker.perform_async(:create_or_update_stp_product, Space.name, id)
|
||||
def update_gateway_product
|
||||
PaymentGatewayService.create_or_update_product(Space.name, id)
|
||||
end
|
||||
end
|
||||
|
@ -30,8 +30,8 @@ class Training < ApplicationRecord
|
||||
|
||||
after_create :create_statistic_subtype
|
||||
after_create :create_trainings_pricings
|
||||
after_create :update_stripe_product
|
||||
after_update :update_stripe_product, if: :saved_change_to_name?
|
||||
after_create :update_gateway_product
|
||||
after_update :update_gateway_product, if: :saved_change_to_name?
|
||||
after_update :update_statistic_subtype, if: :saved_change_to_name?
|
||||
after_destroy :remove_statistic_subtype
|
||||
|
||||
@ -69,7 +69,7 @@ class Training < ApplicationRecord
|
||||
end
|
||||
end
|
||||
|
||||
def update_stripe_product
|
||||
StripeWorker.perform_async(:create_or_update_stp_product, Training.name, id)
|
||||
def update_gateway_product
|
||||
PaymentGatewayService.create_or_update_product(Training.name, id)
|
||||
end
|
||||
end
|
||||
|
33
app/services/payment_gateway_service.rb
Normal file
33
app/services/payment_gateway_service.rb
Normal file
@ -0,0 +1,33 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# create remote items on currently active payment gateway
|
||||
class PaymentGatewayService
|
||||
def initialize
|
||||
@gateway = if Stripe::Helper.enabled?
|
||||
require 'stripe/service'
|
||||
Stripe::Service
|
||||
elsif PayZen::Helper.enabled?
|
||||
require 'pay_zen/service'
|
||||
PayZen::Service
|
||||
else
|
||||
require 'payment/service'
|
||||
Payment::Service
|
||||
end
|
||||
end
|
||||
|
||||
def create_subscription(payment_schedule, gateway_object_id)
|
||||
@gateway.create_subscription(payment_schedule, gateway_object_id)
|
||||
end
|
||||
|
||||
def create_coupon(coupon_id)
|
||||
@gateway.create_coupon(coupon_id)
|
||||
end
|
||||
|
||||
def delete_coupon(coupon_id)
|
||||
@gateway.delete_coupon(coupon_id)
|
||||
end
|
||||
|
||||
def create_or_update_product(klass, id)
|
||||
@gateway.create_or_update_product(klass, id)
|
||||
end
|
||||
end
|
@ -20,7 +20,7 @@ class PlansService
|
||||
{ errors: plan.errors }
|
||||
end
|
||||
end
|
||||
rescue Stripe::InvalidRequestError => e
|
||||
rescue PaymentGatewayError => e
|
||||
{ errors: e.message }
|
||||
end
|
||||
end
|
||||
|
@ -1,113 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Helpers and utilities for interactions with the Stripe payment gateway
|
||||
class StripeService
|
||||
class << self
|
||||
|
||||
# Create the provided PaymentSchedule on Stripe, using the Subscription API
|
||||
def create_stripe_subscription(payment_schedule, setup_intent_id)
|
||||
stripe_key = Setting.get('stripe_secret_key')
|
||||
first_item = payment_schedule.ordered_items.first
|
||||
|
||||
case payment_schedule.scheduled_type
|
||||
when Reservation.name
|
||||
subscription = payment_schedule.scheduled.subscription
|
||||
reservable_stp_id = payment_schedule.scheduled.reservable&.payment_gateway_object&.gateway_object_id
|
||||
when Subscription.name
|
||||
subscription = payment_schedule.scheduled
|
||||
reservable_stp_id = nil
|
||||
else
|
||||
raise InvalidSubscriptionError
|
||||
end
|
||||
|
||||
handle_wallet_transaction(payment_schedule)
|
||||
|
||||
# setup intent (associates the customer and the payment method)
|
||||
intent = Stripe::SetupIntent.retrieve(setup_intent_id, api_key: stripe_key)
|
||||
# subscription (recurring price)
|
||||
price = create_price(first_item.details['recurring'],
|
||||
subscription.plan.payment_gateway_object.gateway_object_id,
|
||||
nil, monthly: true)
|
||||
# other items (not recurring)
|
||||
items = subscription_invoice_items(payment_schedule, subscription, first_item, reservable_stp_id)
|
||||
|
||||
stp_subscription = Stripe::Subscription.create({
|
||||
customer: payment_schedule.invoicing_profile.user.payment_gateway_object.gateway_object_id,
|
||||
cancel_at: (payment_schedule.ordered_items.last.due_date + 3.day).to_i,
|
||||
add_invoice_items: items,
|
||||
coupon: payment_schedule.coupon&.code,
|
||||
items: [
|
||||
{ price: price[:id] }
|
||||
],
|
||||
default_payment_method: intent[:payment_method]
|
||||
}, { api_key: stripe_key })
|
||||
pgo = PaymentGatewayObject.new(item: payment_schedule)
|
||||
pgo.gateway_object = stp_subscription
|
||||
pgo.save!
|
||||
end
|
||||
|
||||
def create_stripe_coupon(coupon_id)
|
||||
coupon = Coupon.find(coupon_id)
|
||||
stp_coupon = { id: coupon.code }
|
||||
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[:duration] = coupon.validity_per_user == 'always' ? 'forever' : 'once'
|
||||
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)
|
||||
second_item = payment_schedule.ordered_items[1]
|
||||
|
||||
items = []
|
||||
if first_item.amount != second_item.amount
|
||||
unless first_item.details['adjustment']&.zero?
|
||||
# adjustment: when dividing the price of the plan / months, sometimes it forces us to round the amount per month.
|
||||
# The difference is invoiced here
|
||||
p1 = create_price(first_item.details['adjustment'],
|
||||
subscription.plan.payment_gateway_object.gateway_object_id,
|
||||
"Price adjustment for payment schedule #{payment_schedule.id}")
|
||||
items.push(price: p1[:id])
|
||||
end
|
||||
unless first_item.details['other_items']&.zero?
|
||||
# when taking a subscription at the same time of a reservation (space, machine or training), the amount of the
|
||||
# reservation is invoiced here.
|
||||
p2 = create_price(first_item.details['other_items'],
|
||||
reservable_stp_id,
|
||||
"Reservations for payment schedule #{payment_schedule.id}")
|
||||
items.push(price: p2[:id])
|
||||
end
|
||||
end
|
||||
|
||||
items
|
||||
end
|
||||
|
||||
def create_price(amount, stp_product_id, name, monthly: false)
|
||||
params = {
|
||||
unit_amount: amount,
|
||||
currency: Setting.get('stripe_currency'),
|
||||
product: stp_product_id,
|
||||
nickname: name
|
||||
}
|
||||
params[:recurring] = { interval: 'month', interval_count: 1 } if monthly
|
||||
|
||||
Stripe::Price.create(params, api_key: Setting.get('stripe_secret_key'))
|
||||
end
|
||||
|
||||
def handle_wallet_transaction(payment_schedule)
|
||||
return unless payment_schedule.wallet_amount
|
||||
|
||||
customer_id = payment_schedule.invoicing_profile.user.payment_gateway_object.gateway_object_id
|
||||
Stripe::Customer.update(customer_id, { balance: -payment_schedule.wallet_amount }, { api_key: Setting.get('stripe_secret_key') })
|
||||
end
|
||||
end
|
||||
end
|
@ -18,6 +18,7 @@ class PaymentScheduleItemWorker
|
||||
|
||||
def check_item(psi)
|
||||
# the following depends on the payment method (stripe/check)
|
||||
# FIXME
|
||||
if psi.payment_schedule.payment_method == 'card'
|
||||
### Stripe
|
||||
stripe_key = Setting.get('stripe_secret_key')
|
||||
|
@ -46,5 +46,27 @@ class PayZen::Charge < PayZen::Client
|
||||
contrib: contrib,
|
||||
customer: customer)
|
||||
end
|
||||
|
||||
##
|
||||
# @see https://payzen.io/fr-FR/rest/V4.0/api/playground/Charge/CreateSubscription
|
||||
##
|
||||
def create_subscription(amount: 0,
|
||||
currency: Setting.get('payzen_currency'),
|
||||
effect_date: DateTime.current.to_s,
|
||||
payment_method_token: nil,
|
||||
rrule: nil,
|
||||
order_id: nil,
|
||||
initial_amount: nil,
|
||||
initial_amount_number: nil)
|
||||
post('Charge/CreateSubscription,',
|
||||
amount: amount,
|
||||
currency: currency,
|
||||
effectDate: effect_date,
|
||||
paymentMethodToken: payment_method_token,
|
||||
rrule: rrule,
|
||||
orderId: order_id,
|
||||
initialAmount: initial_amount,
|
||||
initialAmountNumber: initial_amount_number)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -14,6 +14,4 @@ class PayZen::Order < PayZen::Client
|
||||
def get(order_id, operation_type: nil)
|
||||
post('/Order/Get/', orderId: order_id, operationType: operation_type)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
39
lib/pay_zen/service.rb
Normal file
39
lib/pay_zen/service.rb
Normal file
@ -0,0 +1,39 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'payment/service'
|
||||
require 'pay_zen/charge'
|
||||
require 'pay_zen/order'
|
||||
|
||||
# PayZen payement gateway
|
||||
module PayZen; end
|
||||
|
||||
## create remote objects on PayZen
|
||||
class PayZen::Service < Payment::Service
|
||||
def create_subscription(payment_schedule, order_id)
|
||||
first_item = payment_schedule.ordered_items.first
|
||||
|
||||
order = PayZen::Order.new.get(order_id: order_id, operation_type: 'VERIFICATION')
|
||||
client = PayZen::Charge.new
|
||||
|
||||
params = {
|
||||
amount: first_item.details['recurring'].to_i,
|
||||
effect_date: first_item.due_date.to_s,
|
||||
payment_method_token: order['answer']['transactions'].first['paymentMethodToken'],
|
||||
rrule: rrule(payment_schedule),
|
||||
order_id: order_id
|
||||
}
|
||||
unless first_item.details['adjustment']&.zero?
|
||||
params[:initial_amount] = first_item.amount
|
||||
params[:initial_amount_number] = 1
|
||||
end
|
||||
client.create_subscription(params)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def rrule(payment_schedule)
|
||||
count = payment_schedule.payment_schedule_items.count
|
||||
last = payment_schedule.ordered_items.last.due_date.strftime('%Y%m%d')
|
||||
"RRULE:FREQ=MONTHLY;COUNT=#{count};UNTIL=#{last}"
|
||||
end
|
||||
end
|
17
lib/pay_zen/subscription.rb
Normal file
17
lib/pay_zen/subscription.rb
Normal file
@ -0,0 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'pay_zen/client'
|
||||
|
||||
# Subscription/* endpoints of the PayZen REST API
|
||||
class PayZen::Subscription < PayZen::Client
|
||||
def initialize(base_url: nil, username: nil, password: nil)
|
||||
super(base_url: base_url, username: username, password: password)
|
||||
end
|
||||
|
||||
##
|
||||
# @see https://payzen.io/fr-FR/rest/V4.0/api/playground/Subscription/Get/
|
||||
##
|
||||
def get(subscription_id, payment_method_token)
|
||||
post('/Subscription/Get/', subscriptionId: subscription_id, paymentMethodToken: payment_method_token)
|
||||
end
|
||||
end
|
@ -14,6 +14,4 @@ class PayZen::Token < PayZen::Client
|
||||
def get(payment_method_token)
|
||||
post('/Token/Get/', paymentMethodToken: payment_method_token)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
16
lib/payment/service.rb
Normal file
16
lib/payment/service.rb
Normal file
@ -0,0 +1,16 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
# Payments module
|
||||
module Payment; end
|
||||
|
||||
# Abstract class that must be implemented by each payment gateway.
|
||||
# Provides methods to create remote objects on the payment gateway
|
||||
class Payment::Service
|
||||
def create_subscription(_payment_schedule, _gateway_object_id); end
|
||||
|
||||
def create_coupon(_coupon_id); end
|
||||
|
||||
def delete_coupon(_coupon_id); end
|
||||
|
||||
def create_or_update_product(_klass, _id); end
|
||||
end
|
126
lib/stripe/service.rb
Normal file
126
lib/stripe/service.rb
Normal file
@ -0,0 +1,126 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require 'payment/service'
|
||||
|
||||
# Stripe payement gateway
|
||||
module Stripe; end
|
||||
|
||||
## create remote objects on stripe
|
||||
class Stripe::Service < Payment::Service
|
||||
# Create the provided PaymentSchedule on Stripe, using the Subscription API
|
||||
def create_subscription(payment_schedule, setup_intent_id)
|
||||
stripe_key = Setting.get('stripe_secret_key')
|
||||
first_item = payment_schedule.ordered_items.first
|
||||
|
||||
case payment_schedule.scheduled_type
|
||||
when Reservation.name
|
||||
subscription = payment_schedule.scheduled.subscription
|
||||
reservable_stp_id = payment_schedule.scheduled.reservable&.payment_gateway_object&.gateway_object_id
|
||||
when Subscription.name
|
||||
subscription = payment_schedule.scheduled
|
||||
reservable_stp_id = nil
|
||||
else
|
||||
raise InvalidSubscriptionError
|
||||
end
|
||||
|
||||
handle_wallet_transaction(payment_schedule)
|
||||
|
||||
# setup intent (associates the customer and the payment method)
|
||||
intent = Stripe::SetupIntent.retrieve(setup_intent_id, api_key: stripe_key)
|
||||
# subscription (recurring price)
|
||||
price = create_price(first_item.details['recurring'],
|
||||
subscription.plan.payment_gateway_object.gateway_object_id,
|
||||
nil, monthly: true)
|
||||
# other items (not recurring)
|
||||
items = subscription_invoice_items(payment_schedule, subscription, first_item, reservable_stp_id)
|
||||
|
||||
stp_subscription = Stripe::Subscription.create({
|
||||
customer: payment_schedule.invoicing_profile.user.payment_gateway_object.gateway_object_id,
|
||||
cancel_at: (payment_schedule.ordered_items.last.due_date + 3.day).to_i,
|
||||
add_invoice_items: items,
|
||||
coupon: payment_schedule.coupon&.code,
|
||||
items: [
|
||||
{ price: price[:id] }
|
||||
],
|
||||
default_payment_method: intent[:payment_method]
|
||||
}, { api_key: stripe_key })
|
||||
pgo = PaymentGatewayObject.new(item: payment_schedule)
|
||||
pgo.gateway_object = stp_subscription
|
||||
pgo.save!
|
||||
end
|
||||
|
||||
def create_coupon(coupon_id)
|
||||
coupon = Coupon.find(coupon_id)
|
||||
stp_coupon = { id: coupon.code }
|
||||
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[:duration] = coupon.validity_per_user == 'always' ? 'forever' : 'once'
|
||||
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_coupon(coupon_id)
|
||||
coupon = Coupon.find(coupon_id)
|
||||
StripeWorker.perform_async(:delete_stripe_coupon, coupon.code)
|
||||
end
|
||||
|
||||
def create_or_update_product(klass, id)
|
||||
StripeWorker.perform_async(:create_or_update_stp_product, klass, id)
|
||||
rescue Stripe::InvalidRequestError => e
|
||||
raise PaymentGatewayError(e)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def subscription_invoice_items(payment_schedule, subscription, first_item, reservable_stp_id)
|
||||
second_item = payment_schedule.ordered_items[1]
|
||||
|
||||
items = []
|
||||
if first_item.amount != second_item.amount
|
||||
unless first_item.details['adjustment']&.zero?
|
||||
# adjustment: when dividing the price of the plan / months, sometimes it forces us to round the amount per month.
|
||||
# The difference is invoiced here
|
||||
p1 = create_price(first_item.details['adjustment'],
|
||||
subscription.plan.payment_gateway_object.gateway_object_id,
|
||||
"Price adjustment for payment schedule #{payment_schedule.id}")
|
||||
items.push(price: p1[:id])
|
||||
end
|
||||
unless first_item.details['other_items']&.zero?
|
||||
# when taking a subscription at the same time of a reservation (space, machine or training), the amount of the
|
||||
# reservation is invoiced here.
|
||||
p2 = create_price(first_item.details['other_items'],
|
||||
reservable_stp_id,
|
||||
"Reservations for payment schedule #{payment_schedule.id}")
|
||||
items.push(price: p2[:id])
|
||||
end
|
||||
end
|
||||
|
||||
items
|
||||
end
|
||||
|
||||
def create_price(amount, stp_product_id, name, monthly: false)
|
||||
params = {
|
||||
unit_amount: amount,
|
||||
currency: Setting.get('stripe_currency'),
|
||||
product: stp_product_id,
|
||||
nickname: name
|
||||
}
|
||||
params[:recurring] = { interval: 'month', interval_count: 1 } if monthly
|
||||
|
||||
Stripe::Price.create(params, api_key: Setting.get('stripe_secret_key'))
|
||||
end
|
||||
|
||||
def handle_wallet_transaction(payment_schedule)
|
||||
return unless payment_schedule.wallet_amount
|
||||
|
||||
customer_id = payment_schedule.invoicing_profile.user.payment_gateway_object.gateway_object_id
|
||||
Stripe::Customer.update(customer_id, { balance: -payment_schedule.wallet_amount }, { api_key: Setting.get('stripe_secret_key') })
|
||||
end
|
||||
end
|
Loading…
x
Reference in New Issue
Block a user