diff --git a/.rubocop.yml b/.rubocop.yml index 2eca3bfc2..c5b1a7062 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -18,6 +18,7 @@ Metrics/BlockLength: - 'app/pdfs/pdf/*.rb' - 'test/**/*.rb' Metrics/ParameterLists: + Max: 6 CountKeywordArgs: false Style/BracesAroundHashParameters: EnforcedStyle: context_dependent diff --git a/app/controllers/api/stripe_controller.rb b/app/controllers/api/stripe_controller.rb index e29e06a7d..f771f0435 100644 --- a/app/controllers/api/stripe_controller.rb +++ b/app/controllers/api/stripe_controller.rb @@ -85,12 +85,7 @@ class API::StripeController < API::PaymentsController stp_subscription = service.subscribe(method.id, cart) res = on_payment_success(stp_subscription, cart) if %w[active not_started].include?(stp_subscription&.status) - intent = if stp_subscription.object == 'subscription_schedule' - nil - elsif stp_subscription.object == 'subscription' - stp_subscription.latest_invoice&.payment_intent - end - render generate_payment_response(intent, 'subscription', res, stp_subscription.id) + render generate_payment_response(stp_subscription.try(:latest_invoice)&.payment_intent, 'subscription', res, stp_subscription.id) end def confirm_subscription diff --git a/app/controllers/api/subscriptions_controller.rb b/app/controllers/api/subscriptions_controller.rb index 51113283e..753fbca06 100644 --- a/app/controllers/api/subscriptions_controller.rb +++ b/app/controllers/api/subscriptions_controller.rb @@ -2,28 +2,13 @@ # API Controller for resources of type Subscription class API::SubscriptionsController < API::ApiController - before_action :set_subscription, only: %i[show payment_details edit update destroy] + before_action :set_subscription, only: %i[show payment_details] before_action :authenticate_user! def show authorize @subscription end - def update - authorize @subscription - - res = Subscriptions::Subscribe.new(current_user.invoicing_profile.id) - .extend_subscription(@subscription, subscription_update_params[:expired_at]) - if res.is_a?(Subscription) - @subscription = res - render status: :created - elsif res - render status: :ok - else - render status: :unprocessable_entity - end - end - def payment_details authorize @subscription end @@ -34,8 +19,4 @@ class API::SubscriptionsController < API::ApiController def set_subscription @subscription = Subscription.find(params[:id]) end - - def subscription_update_params - params.require(:subscription).permit(:expired_at) - end end diff --git a/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx b/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx index 1791c46a4..6f779bcea 100644 --- a/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx +++ b/app/frontend/src/javascript/components/payment/local-payment/local-payment-form.tsx @@ -1,4 +1,4 @@ -import React, { FormEvent, useState } from 'react'; +import React, { FormEvent, useEffect, useState } from 'react'; import Select from 'react-select'; import { useTranslation } from 'react-i18next'; import { GatewayFormProps } from '../abstract-payment-modal'; @@ -30,6 +30,14 @@ export const LocalPaymentForm: React.FC = ({ onSubmit, onSucce const [method, setMethod] = useState('check'); const [onlinePaymentModal, setOnlinePaymentModal] = useState(false); + useEffect(() => { + if (cart.payment_method === PaymentMethod.Card) { + setMethod('card'); + } else { + setMethod('check'); + } + }, [cart]); + /** * Open/closes the online payment modal, used to collect card credentials when paying the payment schedule by card. */ @@ -112,7 +120,7 @@ export const LocalPaymentForm: React.FC = ({ onSubmit, onSucce className="method-select" onChange={handleUpdateMethod} options={buildMethodOptions()} - defaultValue={methodToOption(method)} /> + value={methodToOption(method)} /> {method === 'card' &&

{t('app.admin.local_payment.card_collection_info')}

} {method === 'check' &&

{t('app.admin.local_payment.check_collection_info', { DEADLINES: paymentSchedule.items.length })}

} diff --git a/app/frontend/src/javascript/controllers/admin/members.js b/app/frontend/src/javascript/controllers/admin/members.js index 0777c5bdd..a9a4a682f 100644 --- a/app/frontend/src/javascript/controllers/admin/members.js +++ b/app/frontend/src/javascript/controllers/admin/members.js @@ -793,79 +793,6 @@ Application.Controllers.controller('EditMemberController', ['$scope', '$state', growl.error(message); }; - /** - * Open a modal dialog, allowing the admin to extend the current user's subscription (freely or not) - * @param subscription {Object} User's subscription object - * @param free {boolean} True if the extent is offered, false otherwise - */ - $scope.updateSubscriptionModal = function (subscription, free) { - const modalInstance = $uibModal.open({ - animation: true, - templateUrl: '/admin/subscriptions/expired_at_modal.html', - size: 'lg', - resolve: { - paymentDetails () { - return Subscription.payment_details({ id: subscription.id }).$promise; - } - }, - controller: ['$scope', '$uibModalInstance', 'Subscription', 'paymentDetails', function ($scope, $uibModalInstance, Subscription, paymentDetails) { - /* PUBLIC SCOPE */ - - $scope.expire_at = subscription.expired_at; - $scope.new_expired_at = new Date(subscription.expired_at); - $scope.days = 0; - $scope.payment_details = paymentDetails; - $scope.datePicker = { - opened: false, - format: Fablab.uibDateFormat, - options: { - startingDay: Fablab.weekStartingDay - }, - minDate: new Date() - }; - - $scope.openDatePicker = function (ev) { - ev.preventDefault(); - ev.stopPropagation(); - return $scope.datePicker.opened = true; - }; - - $scope.ok = function () { - Subscription.update( - { id: subscription.id }, - { subscription: { expired_at: $scope.new_expired_at, free } }, - function (_subscription) { - growl.success(_t('app.admin.members_edit.you_successfully_changed_the_expiration_date_of_the_user_s_subscription')); - return $uibModalInstance.close(_subscription); - }, - function (error) { - growl.error(_t('app.admin.members_edit.a_problem_occurred_while_saving_the_date')); - console.error(error); - } - ); - }; - - $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); }; - - /* PRIVATE SCOPE */ - - /** - * Kind of constructor: these actions will be realized first when the controller is loaded - */ - function initialize () { - if (!free) { - $scope.new_expired_at = moment($scope.expire_at).add(subscription.plan.interval_count, subscription.plan.interval).toDate(); - } - } - - // !!! MUST BE CALLED AT THE END of the controller - initialize(); - }] - }); - // once the form was validated successfully ... - return modalInstance.result.then(function (subscription) { $scope.subscription.expired_at = subscription.expired_at; }); - }; - /** * Open a modal dialog allowing the admin to set a subscription for the given user. * @param user {Object} User object, user currently reviewed, as recovered from GET /api/members/:id diff --git a/app/frontend/src/javascript/services/subscription.js b/app/frontend/src/javascript/services/subscription.js index b98eb5f91..938d1c160 100644 --- a/app/frontend/src/javascript/services/subscription.js +++ b/app/frontend/src/javascript/services/subscription.js @@ -3,9 +3,6 @@ Application.Services.factory('Subscription', ['$resource', function ($resource) { return $resource('/api/subscriptions/:id', { id: '@id' }, { - update: { - method: 'PUT' - }, payment_details: { url: '/api/subscriptions/:id/payment_details', method: 'GET' diff --git a/app/frontend/templates/admin/subscriptions/expired_at_modal.html b/app/frontend/templates/admin/subscriptions/expired_at_modal.html deleted file mode 100644 index 38bf7b632..000000000 --- a/app/frontend/templates/admin/subscriptions/expired_at_modal.html +++ /dev/null @@ -1,47 +0,0 @@ - - - diff --git a/app/models/payment_schedule.rb b/app/models/payment_schedule.rb index d6b5f1dbd..749e31d9e 100644 --- a/app/models/payment_schedule.rb +++ b/app/models/payment_schedule.rb @@ -81,10 +81,6 @@ class PaymentSchedule < PaymentDocument PaymentGatewayService.new.create_subscription(self, *args) end - def post_save_extend(gateway_method_id) - PaymentGatewayService.new.extend_subscription(self, gateway_method_id) - end - def render_resource { partial: 'api/payment_schedules/payment_schedule', locals: { payment_schedule: self } } end diff --git a/app/policies/subscription_policy.rb b/app/policies/subscription_policy.rb index 9e937385c..67de23c61 100644 --- a/app/policies/subscription_policy.rb +++ b/app/policies/subscription_policy.rb @@ -2,18 +2,10 @@ # Check the access policies for API::SubscriptionsController class SubscriptionPolicy < ApplicationPolicy - def create? - Setting.get('plans_module') && (user.admin? || (user.manager? && record.user_id != user.id) || record.price.zero?) - end - def show? user.admin? or record.user_id == user.id end - def update? - user.admin? || (user.manager? && record.user.id != user.id) - end - def payment_details? user.admin? || user.manager? end diff --git a/app/services/payment_gateway_service.rb b/app/services/payment_gateway_service.rb index 5c6f855f6..5641db951 100644 --- a/app/services/payment_gateway_service.rb +++ b/app/services/payment_gateway_service.rb @@ -23,10 +23,6 @@ class PaymentGatewayService @gateway.create_subscription(payment_schedule, *args) end - def extend_subscription(payment_schedule, gateway_object_id) - @gateway.extend_subscription(payment_schedule, gateway_object_id) - end - def create_user(user_id) @gateway.create_user(user_id) end diff --git a/app/services/subscriptions/subscribe.rb b/app/services/subscriptions/subscribe.rb deleted file mode 100644 index 1f050ca9c..000000000 --- a/app/services/subscriptions/subscribe.rb +++ /dev/null @@ -1,62 +0,0 @@ -# frozen_string_literal: true - -# Provides helper methods for Subscription actions -class Subscriptions::Subscribe - attr_accessor :user_id, :operator_profile_id - - def initialize(operator_profile_id, user_id = nil) - @user_id = user_id - @operator_profile_id = operator_profile_id - end - - # TODO, delete this - def extend_subscription(subscription, new_expiration_date) - new_sub = Subscription.create( - plan_id: subscription.plan_id, - statistic_profile_id: subscription.statistic_profile_id - ) - new_sub.expiration_date = new_expiration_date - if new_sub.save - schedule = subscription.original_payment_schedule - - operator = InvoicingProfile.find(@operator_profile_id).user - cs = CartService.new(operator) - cart = cs.from_hash(customer_id: subscription.user.id, - items: [ - { - subscription: { - plan_id: subscription.plan_id - } - } - ], - payment_schedule: !schedule.nil?) - details = cart.total - - payment = if schedule - operator = InvoicingProfile.find(operator_profile_id)&.user - - PaymentScheduleService.new.create( - [new_sub], - details[:before_coupon], - operator: operator, - payment_method: schedule.payment_method, - user: new_sub.user, - payment_id: schedule.gateway_payment_mean&.id, - payment_type: schedule.gateway_payment_mean&.class - ) - else - InvoicesService.create( - details, - operator_profile_id, - [new_sub], - new_sub.user - ) - end - payment.save - payment.post_save_extend(schedule&.gateway_payment_mean&.id) - UsersCredits::Manager.new(user: new_sub.user).reset_credits - return new_sub - end - false - end -end diff --git a/app/views/api/subscriptions/update.json.jbuilder b/app/views/api/subscriptions/update.json.jbuilder deleted file mode 100644 index f48db8b44..000000000 --- a/app/views/api/subscriptions/update.json.jbuilder +++ /dev/null @@ -1 +0,0 @@ -json.partial! 'api/subscriptions/subscription', subscription: @subscription diff --git a/config/routes.rb b/config/routes.rb index ac8efe0d1..4fca0d997 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -98,7 +98,7 @@ Rails.application.routes.draw do end resources :groups, only: %i[index create update destroy] - resources :subscriptions, only: %i[show update] do + resources :subscriptions, only: %i[show] do get 'payment_details', action: 'payment_details', on: :member end resources :plan_categories diff --git a/lib/pay_zen/service.rb b/lib/pay_zen/service.rb index b8158892c..b09678bd8 100644 --- a/lib/pay_zen/service.rb +++ b/lib/pay_zen/service.rb @@ -48,10 +48,6 @@ class PayZen::Service < Payment::Service pgo_sub.save! end - def extend_subscription(payment_schedule, payment_method_id) - create_subscription(payment_schedule, payment_method_id) - end - def process_payment_schedule_item(payment_schedule_item) pz_order = payment_schedule_item.payment_schedule.gateway_order.retrieve transaction = pz_order['answer']['transactions'].last diff --git a/lib/stripe/service.rb b/lib/stripe/service.rb index 4e1b00480..a14ef503b 100644 --- a/lib/stripe/service.rb +++ b/lib/stripe/service.rb @@ -7,7 +7,8 @@ module Stripe; end ## create remote objects on stripe class Stripe::Service < Payment::Service - # Create the provided PaymentSchedule on Stripe, using the Subscription API + + # Build the subscription base on the given shopping cart and create it on the remote stripe API def subscribe(payment_method_id, shopping_cart) price_details = shopping_cart.total @@ -27,40 +28,7 @@ class Stripe::Service < Payment::Service # other items (not recurring) items = subscription_invoice_items(payment_schedule, subscription, first_item, reservable_stp_id) - - stripe_key = Setting.get('stripe_secret_key') - if subscription.start_at.nil? - Stripe::Subscription.create({ - customer: shopping_cart.customer.payment_gateway_object.gateway_object_id, - cancel_at: (payment_schedule.payment_schedule_items.max_by(&:due_date).due_date + 1.month).to_i, - add_invoice_items: items, - coupon: payment_schedule.coupon&.code, - items: [ - { price: price[:id] } - ], - default_payment_method: payment_method_id, - expand: %w[latest_invoice.payment_intent] - }, { api_key: stripe_key }) - else - Stripe::SubscriptionSchedule.create({ - customer: shopping_cart.customer.payment_gateway_object.gateway_object_id, - start_date: subscription.start_at.nil? ? 'now' : subscription.start_at.to_i, - end_behavior: 'cancel', - phases: [ - { - items: [ - { price: price[:id] } - ], - add_invoice_items: items, - coupon: payment_schedule.coupon&.code, - default_payment_method: payment_method_id, - end_date: ( - payment_schedule.payment_schedule_items.max_by(&:due_date).due_date + 1.month - ).to_i - } - ] - }, { api_key: stripe_key }) - end + create_remote_subscription(shopping_cart, payment_schedule, items, price, payment_method_id, subscription) end def create_subscription(payment_schedule, stp_object_id, stp_object_type) @@ -75,20 +43,6 @@ class Stripe::Service < Payment::Service pgo.save! end - def extend_subscription(payment_schedule, payment_method_id) - # TODO, use Stripe::Subscription.update(sub_xxx, {cancel_at: new_date}, {api_key: stripe_key}) - - # FIXME, argument cart missing - stp_subscription = subscribe(payment_schedule, payment_method_id) - - # not required? - handle_wallet_transaction(payment_schedule) - - pgo = PaymentGatewayObject.new(item: payment_schedule) - pgo.gateway_object = stp_subscription - pgo.save! - end - def create_user(user_id) StripeWorker.perform_async(:create_stripe_customer, user_id) end @@ -198,6 +152,44 @@ class Stripe::Service < Payment::Service private + + # Create the provided PaymentSchedule on Stripe, using the Subscription API + def create_remote_subscription(shopping_cart, payment_schedule, items, price, payment_method_id, subscription) + stripe_key = Setting.get('stripe_secret_key') + if subscription.start_at.nil? + Stripe::Subscription.create({ + customer: shopping_cart.customer.payment_gateway_object.gateway_object_id, + cancel_at: (payment_schedule.payment_schedule_items.max_by(&:due_date).due_date + 1.month).to_i, + add_invoice_items: items, + coupon: payment_schedule.coupon&.code, + items: [ + { price: price[:id] } + ], + default_payment_method: payment_method_id, + expand: %w[latest_invoice.payment_intent] + }, { api_key: stripe_key }) + else + Stripe::SubscriptionSchedule.create({ + customer: shopping_cart.customer.payment_gateway_object.gateway_object_id, + start_date: subscription.start_at.to_i, + end_behavior: 'cancel', + phases: [ + { + items: [ + { price: price[:id] } + ], + add_invoice_items: items, + coupon: payment_schedule.coupon&.code, + default_payment_method: payment_method_id, + end_date: ( + payment_schedule.payment_schedule_items.max_by(&:due_date).due_date + 1.month + ).to_i + } + ] + }, { api_key: stripe_key }) + end + end + def subscription_invoice_items(payment_schedule, subscription, first_item, reservable_stp_id) second_item = payment_schedule.payment_schedule_items.sort_by(&:due_date)[1]