1
0
mirror of https://github.com/LaCasemate/fab-manager.git synced 2025-04-11 01:02:34 +02:00

[bug] wallet usage is noted on subscription invoices even if wallet was not used + use payment confirm API for subscriptions only (sca)

This commit is contained in:
Sylvain 2019-09-10 17:57:46 +02:00
parent 4d0ac9b3ca
commit ac0489a496
7 changed files with 75 additions and 68 deletions

View File

@ -246,6 +246,14 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
// Used in wallet info template to interpolate some translations // Used in wallet info template to interpolate some translations
$scope.numberFilter = $filter('number'); $scope.numberFilter = $filter('number');
// Cart items
$scope.cartItems = {
coupon_code: ((coupon ? coupon.code : undefined)),
subscription: {
plan_id: selectedPlan.id
}
};
// retrieve the CGV // retrieve the CGV
CustomAsset.get({ name: 'cgv-file' }, function (cgv) { $scope.cgv = cgv.custom_asset; }); CustomAsset.get({ name: 'cgv-file' }, function (cgv) { $scope.cgv = cgv.custom_asset; });
@ -254,29 +262,8 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
* Handle the stripe's card tokenization process response and save the subscription to the API with the * Handle the stripe's card tokenization process response and save the subscription to the API with the
* card token just created. * card token just created.
*/ */
$scope.payment = function (status, response) { $scope.onPaymentSuccess = function (response) {
if (response.error) { $uibModalInstance.close(response);
growl.error(response.error.message);
} else {
$scope.attempting = true;
Subscription.save({
coupon_code: ((coupon ? coupon.code : undefined)),
subscription: {
plan_id: selectedPlan.id,
user_id: member.id,
card_token: response.id
}
}
, function (data) { // success
$uibModalInstance.close(data);
}
, function (data, status) { // failed
$scope.alerts = [];
$scope.alerts.push({ msg: _t('an_error_occured_during_the_payment_process_please_try_again_later'), type: 'danger' });
$scope.attempting = false;
}
);
}
}; };
} }
] ]
@ -367,7 +354,7 @@ Application.Controllers.controller('PlansIndexController', ['$scope', '$rootScop
$scope.cancel = function () { $uibModalInstance.dismiss('cancel'); }; $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
} }
] ]
}).result['finally'](null).then(function (reservation) { }).result['finally'](null).then(function (subscription) {
$scope.ctrl.member.subscribed_plan = angular.copy($scope.selectedPlan); $scope.ctrl.member.subscribed_plan = angular.copy($scope.selectedPlan);
Auth._currentUser.subscribed_plan = angular.copy($scope.selectedPlan); Auth._currentUser.subscribed_plan = angular.copy($scope.selectedPlan);
$scope.ctrl.member = null; $scope.ctrl.member = null;

View File

@ -19,28 +19,32 @@ class API::PaymentsController < API::ApiController
end end
# Compute the price # Compute the price
reservable = cart_items_params[:reservable_type].constantize.find(cart_items_params[:reservable_id]) if params[:cart_items][:reservation]
price_details = Price.compute(false, reservable = cart_items_params[:reservable_type].constantize.find(cart_items_params[:reservable_id])
current_user, price_details = Price.compute(false,
reservable, current_user,
cart_items_params[:slots_attributes] || [], reservable,
cart_items_params[:plan_id], cart_items_params[:slots_attributes] || [],
cart_items_params[:nb_reserve_places], cart_items_params[:plan_id],
cart_items_params[:tickets_attributes], cart_items_params[:nb_reserve_places],
coupon_params[:coupon_code]) cart_items_params[:tickets_attributes],
coupon_params[:coupon_code])
# Subtract wallet amount from total
total = price_details[:total]
wallet_debit = get_wallet_debit(current_user, total)
# Subtract wallet amount from total
total = price_details[:total]
wallet_debit = get_wallet_debit(current_user, total)
amount = total - wallet_debit
elsif params[:cart_items][:subscription]
amount = 2000 # TODO
end
# Create the PaymentIntent # Create the PaymentIntent
intent = Stripe::PaymentIntent.create( intent = Stripe::PaymentIntent.create(
payment_method: params[:payment_method_id], payment_method: params[:payment_method_id],
amount: total - wallet_debit, amount: amount,
currency: Rails.application.secrets.stripe_currency, currency: Rails.application.secrets.stripe_currency,
confirmation_method: 'manual', confirmation_method: 'manual',
confirm: true, confirm: true,
customer: current_user.stp_customer_id, customer: current_user.stp_customer_id
) )
elsif params[:payment_intent_id].present? elsif params[:payment_intent_id].present?
intent = Stripe::PaymentIntent.confirm(params[:payment_intent_id]) intent = Stripe::PaymentIntent.confirm(params[:payment_intent_id])
@ -48,20 +52,26 @@ class API::PaymentsController < API::ApiController
rescue Stripe::CardError => e rescue Stripe::CardError => e
# Display error on client # Display error on client
render(status: 200, json: { error: e.message }) and return render(status: 200, json: { error: e.message }) and return
rescue InvalidCouponError
render(json: { coupon_code: 'wrong coupon code or expired' }, status: :unprocessable_entity) and return
end
if intent.status == 'succeeded'
if params[:cart_items][:reservation]
render(on_reservation_success(intent)) and return
elsif params[:cart_items][:subscription]
render(on_subscription_success(intent)) and return
end
end end
render(on_payment_success(intent)) and return if intent.status == 'succeeded'
render generate_payment_response(intent) render generate_payment_response(intent)
end end
private private
def on_payment_success(intent) def on_reservation_success(intent)
# TODO create subscription is needed
user_id = params[:cart_items][:reservation][:user_id]
@reservation = Reservation.new(reservation_params) @reservation = Reservation.new(reservation_params)
is_reserve = Reservations::Reserve.new(user_id, current_user.invoicing_profile.id) is_reserve = Reservations::Reserve.new(current_user.id, current_user.invoicing_profile.id)
.pay_and_save(@reservation, coupon: coupon_params[:coupon_code], payment_intent_id: intent.id) .pay_and_save(@reservation, coupon: coupon_params[:coupon_code], payment_intent_id: intent.id)
Stripe::PaymentIntent.update( Stripe::PaymentIntent.update(
intent.id, intent.id,
@ -75,8 +85,23 @@ class API::PaymentsController < API::ApiController
else else
{ json: @reservation.errors, status: :unprocessable_entity } { json: @reservation.errors, status: :unprocessable_entity }
end end
rescue InvalidCouponError end
{ json: { coupon_code: 'wrong coupon code or expired' }, status: :unprocessable_entity }
def on_subscription_success(intent)
@subscription = Subscription.new(subscription_params)
is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, current_user.id)
.pay_and_save(@subscription, coupon: coupon_params[:coupon_code], invoice: true, payment_intent_id: intent.id)
Stripe::PaymentIntent.update(
intent.id,
description: "Invoice reference: #{@subscription.invoices.first.reference}"
)
if is_subscribe
{ template: 'api/subscriptions/show', status: :created, location: @subscription }
else
{ json: @subscription.errors, status: :unprocessable_entity }
end
end end
def generate_payment_response(intent) def generate_payment_response(intent)
@ -110,6 +135,10 @@ class API::PaymentsController < API::ApiController
slots_attributes: %i[id start_at end_at availability_id offered]) slots_attributes: %i[id start_at end_at availability_id offered])
end end
def subscription_params
params[:cart_items].require(:subscription).permit(:plan_id)
end
def cart_items_params def cart_items_params
params[:cart_items].require(:reservation).permit(:reservable_id, :reservable_type, :plan_id, :user_id, :nb_reserve_places, params[:cart_items].require(:reservation).permit(:reservable_id, :reservable_type, :plan_id, :user_id, :nb_reserve_places,
tickets_attributes: %i[event_price_category_id booked], tickets_attributes: %i[event_price_category_id booked],

View File

@ -57,7 +57,7 @@ class API::ReservationsController < API::ApiController
end end
def reservation_params def reservation_params
params.require(:reservation).permit(:message, :reservable_id, :reservable_type, :card_token, :plan_id, :nb_reserve_places, params.require(:reservation).permit(:message, :reservable_id, :reservable_type, :plan_id, :nb_reserve_places,
tickets_attributes: %i[event_price_category_id booked], tickets_attributes: %i[event_price_category_id booked],
slots_attributes: %i[id start_at end_at availability_id offered]) slots_attributes: %i[id start_at end_at availability_id offered])
end end

View File

@ -16,7 +16,7 @@ class API::SubscriptionsController < API::ApiController
@subscription = Subscription.new(subscription_params) @subscription = Subscription.new(subscription_params)
is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, user_id) is_subscribe = Subscriptions::Subscribe.new(current_user.invoicing_profile.id, user_id)
.pay_and_save(@subscription, coupon_params[:coupon_code], true) .pay_and_save(@subscription, coupon: coupon_params[:coupon_code], invoice: true)
if is_subscribe if is_subscribe
render :show, status: :created, location: @subscription render :show, status: :created, location: @subscription
@ -51,7 +51,7 @@ class API::SubscriptionsController < API::ApiController
# Never trust parameters from the scary internet, only allow the white list through. # Never trust parameters from the scary internet, only allow the white list through.
def subscription_params def subscription_params
params.require(:subscription).permit(:plan_id, :card_token) params.require(:subscription).permit(:plan_id)
end end
def coupon_params def coupon_params
@ -61,12 +61,4 @@ class API::SubscriptionsController < API::ApiController
def subscription_update_params def subscription_update_params
params.require(:subscription).permit(:expired_at) params.require(:subscription).permit(:expired_at)
end end
# TODO, refactor subscriptions logic and move this in model/validator
def valid_card_token?(token)
Stripe::Token.retrieve(token)
rescue Stripe::InvalidRequestError => e
@subscription.errors[:card_token] << e.message
false
end
end end

View File

@ -18,7 +18,7 @@ class Reservation < ActiveRecord::Base
validate :machine_not_already_reserved, if: -> { reservable.is_a?(Machine) } validate :machine_not_already_reserved, if: -> { reservable.is_a?(Machine) }
validate :training_not_fully_reserved, if: -> { reservable.is_a?(Training) } validate :training_not_fully_reserved, if: -> { reservable.is_a?(Training) }
attr_accessor :card_token, :plan_id, :subscription attr_accessor :plan_id, :subscription
after_commit :notify_member_create_reservation, on: :create after_commit :notify_member_create_reservation, on: :create
after_commit :notify_admin_member_create_reservation, on: :create after_commit :notify_admin_member_create_reservation, on: :create

View File

@ -13,8 +13,6 @@ class Subscription < ActiveRecord::Base
validates_presence_of :plan_id validates_presence_of :plan_id
validates_with SubscriptionGroupValidator validates_with SubscriptionGroupValidator
attr_accessor :card_token
# creation # creation
after_save :notify_member_subscribed_plan after_save :notify_member_subscribed_plan
after_save :notify_admin_subscribed_plan after_save :notify_admin_subscribed_plan
@ -22,7 +20,7 @@ class Subscription < ActiveRecord::Base
# @param invoice if true then only the subscription is payed, without reservation # @param invoice if true then only the subscription is payed, without reservation
# if false then the subscription is payed with reservation # if false then the subscription is payed with reservation
def save_with_payment(operator_profile_id, invoice = true, coupon_code = nil) def save_with_payment(operator_profile_id, invoice = true, coupon_code = nil, payment_intent_id = nil)
return false unless valid? return false unless valid?
set_expiration_date set_expiration_date
@ -35,7 +33,7 @@ class Subscription < ActiveRecord::Base
# debit wallet # debit wallet
wallet_transaction = debit_user_wallet wallet_transaction = debit_user_wallet
invoc = generate_invoice(operator_profile_id, coupon_code) invoc = generate_invoice(operator_profile_id, coupon_code, payment_intent_id)
if wallet_transaction if wallet_transaction
invoc.wallet_amount = @wallet_amount_debit invoc.wallet_amount = @wallet_amount_debit
invoc.wallet_transaction_id = wallet_transaction.id invoc.wallet_transaction_id = wallet_transaction.id
@ -45,7 +43,7 @@ class Subscription < ActiveRecord::Base
true true
end end
def generate_invoice(operator_profile_id, coupon_code = nil) def generate_invoice(operator_profile_id, coupon_code = nil, payment_intent_id = nil)
coupon_id = nil coupon_id = nil
total = plan.amount total = plan.amount
@ -65,7 +63,8 @@ class Subscription < ActiveRecord::Base
statistic_profile: user.statistic_profile, statistic_profile: user.statistic_profile,
total: total, total: total,
coupon_id: coupon_id, coupon_id: coupon_id,
operator_profile_id: operator_profile_id operator_profile_id: operator_profile_id,
stp_payment_intent_id: payment_intent_id
) )
invoice.invoice_items.push InvoiceItem.new( invoice.invoice_items.push InvoiceItem.new(
amount: plan.amount, amount: plan.amount,
@ -194,7 +193,7 @@ class Subscription < ActiveRecord::Base
end end
def debit_user_wallet def debit_user_wallet
return unless @wallet_amount_debit.present? || @wallet_amount_debit.zero? return if !@wallet_amount_debit.present? || @wallet_amount_debit.zero?
amount = @wallet_amount_debit / 100.0 amount = @wallet_amount_debit / 100.0
WalletService.new(user: user, wallet: user.wallet).debit(amount, self) WalletService.new(user: user, wallet: user.wallet).debit(amount, self)

View File

@ -9,11 +9,11 @@ class Subscriptions::Subscribe
@operator_profile_id = operator_profile_id @operator_profile_id = operator_profile_id
end end
def pay_and_save(subscription, coupon, invoice) def pay_and_save(subscription, coupon: nil, invoice: nil, payment_intent_id: nil)
return false if user_id.nil? return false if user_id.nil?
subscription.statistic_profile_id = StatisticProfile.find_by(user_id: user_id).id subscription.statistic_profile_id = StatisticProfile.find_by(user_id: user_id).id
subscription.save_with_payment(operator_profile_id, invoice, coupon) subscription.save_with_payment(operator_profile_id, invoice, coupon, payment_intent_id)
end end
def extend_subscription(subscription, new_expiration_date, free_days) def extend_subscription(subscription, new_expiration_date, free_days)